roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @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     show : function() {
1129         this.el.removeClass('hidden');
1130     },
1131     hide: function() {
1132         if (!this.el.hasClass('hidden')) {
1133             this.el.addClass('hidden');
1134         }
1135         
1136     }
1137    
1138 });
1139
1140  /*
1141  * - LGPL
1142  *
1143  * image
1144  * 
1145  */
1146
1147
1148 /**
1149  * @class Roo.bootstrap.Img
1150  * @extends Roo.bootstrap.Component
1151  * Bootstrap Img class
1152  * @cfg {Boolean} imgResponsive false | true
1153  * @cfg {String} border rounded | circle | thumbnail
1154  * @cfg {String} src image source
1155  * @cfg {String} alt image alternative text
1156  * @cfg {String} href a tag href
1157  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1158  * 
1159  * @constructor
1160  * Create a new Input
1161  * @param {Object} config The config object
1162  */
1163
1164 Roo.bootstrap.Img = function(config){
1165     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1166     
1167     this.addEvents({
1168         // img events
1169         /**
1170          * @event click
1171          * The img click event for the img.
1172          * @param {Roo.EventObject} e
1173          */
1174         "click" : true
1175     });
1176 };
1177
1178 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1179     
1180     imgResponsive: true,
1181     border: '',
1182     src: '',
1183     href: false,
1184     target: false,
1185
1186     getAutoCreate : function(){
1187         
1188         var cfg = {
1189             tag: 'img',
1190             cls: (this.imgResponsive) ? 'img-responsive' : '',
1191             html : null
1192         }
1193         
1194         cfg.html = this.html || cfg.html;
1195         
1196         cfg.src = this.src || cfg.src;
1197         
1198         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1199             cfg.cls += ' img-' + this.border;
1200         }
1201         
1202         if(this.alt){
1203             cfg.alt = this.alt;
1204         }
1205         
1206         if(this.href){
1207             var a = {
1208                 tag: 'a',
1209                 href: this.href,
1210                 cn: [
1211                     cfg
1212                 ]
1213             }
1214             
1215             if(this.target){
1216                 a.target = this.target;
1217             }
1218             
1219         }
1220         
1221         
1222         return (this.href) ? a : cfg;
1223     },
1224     
1225     initEvents: function() {
1226         
1227         if(!this.href){
1228             this.el.on('click', this.onClick, this);
1229         }
1230     },
1231     
1232     onClick : function(e)
1233     {
1234         Roo.log('img onclick');
1235         this.fireEvent('click', this, e);
1236     }
1237    
1238 });
1239
1240  /*
1241  * - LGPL
1242  *
1243  * image
1244  * 
1245  */
1246
1247
1248 /**
1249  * @class Roo.bootstrap.Link
1250  * @extends Roo.bootstrap.Component
1251  * Bootstrap Link Class
1252  * @cfg {String} alt image alternative text
1253  * @cfg {String} href a tag href
1254  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1255  * @cfg {String} html the content of the link.
1256  * @cfg {String} anchor name for the anchor link
1257
1258  * @cfg {Boolean} preventDefault (true | false) default false
1259
1260  * 
1261  * @constructor
1262  * Create a new Input
1263  * @param {Object} config The config object
1264  */
1265
1266 Roo.bootstrap.Link = function(config){
1267     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1268     
1269     this.addEvents({
1270         // img events
1271         /**
1272          * @event click
1273          * The img click event for the img.
1274          * @param {Roo.EventObject} e
1275          */
1276         "click" : true
1277     });
1278 };
1279
1280 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1281     
1282     href: false,
1283     target: false,
1284     preventDefault: false,
1285     anchor : false,
1286     alt : false,
1287
1288     getAutoCreate : function()
1289     {
1290         
1291         var cfg = {
1292             tag: 'a'
1293         };
1294         // anchor's do not require html/href...
1295         if (this.anchor === false) {
1296             cfg.html = this.html || 'html-missing';
1297             cfg.href = this.href || '#';
1298         } else {
1299             cfg.name = this.anchor;
1300             if (this.html !== false) {
1301                 cfg.html = this.html;
1302             }
1303             if (this.href !== false) {
1304                 cfg.href = this.href;
1305             }
1306         }
1307         
1308         if(this.alt !== false){
1309             cfg.alt = this.alt;
1310         }
1311         
1312         
1313         if(this.target !== false) {
1314             cfg.target = this.target;
1315         }
1316         
1317         return cfg;
1318     },
1319     
1320     initEvents: function() {
1321         
1322         if(!this.href || this.preventDefault){
1323             this.el.on('click', this.onClick, this);
1324         }
1325     },
1326     
1327     onClick : function(e)
1328     {
1329         if(this.preventDefault){
1330             e.preventDefault();
1331         }
1332         //Roo.log('img onclick');
1333         this.fireEvent('click', this, e);
1334     }
1335    
1336 });
1337
1338  /*
1339  * - LGPL
1340  *
1341  * header
1342  * 
1343  */
1344
1345 /**
1346  * @class Roo.bootstrap.Header
1347  * @extends Roo.bootstrap.Component
1348  * Bootstrap Header class
1349  * @cfg {String} html content of header
1350  * @cfg {Number} level (1|2|3|4|5|6) default 1
1351  * 
1352  * @constructor
1353  * Create a new Header
1354  * @param {Object} config The config object
1355  */
1356
1357
1358 Roo.bootstrap.Header  = function(config){
1359     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1360 };
1361
1362 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1363     
1364     //href : false,
1365     html : false,
1366     level : 1,
1367     
1368     
1369     
1370     getAutoCreate : function(){
1371         
1372         var cfg = {
1373             tag: 'h' + (1 *this.level),
1374             html: this.html || 'fill in html'
1375         } ;
1376         
1377         return cfg;
1378     }
1379    
1380 });
1381
1382  
1383
1384  /*
1385  * Based on:
1386  * Ext JS Library 1.1.1
1387  * Copyright(c) 2006-2007, Ext JS, LLC.
1388  *
1389  * Originally Released Under LGPL - original licence link has changed is not relivant.
1390  *
1391  * Fork - LGPL
1392  * <script type="text/javascript">
1393  */
1394  
1395 /**
1396  * @class Roo.bootstrap.MenuMgr
1397  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1398  * @singleton
1399  */
1400 Roo.bootstrap.MenuMgr = function(){
1401    var menus, active, groups = {}, attached = false, lastShow = new Date();
1402
1403    // private - called when first menu is created
1404    function init(){
1405        menus = {};
1406        active = new Roo.util.MixedCollection();
1407        Roo.get(document).addKeyListener(27, function(){
1408            if(active.length > 0){
1409                hideAll();
1410            }
1411        });
1412    }
1413
1414    // private
1415    function hideAll(){
1416        if(active && active.length > 0){
1417            var c = active.clone();
1418            c.each(function(m){
1419                m.hide();
1420            });
1421        }
1422    }
1423
1424    // private
1425    function onHide(m){
1426        active.remove(m);
1427        if(active.length < 1){
1428            Roo.get(document).un("mouseup", onMouseDown);
1429             
1430            attached = false;
1431        }
1432    }
1433
1434    // private
1435    function onShow(m){
1436        var last = active.last();
1437        lastShow = new Date();
1438        active.add(m);
1439        if(!attached){
1440           Roo.get(document).on("mouseup", onMouseDown);
1441            
1442            attached = true;
1443        }
1444        if(m.parentMenu){
1445           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1446           m.parentMenu.activeChild = m;
1447        }else if(last && last.isVisible()){
1448           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1449        }
1450    }
1451
1452    // private
1453    function onBeforeHide(m){
1454        if(m.activeChild){
1455            m.activeChild.hide();
1456        }
1457        if(m.autoHideTimer){
1458            clearTimeout(m.autoHideTimer);
1459            delete m.autoHideTimer;
1460        }
1461    }
1462
1463    // private
1464    function onBeforeShow(m){
1465        var pm = m.parentMenu;
1466        if(!pm && !m.allowOtherMenus){
1467            hideAll();
1468        }else if(pm && pm.activeChild && active != m){
1469            pm.activeChild.hide();
1470        }
1471    }
1472
1473    // private
1474    function onMouseDown(e){
1475         Roo.log("on MouseDown");
1476         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1477            hideAll();
1478         }
1479         
1480         
1481    }
1482
1483    // private
1484    function onBeforeCheck(mi, state){
1485        if(state){
1486            var g = groups[mi.group];
1487            for(var i = 0, l = g.length; i < l; i++){
1488                if(g[i] != mi){
1489                    g[i].setChecked(false);
1490                }
1491            }
1492        }
1493    }
1494
1495    return {
1496
1497        /**
1498         * Hides all menus that are currently visible
1499         */
1500        hideAll : function(){
1501             hideAll();  
1502        },
1503
1504        // private
1505        register : function(menu){
1506            if(!menus){
1507                init();
1508            }
1509            menus[menu.id] = menu;
1510            menu.on("beforehide", onBeforeHide);
1511            menu.on("hide", onHide);
1512            menu.on("beforeshow", onBeforeShow);
1513            menu.on("show", onShow);
1514            var g = menu.group;
1515            if(g && menu.events["checkchange"]){
1516                if(!groups[g]){
1517                    groups[g] = [];
1518                }
1519                groups[g].push(menu);
1520                menu.on("checkchange", onCheck);
1521            }
1522        },
1523
1524         /**
1525          * Returns a {@link Roo.menu.Menu} object
1526          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1527          * be used to generate and return a new Menu instance.
1528          */
1529        get : function(menu){
1530            if(typeof menu == "string"){ // menu id
1531                return menus[menu];
1532            }else if(menu.events){  // menu instance
1533                return menu;
1534            }
1535            /*else if(typeof menu.length == 'number'){ // array of menu items?
1536                return new Roo.bootstrap.Menu({items:menu});
1537            }else{ // otherwise, must be a config
1538                return new Roo.bootstrap.Menu(menu);
1539            }
1540            */
1541            return false;
1542        },
1543
1544        // private
1545        unregister : function(menu){
1546            delete menus[menu.id];
1547            menu.un("beforehide", onBeforeHide);
1548            menu.un("hide", onHide);
1549            menu.un("beforeshow", onBeforeShow);
1550            menu.un("show", onShow);
1551            var g = menu.group;
1552            if(g && menu.events["checkchange"]){
1553                groups[g].remove(menu);
1554                menu.un("checkchange", onCheck);
1555            }
1556        },
1557
1558        // private
1559        registerCheckable : function(menuItem){
1560            var g = menuItem.group;
1561            if(g){
1562                if(!groups[g]){
1563                    groups[g] = [];
1564                }
1565                groups[g].push(menuItem);
1566                menuItem.on("beforecheckchange", onBeforeCheck);
1567            }
1568        },
1569
1570        // private
1571        unregisterCheckable : function(menuItem){
1572            var g = menuItem.group;
1573            if(g){
1574                groups[g].remove(menuItem);
1575                menuItem.un("beforecheckchange", onBeforeCheck);
1576            }
1577        }
1578    };
1579 }();/*
1580  * - LGPL
1581  *
1582  * menu
1583  * 
1584  */
1585
1586 /**
1587  * @class Roo.bootstrap.Menu
1588  * @extends Roo.bootstrap.Component
1589  * Bootstrap Menu class - container for MenuItems
1590  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1591  * 
1592  * @constructor
1593  * Create a new Menu
1594  * @param {Object} config The config object
1595  */
1596
1597
1598 Roo.bootstrap.Menu = function(config){
1599     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1600     if (this.registerMenu) {
1601         Roo.bootstrap.MenuMgr.register(this);
1602     }
1603     this.addEvents({
1604         /**
1605          * @event beforeshow
1606          * Fires before this menu is displayed
1607          * @param {Roo.menu.Menu} this
1608          */
1609         beforeshow : true,
1610         /**
1611          * @event beforehide
1612          * Fires before this menu is hidden
1613          * @param {Roo.menu.Menu} this
1614          */
1615         beforehide : true,
1616         /**
1617          * @event show
1618          * Fires after this menu is displayed
1619          * @param {Roo.menu.Menu} this
1620          */
1621         show : true,
1622         /**
1623          * @event hide
1624          * Fires after this menu is hidden
1625          * @param {Roo.menu.Menu} this
1626          */
1627         hide : true,
1628         /**
1629          * @event click
1630          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1631          * @param {Roo.menu.Menu} this
1632          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1633          * @param {Roo.EventObject} e
1634          */
1635         click : true,
1636         /**
1637          * @event mouseover
1638          * Fires when the mouse is hovering over this menu
1639          * @param {Roo.menu.Menu} this
1640          * @param {Roo.EventObject} e
1641          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1642          */
1643         mouseover : true,
1644         /**
1645          * @event mouseout
1646          * Fires when the mouse exits this menu
1647          * @param {Roo.menu.Menu} this
1648          * @param {Roo.EventObject} e
1649          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1650          */
1651         mouseout : true,
1652         /**
1653          * @event itemclick
1654          * Fires when a menu item contained in this menu is clicked
1655          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1656          * @param {Roo.EventObject} e
1657          */
1658         itemclick: true
1659     });
1660     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1661 };
1662
1663 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1664     
1665    /// html : false,
1666     //align : '',
1667     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1668     type: false,
1669     /**
1670      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1671      */
1672     registerMenu : true,
1673     
1674     menuItems :false, // stores the menu items..
1675     
1676     hidden:true,
1677     
1678     parentMenu : false,
1679     
1680     getChildContainer : function() {
1681         return this.el;  
1682     },
1683     
1684     getAutoCreate : function(){
1685          
1686         //if (['right'].indexOf(this.align)!==-1) {
1687         //    cfg.cn[1].cls += ' pull-right'
1688         //}
1689         
1690         
1691         var cfg = {
1692             tag : 'ul',
1693             cls : 'dropdown-menu' ,
1694             style : 'z-index:1000'
1695             
1696         }
1697         
1698         if (this.type === 'submenu') {
1699             cfg.cls = 'submenu active';
1700         }
1701         if (this.type === 'treeview') {
1702             cfg.cls = 'treeview-menu';
1703         }
1704         
1705         return cfg;
1706     },
1707     initEvents : function() {
1708         
1709        // Roo.log("ADD event");
1710        // Roo.log(this.triggerEl.dom);
1711         this.triggerEl.on('click', this.onTriggerPress, this);
1712         this.triggerEl.addClass('dropdown-toggle');
1713         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1714
1715         this.el.on("mouseover", this.onMouseOver, this);
1716         this.el.on("mouseout", this.onMouseOut, this);
1717         
1718         
1719     },
1720     findTargetItem : function(e){
1721         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1722         if(!t){
1723             return false;
1724         }
1725         //Roo.log(t);         Roo.log(t.id);
1726         if(t && t.id){
1727             //Roo.log(this.menuitems);
1728             return this.menuitems.get(t.id);
1729             
1730             //return this.items.get(t.menuItemId);
1731         }
1732         
1733         return false;
1734     },
1735     onClick : function(e){
1736         Roo.log("menu.onClick");
1737         var t = this.findTargetItem(e);
1738         if(!t || t.isContainer){
1739             return;
1740         }
1741         Roo.log(e);
1742         /*
1743         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1744             if(t == this.activeItem && t.shouldDeactivate(e)){
1745                 this.activeItem.deactivate();
1746                 delete this.activeItem;
1747                 return;
1748             }
1749             if(t.canActivate){
1750                 this.setActiveItem(t, true);
1751             }
1752             return;
1753             
1754             
1755         }
1756         */
1757        
1758         Roo.log('pass click event');
1759         
1760         t.onClick(e);
1761         
1762         this.fireEvent("click", this, t, e);
1763         
1764         this.hide();
1765     },
1766      onMouseOver : function(e){
1767         var t  = this.findTargetItem(e);
1768         //Roo.log(t);
1769         //if(t){
1770         //    if(t.canActivate && !t.disabled){
1771         //        this.setActiveItem(t, true);
1772         //    }
1773         //}
1774         
1775         this.fireEvent("mouseover", this, e, t);
1776     },
1777     isVisible : function(){
1778         return !this.hidden;
1779     },
1780      onMouseOut : function(e){
1781         var t  = this.findTargetItem(e);
1782         
1783         //if(t ){
1784         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1785         //        this.activeItem.deactivate();
1786         //        delete this.activeItem;
1787         //    }
1788         //}
1789         this.fireEvent("mouseout", this, e, t);
1790     },
1791     
1792     
1793     /**
1794      * Displays this menu relative to another element
1795      * @param {String/HTMLElement/Roo.Element} element The element to align to
1796      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1797      * the element (defaults to this.defaultAlign)
1798      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1799      */
1800     show : function(el, pos, parentMenu){
1801         this.parentMenu = parentMenu;
1802         if(!this.el){
1803             this.render();
1804         }
1805         this.fireEvent("beforeshow", this);
1806         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1807     },
1808      /**
1809      * Displays this menu at a specific xy position
1810      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1811      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1812      */
1813     showAt : function(xy, parentMenu, /* private: */_e){
1814         this.parentMenu = parentMenu;
1815         if(!this.el){
1816             this.render();
1817         }
1818         if(_e !== false){
1819             this.fireEvent("beforeshow", this);
1820             
1821             //xy = this.el.adjustForConstraints(xy);
1822         }
1823         //this.el.setXY(xy);
1824         //this.el.show();
1825         this.hideMenuItems();
1826         this.hidden = false;
1827         this.triggerEl.addClass('open');
1828         this.focus();
1829         this.fireEvent("show", this);
1830     },
1831     
1832     focus : function(){
1833         return;
1834         if(!this.hidden){
1835             this.doFocus.defer(50, this);
1836         }
1837     },
1838
1839     doFocus : function(){
1840         if(!this.hidden){
1841             this.focusEl.focus();
1842         }
1843     },
1844
1845     /**
1846      * Hides this menu and optionally all parent menus
1847      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1848      */
1849     hide : function(deep){
1850         
1851         this.hideMenuItems();
1852         if(this.el && this.isVisible()){
1853             this.fireEvent("beforehide", this);
1854             if(this.activeItem){
1855                 this.activeItem.deactivate();
1856                 this.activeItem = null;
1857             }
1858             this.triggerEl.removeClass('open');;
1859             this.hidden = true;
1860             this.fireEvent("hide", this);
1861         }
1862         if(deep === true && this.parentMenu){
1863             this.parentMenu.hide(true);
1864         }
1865     },
1866     
1867     onTriggerPress  : function(e)
1868     {
1869         
1870         Roo.log('trigger press');
1871         //Roo.log(e.getTarget());
1872        // Roo.log(this.triggerEl.dom);
1873         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1874             return;
1875         }
1876         if (this.isVisible()) {
1877             Roo.log('hide');
1878             this.hide();
1879         } else {
1880             this.show(this.triggerEl, false, false);
1881         }
1882         
1883         
1884     },
1885     
1886          
1887        
1888     
1889     hideMenuItems : function()
1890     {
1891         //$(backdrop).remove()
1892         Roo.select('.open',true).each(function(aa) {
1893             
1894             aa.removeClass('open');
1895           //var parent = getParent($(this))
1896           //var relatedTarget = { relatedTarget: this }
1897           
1898            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1899           //if (e.isDefaultPrevented()) return
1900            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1901         })
1902     },
1903     addxtypeChild : function (tree, cntr) {
1904         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1905           
1906         this.menuitems.add(comp);
1907         return comp;
1908
1909     },
1910     getEl : function()
1911     {
1912         Roo.log(this.el);
1913         return this.el;
1914     }
1915 });
1916
1917  
1918  /*
1919  * - LGPL
1920  *
1921  * menu item
1922  * 
1923  */
1924
1925
1926 /**
1927  * @class Roo.bootstrap.MenuItem
1928  * @extends Roo.bootstrap.Component
1929  * Bootstrap MenuItem class
1930  * @cfg {String} html the menu label
1931  * @cfg {String} href the link
1932  * @cfg {Boolean} preventDefault (true | false) default true
1933  * @cfg {Boolean} isContainer (true | false) default false
1934  * 
1935  * 
1936  * @constructor
1937  * Create a new MenuItem
1938  * @param {Object} config The config object
1939  */
1940
1941
1942 Roo.bootstrap.MenuItem = function(config){
1943     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1944     this.addEvents({
1945         // raw events
1946         /**
1947          * @event click
1948          * The raw click event for the entire grid.
1949          * @param {Roo.EventObject} e
1950          */
1951         "click" : true
1952     });
1953 };
1954
1955 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1956     
1957     href : false,
1958     html : false,
1959     preventDefault: true,
1960     isContainer : false,
1961     
1962     getAutoCreate : function(){
1963         
1964         if(this.isContainer){
1965             return {
1966                 tag: 'li',
1967                 cls: 'dropdown-menu-item'
1968             };
1969         }
1970         
1971         var cfg= {
1972             tag: 'li',
1973             cls: 'dropdown-menu-item',
1974             cn: [
1975                     {
1976                         tag : 'a',
1977                         href : '#',
1978                         html : 'Link'
1979                     }
1980                 ]
1981         };
1982         if (this.parent().type == 'treeview') {
1983             cfg.cls = 'treeview-menu';
1984         }
1985         
1986         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1987         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1988         return cfg;
1989     },
1990     
1991     initEvents: function() {
1992         
1993         //this.el.select('a').on('click', this.onClick, this);
1994         
1995     },
1996     onClick : function(e)
1997     {
1998         Roo.log('item on click ');
1999         //if(this.preventDefault){
2000         //    e.preventDefault();
2001         //}
2002         //this.parent().hideMenuItems();
2003         
2004         this.fireEvent('click', this, e);
2005     },
2006     getEl : function()
2007     {
2008         return this.el;
2009     }
2010 });
2011
2012  
2013
2014  /*
2015  * - LGPL
2016  *
2017  * menu separator
2018  * 
2019  */
2020
2021
2022 /**
2023  * @class Roo.bootstrap.MenuSeparator
2024  * @extends Roo.bootstrap.Component
2025  * Bootstrap MenuSeparator class
2026  * 
2027  * @constructor
2028  * Create a new MenuItem
2029  * @param {Object} config The config object
2030  */
2031
2032
2033 Roo.bootstrap.MenuSeparator = function(config){
2034     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2035 };
2036
2037 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2038     
2039     getAutoCreate : function(){
2040         var cfg = {
2041             cls: 'divider',
2042             tag : 'li'
2043         };
2044         
2045         return cfg;
2046     }
2047    
2048 });
2049
2050  
2051
2052  
2053 /*
2054 <div class="modal fade">
2055   <div class="modal-dialog">
2056     <div class="modal-content">
2057       <div class="modal-header">
2058         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2059         <h4 class="modal-title">Modal title</h4>
2060       </div>
2061       <div class="modal-body">
2062         <p>One fine body&hellip;</p>
2063       </div>
2064       <div class="modal-footer">
2065         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2066         <button type="button" class="btn btn-primary">Save changes</button>
2067       </div>
2068     </div><!-- /.modal-content -->
2069   </div><!-- /.modal-dialog -->
2070 </div><!-- /.modal -->
2071 */
2072 /*
2073  * - LGPL
2074  *
2075  * page contgainer.
2076  * 
2077  */
2078
2079 /**
2080  * @class Roo.bootstrap.Modal
2081  * @extends Roo.bootstrap.Component
2082  * Bootstrap Modal class
2083  * @cfg {String} title Title of dialog
2084  * @cfg {Boolean} specificTitle default false
2085  * @cfg {Array} buttons Array of buttons or standard button set..
2086  * @cfg {String} buttonPosition (left|right|center) default right
2087  * @cfg {Boolean} animate default true
2088  * @cfg {Boolean} allow_close default true
2089  * 
2090  * @constructor
2091  * Create a new Modal Dialog
2092  * @param {Object} config The config object
2093  */
2094
2095 Roo.bootstrap.Modal = function(config){
2096     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2097     this.addEvents({
2098         // raw events
2099         /**
2100          * @event btnclick
2101          * The raw btnclick event for the button
2102          * @param {Roo.EventObject} e
2103          */
2104         "btnclick" : true
2105     });
2106     this.buttons = this.buttons || [];
2107 };
2108
2109 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2110     
2111     title : 'test dialog',
2112    
2113     buttons : false,
2114     
2115     // set on load...
2116     body:  false,
2117     
2118     specificTitle: false,
2119     
2120     buttonPosition: 'right',
2121     
2122     allow_close : true,
2123     
2124     animate : true,
2125     
2126     onRender : function(ct, position)
2127     {
2128         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2129      
2130         if(!this.el){
2131             var cfg = Roo.apply({},  this.getAutoCreate());
2132             cfg.id = Roo.id();
2133             //if(!cfg.name){
2134             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2135             //}
2136             //if (!cfg.name.length) {
2137             //    delete cfg.name;
2138            // }
2139             if (this.cls) {
2140                 cfg.cls += ' ' + this.cls;
2141             }
2142             if (this.style) {
2143                 cfg.style = this.style;
2144             }
2145             this.el = Roo.get(document.body).createChild(cfg, position);
2146         }
2147         //var type = this.el.dom.type;
2148         
2149         if(this.tabIndex !== undefined){
2150             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2151         }
2152         
2153         
2154         
2155         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2156         this.maskEl.enableDisplayMode("block");
2157         this.maskEl.hide();
2158         //this.el.addClass("x-dlg-modal");
2159     
2160         if (this.buttons.length) {
2161             Roo.each(this.buttons, function(bb) {
2162                 b = Roo.apply({}, bb);
2163                 b.xns = b.xns || Roo.bootstrap;
2164                 b.xtype = b.xtype || 'Button';
2165                 if (typeof(b.listeners) == 'undefined') {
2166                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2167                 }
2168                 
2169                 var btn = Roo.factory(b);
2170                 
2171                 btn.onRender(this.el.select('.modal-footer div').first());
2172                 
2173             },this);
2174         }
2175         // render the children.
2176         var nitems = [];
2177         
2178         if(typeof(this.items) != 'undefined'){
2179             var items = this.items;
2180             delete this.items;
2181
2182             for(var i =0;i < items.length;i++) {
2183                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2184             }
2185         }
2186         
2187         this.items = nitems;
2188         
2189         this.body = this.el.select('.modal-body',true).first();
2190         this.close = this.el.select('.modal-header .close', true).first();
2191         this.footer = this.el.select('.modal-footer',true).first();
2192         this.initEvents();
2193         //this.el.addClass([this.fieldClass, this.cls]);
2194         
2195     },
2196     getAutoCreate : function(){
2197         
2198         
2199         var bdy = {
2200                 cls : 'modal-body',
2201                 html : this.html || ''
2202         };
2203         
2204         var title = {
2205             tag: 'h4',
2206             cls : 'modal-title',
2207             html : this.title
2208         };
2209         
2210         if(this.specificTitle){
2211             title = this.title;
2212             
2213         };
2214         
2215         var header = [];
2216         if (this.allow_close) {
2217             header.push({
2218                 tag: 'button',
2219                 cls : 'close',
2220                 html : '&times'
2221             });
2222         }
2223         header.push(title);
2224         
2225         var modal = {
2226             cls: "modal",
2227             style : 'display: none',
2228             cn : [
2229                 {
2230                     cls: "modal-dialog",
2231                     cn : [
2232                         {
2233                             cls : "modal-content",
2234                             cn : [
2235                                 {
2236                                     cls : 'modal-header',
2237                                     cn : header
2238                                 },
2239                                 bdy,
2240                                 {
2241                                     cls : 'modal-footer',
2242                                     cn : [
2243                                         {
2244                                             tag: 'div',
2245                                             cls: 'btn-' + this.buttonPosition
2246                                         }
2247                                     ]
2248                                     
2249                                 }
2250                                 
2251                                 
2252                             ]
2253                             
2254                         }
2255                     ]
2256                         
2257                 }
2258             ]
2259         };
2260         
2261         if(this.animate){
2262             modal.cls += ' fade';
2263         }
2264         
2265         return modal;
2266           
2267     },
2268     getChildContainer : function() {
2269          
2270          return this.el.select('.modal-body',true).first();
2271         
2272     },
2273     getButtonContainer : function() {
2274          return this.el.select('.modal-footer div',true).first();
2275         
2276     },
2277     initEvents : function()
2278     {
2279         this.el.select('.modal-header .close').on('click', this.hide, this);
2280 //        
2281 //        this.addxtype(this);
2282     },
2283     show : function() {
2284         
2285         if (!this.rendered) {
2286             this.render();
2287         }
2288         
2289         this.el.setStyle('display', 'block');
2290         
2291         if(this.animate){
2292             var _this = this;
2293             (function(){ _this.el.addClass('in'); }).defer(50);
2294         }else{
2295             this.el.addClass('in');
2296         }
2297         
2298         Roo.get(document.body).addClass("x-body-masked");
2299         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2300         this.maskEl.show();
2301         this.el.setStyle('zIndex', '10001');
2302        
2303         this.fireEvent('show', this);
2304         
2305         
2306     },
2307     hide : function()
2308     {
2309         this.maskEl.hide();
2310         Roo.get(document.body).removeClass("x-body-masked");
2311         this.el.removeClass('in');
2312         
2313         if(this.animate){
2314             var _this = this;
2315             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2316         }else{
2317             this.el.setStyle('display', 'none');
2318         }
2319         
2320         this.fireEvent('hide', this);
2321     },
2322     
2323     addButton : function(str, cb)
2324     {
2325          
2326         
2327         var b = Roo.apply({}, { html : str } );
2328         b.xns = b.xns || Roo.bootstrap;
2329         b.xtype = b.xtype || 'Button';
2330         if (typeof(b.listeners) == 'undefined') {
2331             b.listeners = { click : cb.createDelegate(this)  };
2332         }
2333         
2334         var btn = Roo.factory(b);
2335            
2336         btn.onRender(this.el.select('.modal-footer div').first());
2337         
2338         return btn;   
2339        
2340     },
2341     
2342     setDefaultButton : function(btn)
2343     {
2344         //this.el.select('.modal-footer').()
2345     },
2346     resizeTo: function(w,h)
2347     {
2348         // skip..
2349     },
2350     setContentSize  : function(w, h)
2351     {
2352         
2353     },
2354     onButtonClick: function(btn,e)
2355     {
2356         //Roo.log([a,b,c]);
2357         this.fireEvent('btnclick', btn.name, e);
2358     },
2359     setTitle: function(str) {
2360         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2361         
2362     }
2363 });
2364
2365
2366 Roo.apply(Roo.bootstrap.Modal,  {
2367     /**
2368          * Button config that displays a single OK button
2369          * @type Object
2370          */
2371         OK :  [{
2372             name : 'ok',
2373             weight : 'primary',
2374             html : 'OK'
2375         }], 
2376         /**
2377          * Button config that displays Yes and No buttons
2378          * @type Object
2379          */
2380         YESNO : [
2381             {
2382                 name  : 'no',
2383                 html : 'No'
2384             },
2385             {
2386                 name  :'yes',
2387                 weight : 'primary',
2388                 html : 'Yes'
2389             }
2390         ],
2391         
2392         /**
2393          * Button config that displays OK and Cancel buttons
2394          * @type Object
2395          */
2396         OKCANCEL : [
2397             {
2398                name : 'cancel',
2399                 html : 'Cancel'
2400             },
2401             {
2402                 name : 'ok',
2403                 weight : 'primary',
2404                 html : 'OK'
2405             }
2406         ],
2407         /**
2408          * Button config that displays Yes, No and Cancel buttons
2409          * @type Object
2410          */
2411         YESNOCANCEL : [
2412             {
2413                 name : 'yes',
2414                 weight : 'primary',
2415                 html : 'Yes'
2416             },
2417             {
2418                 name : 'no',
2419                 html : 'No'
2420             },
2421             {
2422                 name : 'cancel',
2423                 html : 'Cancel'
2424             }
2425         ]
2426 });
2427  /*
2428  * - LGPL
2429  *
2430  * messagebox - can be used as a replace
2431  * 
2432  */
2433 /**
2434  * @class Roo.MessageBox
2435  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2436  * Example usage:
2437  *<pre><code>
2438 // Basic alert:
2439 Roo.Msg.alert('Status', 'Changes saved successfully.');
2440
2441 // Prompt for user data:
2442 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2443     if (btn == 'ok'){
2444         // process text value...
2445     }
2446 });
2447
2448 // Show a dialog using config options:
2449 Roo.Msg.show({
2450    title:'Save Changes?',
2451    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2452    buttons: Roo.Msg.YESNOCANCEL,
2453    fn: processResult,
2454    animEl: 'elId'
2455 });
2456 </code></pre>
2457  * @singleton
2458  */
2459 Roo.bootstrap.MessageBox = function(){
2460     var dlg, opt, mask, waitTimer;
2461     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2462     var buttons, activeTextEl, bwidth;
2463
2464     
2465     // private
2466     var handleButton = function(button){
2467         dlg.hide();
2468         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2469     };
2470
2471     // private
2472     var handleHide = function(){
2473         if(opt && opt.cls){
2474             dlg.el.removeClass(opt.cls);
2475         }
2476         //if(waitTimer){
2477         //    Roo.TaskMgr.stop(waitTimer);
2478         //    waitTimer = null;
2479         //}
2480     };
2481
2482     // private
2483     var updateButtons = function(b){
2484         var width = 0;
2485         if(!b){
2486             buttons["ok"].hide();
2487             buttons["cancel"].hide();
2488             buttons["yes"].hide();
2489             buttons["no"].hide();
2490             //dlg.footer.dom.style.display = 'none';
2491             return width;
2492         }
2493         dlg.footer.dom.style.display = '';
2494         for(var k in buttons){
2495             if(typeof buttons[k] != "function"){
2496                 if(b[k]){
2497                     buttons[k].show();
2498                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2499                     width += buttons[k].el.getWidth()+15;
2500                 }else{
2501                     buttons[k].hide();
2502                 }
2503             }
2504         }
2505         return width;
2506     };
2507
2508     // private
2509     var handleEsc = function(d, k, e){
2510         if(opt && opt.closable !== false){
2511             dlg.hide();
2512         }
2513         if(e){
2514             e.stopEvent();
2515         }
2516     };
2517
2518     return {
2519         /**
2520          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2521          * @return {Roo.BasicDialog} The BasicDialog element
2522          */
2523         getDialog : function(){
2524            if(!dlg){
2525                 dlg = new Roo.bootstrap.Modal( {
2526                     //draggable: true,
2527                     //resizable:false,
2528                     //constraintoviewport:false,
2529                     //fixedcenter:true,
2530                     //collapsible : false,
2531                     //shim:true,
2532                     //modal: true,
2533                   //  width:400,
2534                   //  height:100,
2535                     //buttonAlign:"center",
2536                     closeClick : function(){
2537                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2538                             handleButton("no");
2539                         }else{
2540                             handleButton("cancel");
2541                         }
2542                     }
2543                 });
2544                 dlg.render();
2545                 dlg.on("hide", handleHide);
2546                 mask = dlg.mask;
2547                 //dlg.addKeyListener(27, handleEsc);
2548                 buttons = {};
2549                 this.buttons = buttons;
2550                 var bt = this.buttonText;
2551                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2552                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2553                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2554                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2555                 Roo.log(buttons)
2556                 bodyEl = dlg.body.createChild({
2557
2558                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2559                         '<textarea class="roo-mb-textarea"></textarea>' +
2560                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2561                 });
2562                 msgEl = bodyEl.dom.firstChild;
2563                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2564                 textboxEl.enableDisplayMode();
2565                 textboxEl.addKeyListener([10,13], function(){
2566                     if(dlg.isVisible() && opt && opt.buttons){
2567                         if(opt.buttons.ok){
2568                             handleButton("ok");
2569                         }else if(opt.buttons.yes){
2570                             handleButton("yes");
2571                         }
2572                     }
2573                 });
2574                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2575                 textareaEl.enableDisplayMode();
2576                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2577                 progressEl.enableDisplayMode();
2578                 var pf = progressEl.dom.firstChild;
2579                 if (pf) {
2580                     pp = Roo.get(pf.firstChild);
2581                     pp.setHeight(pf.offsetHeight);
2582                 }
2583                 
2584             }
2585             return dlg;
2586         },
2587
2588         /**
2589          * Updates the message box body text
2590          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2591          * the XHTML-compliant non-breaking space character '&amp;#160;')
2592          * @return {Roo.MessageBox} This message box
2593          */
2594         updateText : function(text){
2595             if(!dlg.isVisible() && !opt.width){
2596                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2597             }
2598             msgEl.innerHTML = text || '&#160;';
2599       
2600             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2601             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2602             var w = Math.max(
2603                     Math.min(opt.width || cw , this.maxWidth), 
2604                     Math.max(opt.minWidth || this.minWidth, bwidth)
2605             );
2606             if(opt.prompt){
2607                 activeTextEl.setWidth(w);
2608             }
2609             if(dlg.isVisible()){
2610                 dlg.fixedcenter = false;
2611             }
2612             // to big, make it scroll. = But as usual stupid IE does not support
2613             // !important..
2614             
2615             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2616                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2617                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2618             } else {
2619                 bodyEl.dom.style.height = '';
2620                 bodyEl.dom.style.overflowY = '';
2621             }
2622             if (cw > w) {
2623                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2624             } else {
2625                 bodyEl.dom.style.overflowX = '';
2626             }
2627             
2628             dlg.setContentSize(w, bodyEl.getHeight());
2629             if(dlg.isVisible()){
2630                 dlg.fixedcenter = true;
2631             }
2632             return this;
2633         },
2634
2635         /**
2636          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2637          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2638          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2639          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2640          * @return {Roo.MessageBox} This message box
2641          */
2642         updateProgress : function(value, text){
2643             if(text){
2644                 this.updateText(text);
2645             }
2646             if (pp) { // weird bug on my firefox - for some reason this is not defined
2647                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2648             }
2649             return this;
2650         },        
2651
2652         /**
2653          * Returns true if the message box is currently displayed
2654          * @return {Boolean} True if the message box is visible, else false
2655          */
2656         isVisible : function(){
2657             return dlg && dlg.isVisible();  
2658         },
2659
2660         /**
2661          * Hides the message box if it is displayed
2662          */
2663         hide : function(){
2664             if(this.isVisible()){
2665                 dlg.hide();
2666             }  
2667         },
2668
2669         /**
2670          * Displays a new message box, or reinitializes an existing message box, based on the config options
2671          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2672          * The following config object properties are supported:
2673          * <pre>
2674 Property    Type             Description
2675 ----------  ---------------  ------------------------------------------------------------------------------------
2676 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2677                                    closes (defaults to undefined)
2678 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2679                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2680 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2681                                    progress and wait dialogs will ignore this property and always hide the
2682                                    close button as they can only be closed programmatically.
2683 cls               String           A custom CSS class to apply to the message box element
2684 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2685                                    displayed (defaults to 75)
2686 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2687                                    function will be btn (the name of the button that was clicked, if applicable,
2688                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2689                                    Progress and wait dialogs will ignore this option since they do not respond to
2690                                    user actions and can only be closed programmatically, so any required function
2691                                    should be called by the same code after it closes the dialog.
2692 icon              String           A CSS class that provides a background image to be used as an icon for
2693                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2694 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2695 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2696 modal             Boolean          False to allow user interaction with the page while the message box is
2697                                    displayed (defaults to true)
2698 msg               String           A string that will replace the existing message box body text (defaults
2699                                    to the XHTML-compliant non-breaking space character '&#160;')
2700 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2701 progress          Boolean          True to display a progress bar (defaults to false)
2702 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2703 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2704 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2705 title             String           The title text
2706 value             String           The string value to set into the active textbox element if displayed
2707 wait              Boolean          True to display a progress bar (defaults to false)
2708 width             Number           The width of the dialog in pixels
2709 </pre>
2710          *
2711          * Example usage:
2712          * <pre><code>
2713 Roo.Msg.show({
2714    title: 'Address',
2715    msg: 'Please enter your address:',
2716    width: 300,
2717    buttons: Roo.MessageBox.OKCANCEL,
2718    multiline: true,
2719    fn: saveAddress,
2720    animEl: 'addAddressBtn'
2721 });
2722 </code></pre>
2723          * @param {Object} config Configuration options
2724          * @return {Roo.MessageBox} This message box
2725          */
2726         show : function(options)
2727         {
2728             
2729             // this causes nightmares if you show one dialog after another
2730             // especially on callbacks..
2731              
2732             if(this.isVisible()){
2733                 
2734                 this.hide();
2735                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2736                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2737                 Roo.log("New Dialog Message:" +  options.msg )
2738                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2739                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2740                 
2741             }
2742             var d = this.getDialog();
2743             opt = options;
2744             d.setTitle(opt.title || "&#160;");
2745             d.close.setDisplayed(opt.closable !== false);
2746             activeTextEl = textboxEl;
2747             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2748             if(opt.prompt){
2749                 if(opt.multiline){
2750                     textboxEl.hide();
2751                     textareaEl.show();
2752                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2753                         opt.multiline : this.defaultTextHeight);
2754                     activeTextEl = textareaEl;
2755                 }else{
2756                     textboxEl.show();
2757                     textareaEl.hide();
2758                 }
2759             }else{
2760                 textboxEl.hide();
2761                 textareaEl.hide();
2762             }
2763             progressEl.setDisplayed(opt.progress === true);
2764             this.updateProgress(0);
2765             activeTextEl.dom.value = opt.value || "";
2766             if(opt.prompt){
2767                 dlg.setDefaultButton(activeTextEl);
2768             }else{
2769                 var bs = opt.buttons;
2770                 var db = null;
2771                 if(bs && bs.ok){
2772                     db = buttons["ok"];
2773                 }else if(bs && bs.yes){
2774                     db = buttons["yes"];
2775                 }
2776                 dlg.setDefaultButton(db);
2777             }
2778             bwidth = updateButtons(opt.buttons);
2779             this.updateText(opt.msg);
2780             if(opt.cls){
2781                 d.el.addClass(opt.cls);
2782             }
2783             d.proxyDrag = opt.proxyDrag === true;
2784             d.modal = opt.modal !== false;
2785             d.mask = opt.modal !== false ? mask : false;
2786             if(!d.isVisible()){
2787                 // force it to the end of the z-index stack so it gets a cursor in FF
2788                 document.body.appendChild(dlg.el.dom);
2789                 d.animateTarget = null;
2790                 d.show(options.animEl);
2791             }
2792             return this;
2793         },
2794
2795         /**
2796          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2797          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2798          * and closing the message box when the process is complete.
2799          * @param {String} title The title bar text
2800          * @param {String} msg The message box body text
2801          * @return {Roo.MessageBox} This message box
2802          */
2803         progress : function(title, msg){
2804             this.show({
2805                 title : title,
2806                 msg : msg,
2807                 buttons: false,
2808                 progress:true,
2809                 closable:false,
2810                 minWidth: this.minProgressWidth,
2811                 modal : true
2812             });
2813             return this;
2814         },
2815
2816         /**
2817          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2818          * If a callback function is passed it will be called after the user clicks the button, and the
2819          * id of the button that was clicked will be passed as the only parameter to the callback
2820          * (could also be the top-right close button).
2821          * @param {String} title The title bar text
2822          * @param {String} msg The message box body text
2823          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2824          * @param {Object} scope (optional) The scope of the callback function
2825          * @return {Roo.MessageBox} This message box
2826          */
2827         alert : function(title, msg, fn, scope){
2828             this.show({
2829                 title : title,
2830                 msg : msg,
2831                 buttons: this.OK,
2832                 fn: fn,
2833                 scope : scope,
2834                 modal : true
2835             });
2836             return this;
2837         },
2838
2839         /**
2840          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2841          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2842          * You are responsible for closing the message box when the process is complete.
2843          * @param {String} msg The message box body text
2844          * @param {String} title (optional) The title bar text
2845          * @return {Roo.MessageBox} This message box
2846          */
2847         wait : function(msg, title){
2848             this.show({
2849                 title : title,
2850                 msg : msg,
2851                 buttons: false,
2852                 closable:false,
2853                 progress:true,
2854                 modal:true,
2855                 width:300,
2856                 wait:true
2857             });
2858             waitTimer = Roo.TaskMgr.start({
2859                 run: function(i){
2860                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2861                 },
2862                 interval: 1000
2863             });
2864             return this;
2865         },
2866
2867         /**
2868          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2869          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2870          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2871          * @param {String} title The title bar text
2872          * @param {String} msg The message box body text
2873          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2874          * @param {Object} scope (optional) The scope of the callback function
2875          * @return {Roo.MessageBox} This message box
2876          */
2877         confirm : function(title, msg, fn, scope){
2878             this.show({
2879                 title : title,
2880                 msg : msg,
2881                 buttons: this.YESNO,
2882                 fn: fn,
2883                 scope : scope,
2884                 modal : true
2885             });
2886             return this;
2887         },
2888
2889         /**
2890          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2891          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2892          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2893          * (could also be the top-right close button) and the text that was entered will be passed as the two
2894          * parameters to the callback.
2895          * @param {String} title The title bar text
2896          * @param {String} msg The message box body text
2897          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2898          * @param {Object} scope (optional) The scope of the callback function
2899          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2900          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2901          * @return {Roo.MessageBox} This message box
2902          */
2903         prompt : function(title, msg, fn, scope, multiline){
2904             this.show({
2905                 title : title,
2906                 msg : msg,
2907                 buttons: this.OKCANCEL,
2908                 fn: fn,
2909                 minWidth:250,
2910                 scope : scope,
2911                 prompt:true,
2912                 multiline: multiline,
2913                 modal : true
2914             });
2915             return this;
2916         },
2917
2918         /**
2919          * Button config that displays a single OK button
2920          * @type Object
2921          */
2922         OK : {ok:true},
2923         /**
2924          * Button config that displays Yes and No buttons
2925          * @type Object
2926          */
2927         YESNO : {yes:true, no:true},
2928         /**
2929          * Button config that displays OK and Cancel buttons
2930          * @type Object
2931          */
2932         OKCANCEL : {ok:true, cancel:true},
2933         /**
2934          * Button config that displays Yes, No and Cancel buttons
2935          * @type Object
2936          */
2937         YESNOCANCEL : {yes:true, no:true, cancel:true},
2938
2939         /**
2940          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2941          * @type Number
2942          */
2943         defaultTextHeight : 75,
2944         /**
2945          * The maximum width in pixels of the message box (defaults to 600)
2946          * @type Number
2947          */
2948         maxWidth : 600,
2949         /**
2950          * The minimum width in pixels of the message box (defaults to 100)
2951          * @type Number
2952          */
2953         minWidth : 100,
2954         /**
2955          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2956          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2957          * @type Number
2958          */
2959         minProgressWidth : 250,
2960         /**
2961          * An object containing the default button text strings that can be overriden for localized language support.
2962          * Supported properties are: ok, cancel, yes and no.
2963          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2964          * @type Object
2965          */
2966         buttonText : {
2967             ok : "OK",
2968             cancel : "Cancel",
2969             yes : "Yes",
2970             no : "No"
2971         }
2972     };
2973 }();
2974
2975 /**
2976  * Shorthand for {@link Roo.MessageBox}
2977  */
2978 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2979 Roo.Msg = Roo.Msg || Roo.MessageBox;
2980 /*
2981  * - LGPL
2982  *
2983  * navbar
2984  * 
2985  */
2986
2987 /**
2988  * @class Roo.bootstrap.Navbar
2989  * @extends Roo.bootstrap.Component
2990  * Bootstrap Navbar class
2991
2992  * @constructor
2993  * Create a new Navbar
2994  * @param {Object} config The config object
2995  */
2996
2997
2998 Roo.bootstrap.Navbar = function(config){
2999     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3000     
3001 };
3002
3003 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3004     
3005     
3006    
3007     // private
3008     navItems : false,
3009     loadMask : false,
3010     
3011     
3012     getAutoCreate : function(){
3013         
3014         
3015         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3016         
3017     },
3018     
3019     initEvents :function ()
3020     {
3021         //Roo.log(this.el.select('.navbar-toggle',true));
3022         this.el.select('.navbar-toggle',true).on('click', function() {
3023            // Roo.log('click');
3024             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3025         }, this);
3026         
3027         var mark = {
3028             tag: "div",
3029             cls:"x-dlg-mask"
3030         }
3031         
3032         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3033         
3034         var size = this.el.getSize();
3035         this.maskEl.setSize(size.width, size.height);
3036         this.maskEl.enableDisplayMode("block");
3037         this.maskEl.hide();
3038         
3039         if(this.loadMask){
3040             this.maskEl.show();
3041         }
3042     },
3043     
3044     
3045     getChildContainer : function()
3046     {
3047         if (this.el.select('.collapse').getCount()) {
3048             return this.el.select('.collapse',true).first();
3049         }
3050         
3051         return this.el;
3052     },
3053     
3054     mask : function()
3055     {
3056         this.maskEl.show();
3057     },
3058     
3059     unmask : function()
3060     {
3061         this.maskEl.hide();
3062     } 
3063     
3064     
3065     
3066     
3067 });
3068
3069
3070
3071  
3072
3073  /*
3074  * - LGPL
3075  *
3076  * navbar
3077  * 
3078  */
3079
3080 /**
3081  * @class Roo.bootstrap.NavSimplebar
3082  * @extends Roo.bootstrap.Navbar
3083  * Bootstrap Sidebar class
3084  *
3085  * @cfg {Boolean} inverse is inverted color
3086  * 
3087  * @cfg {String} type (nav | pills | tabs)
3088  * @cfg {Boolean} arrangement stacked | justified
3089  * @cfg {String} align (left | right) alignment
3090  * 
3091  * @cfg {Boolean} main (true|false) main nav bar? default false
3092  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3093  * 
3094  * @cfg {String} tag (header|footer|nav|div) default is nav 
3095
3096  * 
3097  * 
3098  * 
3099  * @constructor
3100  * Create a new Sidebar
3101  * @param {Object} config The config object
3102  */
3103
3104
3105 Roo.bootstrap.NavSimplebar = function(config){
3106     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3107 };
3108
3109 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3110     
3111     inverse: false,
3112     
3113     type: false,
3114     arrangement: '',
3115     align : false,
3116     
3117     
3118     
3119     main : false,
3120     
3121     
3122     tag : false,
3123     
3124     
3125     getAutoCreate : function(){
3126         
3127         
3128         var cfg = {
3129             tag : this.tag || 'div',
3130             cls : 'navbar'
3131         };
3132           
3133         
3134         cfg.cn = [
3135             {
3136                 cls: 'nav',
3137                 tag : 'ul'
3138             }
3139         ];
3140         
3141          
3142         this.type = this.type || 'nav';
3143         if (['tabs','pills'].indexOf(this.type)!==-1) {
3144             cfg.cn[0].cls += ' nav-' + this.type
3145         
3146         
3147         } else {
3148             if (this.type!=='nav') {
3149                 Roo.log('nav type must be nav/tabs/pills')
3150             }
3151             cfg.cn[0].cls += ' navbar-nav'
3152         }
3153         
3154         
3155         
3156         
3157         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3158             cfg.cn[0].cls += ' nav-' + this.arrangement;
3159         }
3160         
3161         
3162         if (this.align === 'right') {
3163             cfg.cn[0].cls += ' navbar-right';
3164         }
3165         
3166         if (this.inverse) {
3167             cfg.cls += ' navbar-inverse';
3168             
3169         }
3170         
3171         
3172         return cfg;
3173     
3174         
3175     }
3176     
3177     
3178     
3179 });
3180
3181
3182
3183  
3184
3185  
3186        /*
3187  * - LGPL
3188  *
3189  * navbar
3190  * 
3191  */
3192
3193 /**
3194  * @class Roo.bootstrap.NavHeaderbar
3195  * @extends Roo.bootstrap.NavSimplebar
3196  * Bootstrap Sidebar class
3197  *
3198  * @cfg {String} brand what is brand
3199  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3200  * @cfg {String} brand_href href of the brand
3201  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3202  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3203  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3204  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3205  * 
3206  * @constructor
3207  * Create a new Sidebar
3208  * @param {Object} config The config object
3209  */
3210
3211
3212 Roo.bootstrap.NavHeaderbar = function(config){
3213     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3214       
3215 };
3216
3217 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3218     
3219     position: '',
3220     brand: '',
3221     brand_href: false,
3222     srButton : true,
3223     autohide : false,
3224     desktopCenter : false,
3225    
3226     
3227     getAutoCreate : function(){
3228         
3229         var   cfg = {
3230             tag: this.nav || 'nav',
3231             cls: 'navbar',
3232             role: 'navigation',
3233             cn: []
3234         };
3235         
3236         var cn = cfg.cn;
3237         if (this.desktopCenter) {
3238             cn.push({cls : 'container', cn : []});
3239             cn = cn[0].cn;
3240         }
3241         
3242         if(this.srButton){
3243             cn.push({
3244                 tag: 'div',
3245                 cls: 'navbar-header',
3246                 cn: [
3247                     {
3248                         tag: 'button',
3249                         type: 'button',
3250                         cls: 'navbar-toggle',
3251                         'data-toggle': 'collapse',
3252                         cn: [
3253                             {
3254                                 tag: 'span',
3255                                 cls: 'sr-only',
3256                                 html: 'Toggle navigation'
3257                             },
3258                             {
3259                                 tag: 'span',
3260                                 cls: 'icon-bar'
3261                             },
3262                             {
3263                                 tag: 'span',
3264                                 cls: 'icon-bar'
3265                             },
3266                             {
3267                                 tag: 'span',
3268                                 cls: 'icon-bar'
3269                             }
3270                         ]
3271                     }
3272                 ]
3273             });
3274         }
3275         
3276         cn.push({
3277             tag: 'div',
3278             cls: 'collapse navbar-collapse',
3279             cn : []
3280         });
3281         
3282         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3283         
3284         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3285             cfg.cls += ' navbar-' + this.position;
3286             
3287             // tag can override this..
3288             
3289             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3290         }
3291         
3292         if (this.brand !== '') {
3293             cn[0].cn.push({
3294                 tag: 'a',
3295                 href: this.brand_href ? this.brand_href : '#',
3296                 cls: 'navbar-brand',
3297                 cn: [
3298                 this.brand
3299                 ]
3300             });
3301         }
3302         
3303         if(this.main){
3304             cfg.cls += ' main-nav';
3305         }
3306         
3307         
3308         return cfg;
3309
3310         
3311     },
3312     getHeaderChildContainer : function()
3313     {
3314         if (this.el.select('.navbar-header').getCount()) {
3315             return this.el.select('.navbar-header',true).first();
3316         }
3317         
3318         return this.getChildContainer();
3319     },
3320     
3321     
3322     initEvents : function()
3323     {
3324         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3325         
3326         if (this.autohide) {
3327             
3328             var prevScroll = 0;
3329             var ft = this.el;
3330             
3331             Roo.get(document).on('scroll',function(e) {
3332                 var ns = Roo.get(document).getScroll().top;
3333                 var os = prevScroll;
3334                 prevScroll = ns;
3335                 
3336                 if(ns > os){
3337                     ft.removeClass('slideDown');
3338                     ft.addClass('slideUp');
3339                     return;
3340                 }
3341                 ft.removeClass('slideUp');
3342                 ft.addClass('slideDown');
3343                  
3344               
3345           },this);
3346         }
3347     }    
3348           
3349       
3350     
3351     
3352 });
3353
3354
3355
3356  
3357
3358  /*
3359  * - LGPL
3360  *
3361  * navbar
3362  * 
3363  */
3364
3365 /**
3366  * @class Roo.bootstrap.NavSidebar
3367  * @extends Roo.bootstrap.Navbar
3368  * Bootstrap Sidebar class
3369  * 
3370  * @constructor
3371  * Create a new Sidebar
3372  * @param {Object} config The config object
3373  */
3374
3375
3376 Roo.bootstrap.NavSidebar = function(config){
3377     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3378 };
3379
3380 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3381     
3382     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3383     
3384     getAutoCreate : function(){
3385         
3386         
3387         return  {
3388             tag: 'div',
3389             cls: 'sidebar sidebar-nav'
3390         };
3391     
3392         
3393     }
3394     
3395     
3396     
3397 });
3398
3399
3400
3401  
3402
3403  /*
3404  * - LGPL
3405  *
3406  * nav group
3407  * 
3408  */
3409
3410 /**
3411  * @class Roo.bootstrap.NavGroup
3412  * @extends Roo.bootstrap.Component
3413  * Bootstrap NavGroup class
3414  * @cfg {String} align left | right
3415  * @cfg {Boolean} inverse false | true
3416  * @cfg {String} type (nav|pills|tab) default nav
3417  * @cfg {String} navId - reference Id for navbar.
3418
3419  * 
3420  * @constructor
3421  * Create a new nav group
3422  * @param {Object} config The config object
3423  */
3424
3425 Roo.bootstrap.NavGroup = function(config){
3426     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3427     this.navItems = [];
3428    
3429     Roo.bootstrap.NavGroup.register(this);
3430      this.addEvents({
3431         /**
3432              * @event changed
3433              * Fires when the active item changes
3434              * @param {Roo.bootstrap.NavGroup} this
3435              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3436              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3437          */
3438         'changed': true
3439      });
3440     
3441 };
3442
3443 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3444     
3445     align: '',
3446     inverse: false,
3447     form: false,
3448     type: 'nav',
3449     navId : '',
3450     // private
3451     
3452     navItems : false, 
3453     
3454     getAutoCreate : function()
3455     {
3456         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3457         
3458         cfg = {
3459             tag : 'ul',
3460             cls: 'nav' 
3461         }
3462         
3463         if (['tabs','pills'].indexOf(this.type)!==-1) {
3464             cfg.cls += ' nav-' + this.type
3465         } else {
3466             if (this.type!=='nav') {
3467                 Roo.log('nav type must be nav/tabs/pills')
3468             }
3469             cfg.cls += ' navbar-nav'
3470         }
3471         
3472         if (this.parent().sidebar) {
3473             cfg = {
3474                 tag: 'ul',
3475                 cls: 'dashboard-menu sidebar-menu'
3476             }
3477             
3478             return cfg;
3479         }
3480         
3481         if (this.form === true) {
3482             cfg = {
3483                 tag: 'form',
3484                 cls: 'navbar-form'
3485             }
3486             
3487             if (this.align === 'right') {
3488                 cfg.cls += ' navbar-right';
3489             } else {
3490                 cfg.cls += ' navbar-left';
3491             }
3492         }
3493         
3494         if (this.align === 'right') {
3495             cfg.cls += ' navbar-right';
3496         }
3497         
3498         if (this.inverse) {
3499             cfg.cls += ' navbar-inverse';
3500             
3501         }
3502         
3503         
3504         return cfg;
3505     },
3506     /**
3507     * sets the active Navigation item
3508     * @param {Roo.bootstrap.NavItem} the new current navitem
3509     */
3510     setActiveItem : function(item)
3511     {
3512         var prev = false;
3513         Roo.each(this.navItems, function(v){
3514             if (v == item) {
3515                 return ;
3516             }
3517             if (v.isActive()) {
3518                 v.setActive(false, true);
3519                 prev = v;
3520                 
3521             }
3522             
3523         });
3524
3525         item.setActive(true, true);
3526         this.fireEvent('changed', this, item, prev);
3527         
3528         
3529     },
3530     /**
3531     * gets the active Navigation item
3532     * @return {Roo.bootstrap.NavItem} the current navitem
3533     */
3534     getActive : function()
3535     {
3536         
3537         var prev = false;
3538         Roo.each(this.navItems, function(v){
3539             
3540             if (v.isActive()) {
3541                 prev = v;
3542                 
3543             }
3544             
3545         });
3546         return prev;
3547     },
3548     
3549     indexOfNav : function()
3550     {
3551         
3552         var prev = false;
3553         Roo.each(this.navItems, function(v,i){
3554             
3555             if (v.isActive()) {
3556                 prev = i;
3557                 
3558             }
3559             
3560         });
3561         return prev;
3562     },
3563     /**
3564     * adds a Navigation item
3565     * @param {Roo.bootstrap.NavItem} the navitem to add
3566     */
3567     addItem : function(cfg)
3568     {
3569         var cn = new Roo.bootstrap.NavItem(cfg);
3570         this.register(cn);
3571         cn.parentId = this.id;
3572         cn.onRender(this.el, null);
3573         return cn;
3574     },
3575     /**
3576     * register a Navigation item
3577     * @param {Roo.bootstrap.NavItem} the navitem to add
3578     */
3579     register : function(item)
3580     {
3581         this.navItems.push( item);
3582         item.navId = this.navId;
3583     
3584     },
3585     
3586     /**
3587     * clear all the Navigation item
3588     */
3589    
3590     clearAll : function()
3591     {
3592         this.navItems = [];
3593         this.el.dom.innerHTML = '';
3594     },
3595     
3596     getNavItem: function(tabId)
3597     {
3598         var ret = false;
3599         Roo.each(this.navItems, function(e) {
3600             if (e.tabId == tabId) {
3601                ret =  e;
3602                return false;
3603             }
3604             return true;
3605             
3606         });
3607         return ret;
3608     },
3609     
3610     setActiveNext : function()
3611     {
3612         var i = this.indexOfNav(this.getActive());
3613         if (i > this.navItems.length) {
3614             return;
3615         }
3616         this.setActiveItem(this.navItems[i+1]);
3617     },
3618     setActivePrev : function()
3619     {
3620         var i = this.indexOfNav(this.getActive());
3621         if (i  < 1) {
3622             return;
3623         }
3624         this.setActiveItem(this.navItems[i-1]);
3625     },
3626     clearWasActive : function(except) {
3627         Roo.each(this.navItems, function(e) {
3628             if (e.tabId != except.tabId && e.was_active) {
3629                e.was_active = false;
3630                return false;
3631             }
3632             return true;
3633             
3634         });
3635     },
3636     getWasActive : function ()
3637     {
3638         var r = false;
3639         Roo.each(this.navItems, function(e) {
3640             if (e.was_active) {
3641                r = e;
3642                return false;
3643             }
3644             return true;
3645             
3646         });
3647         return r;
3648     }
3649     
3650     
3651 });
3652
3653  
3654 Roo.apply(Roo.bootstrap.NavGroup, {
3655     
3656     groups: {},
3657      /**
3658     * register a Navigation Group
3659     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3660     */
3661     register : function(navgrp)
3662     {
3663         this.groups[navgrp.navId] = navgrp;
3664         
3665     },
3666     /**
3667     * fetch a Navigation Group based on the navigation ID
3668     * @param {string} the navgroup to add
3669     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3670     */
3671     get: function(navId) {
3672         if (typeof(this.groups[navId]) == 'undefined') {
3673             return false;
3674             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3675         }
3676         return this.groups[navId] ;
3677     }
3678     
3679     
3680     
3681 });
3682
3683  /*
3684  * - LGPL
3685  *
3686  * row
3687  * 
3688  */
3689
3690 /**
3691  * @class Roo.bootstrap.NavItem
3692  * @extends Roo.bootstrap.Component
3693  * Bootstrap Navbar.NavItem class
3694  * @cfg {String} href  link to
3695  * @cfg {String} html content of button
3696  * @cfg {String} badge text inside badge
3697  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3698  * @cfg {String} glyphicon name of glyphicon
3699  * @cfg {String} icon name of font awesome icon
3700  * @cfg {Boolean} active Is item active
3701  * @cfg {Boolean} disabled Is item disabled
3702  
3703  * @cfg {Boolean} preventDefault (true | false) default false
3704  * @cfg {String} tabId the tab that this item activates.
3705  * @cfg {String} tagtype (a|span) render as a href or span?
3706   
3707  * @constructor
3708  * Create a new Navbar Item
3709  * @param {Object} config The config object
3710  */
3711 Roo.bootstrap.NavItem = function(config){
3712     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3713     this.addEvents({
3714         // raw events
3715         /**
3716          * @event click
3717          * The raw click event for the entire grid.
3718          * @param {Roo.EventObject} e
3719          */
3720         "click" : true,
3721          /**
3722             * @event changed
3723             * Fires when the active item active state changes
3724             * @param {Roo.bootstrap.NavItem} this
3725             * @param {boolean} state the new state
3726              
3727          */
3728         'changed': true
3729     });
3730    
3731 };
3732
3733 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3734     
3735     href: false,
3736     html: '',
3737     badge: '',
3738     icon: false,
3739     glyphicon: false,
3740     active: false,
3741     preventDefault : false,
3742     tabId : false,
3743     tagtype : 'a',
3744     disabled : false,
3745     
3746     was_active : false,
3747     
3748     getAutoCreate : function(){
3749          
3750         var cfg = {
3751             tag: 'li',
3752             cls: 'nav-item'
3753             
3754         }
3755         if (this.active) {
3756             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3757         }
3758         if (this.disabled) {
3759             cfg.cls += ' disabled';
3760         }
3761         
3762         if (this.href || this.html || this.glyphicon || this.icon) {
3763             cfg.cn = [
3764                 {
3765                     tag: this.tagtype,
3766                     href : this.href || "#",
3767                     html: this.html || ''
3768                 }
3769             ];
3770             
3771             if (this.icon) {
3772                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3773             }
3774
3775             if(this.glyphicon) {
3776                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3777             }
3778             
3779             if (this.menu) {
3780                 
3781                 cfg.cn[0].html += " <span class='caret'></span>";
3782              
3783             }
3784             
3785             if (this.badge !== '') {
3786                  
3787                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3788             }
3789         }
3790         
3791         
3792         
3793         return cfg;
3794     },
3795     initEvents: function() 
3796     {
3797         if (typeof (this.menu) != 'undefined') {
3798             this.menu.parentType = this.xtype;
3799             this.menu.triggerEl = this.el;
3800             this.menu = this.addxtype(Roo.apply({}, this.menu));
3801         }
3802         
3803         this.el.select('a',true).on('click', this.onClick, this);
3804         
3805         if(this.tagtype == 'span'){
3806             this.el.select('span',true).on('click', this.onClick, this);
3807         }
3808        
3809         // at this point parent should be available..
3810         this.parent().register(this);
3811     },
3812     
3813     onClick : function(e)
3814     {
3815         if(this.preventDefault || this.href == '#'){
3816             e.preventDefault();
3817         }
3818         
3819         if (this.disabled) {
3820             return;
3821         }
3822         
3823         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3824         if (tg && tg.transition) {
3825             Roo.log("waiting for the transitionend");
3826             return;
3827         }
3828         
3829         Roo.log("fire event clicked");
3830         if(this.fireEvent('click', this, e) === false){
3831             return;
3832         };
3833         
3834         if(this.tagtype == 'span'){
3835             return;
3836         }
3837         
3838         var p = this.parent();
3839         if (['tabs','pills'].indexOf(p.type)!==-1) {
3840             if (typeof(p.setActiveItem) !== 'undefined') {
3841                 p.setActiveItem(this);
3842             }
3843         }
3844         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3845         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3846             // remove the collapsed menu expand...
3847             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3848         }
3849         
3850     },
3851     
3852     isActive: function () {
3853         return this.active
3854     },
3855     setActive : function(state, fire, is_was_active)
3856     {
3857         if (this.active && !state & this.navId) {
3858             this.was_active = true;
3859             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3860             if (nv) {
3861                 nv.clearWasActive(this);
3862             }
3863             
3864         }
3865         this.active = state;
3866         
3867         if (!state ) {
3868             this.el.removeClass('active');
3869         } else if (!this.el.hasClass('active')) {
3870             this.el.addClass('active');
3871         }
3872         if (fire) {
3873             this.fireEvent('changed', this, state);
3874         }
3875         
3876         // show a panel if it's registered and related..
3877         
3878         if (!this.navId || !this.tabId || !state || is_was_active) {
3879             return;
3880         }
3881         
3882         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3883         if (!tg) {
3884             return;
3885         }
3886         var pan = tg.getPanelByName(this.tabId);
3887         if (!pan) {
3888             return;
3889         }
3890         // if we can not flip to new panel - go back to old nav highlight..
3891         if (false == tg.showPanel(pan)) {
3892             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3893             if (nv) {
3894                 var onav = nv.getWasActive();
3895                 if (onav) {
3896                     onav.setActive(true, false, true);
3897                 }
3898             }
3899             
3900         }
3901         
3902         
3903         
3904     },
3905      // this should not be here...
3906     setDisabled : function(state)
3907     {
3908         this.disabled = state;
3909         if (!state ) {
3910             this.el.removeClass('disabled');
3911         } else if (!this.el.hasClass('disabled')) {
3912             this.el.addClass('disabled');
3913         }
3914         
3915     },
3916     
3917     /**
3918      * Fetch the element to display the tooltip on.
3919      * @return {Roo.Element} defaults to this.el
3920      */
3921     tooltipEl : function()
3922     {
3923         return this.el.select('' + this.tagtype + '', true).first();
3924     }
3925 });
3926  
3927
3928  /*
3929  * - LGPL
3930  *
3931  * sidebar item
3932  *
3933  *  li
3934  *    <span> icon </span>
3935  *    <span> text </span>
3936  *    <span>badge </span>
3937  */
3938
3939 /**
3940  * @class Roo.bootstrap.NavSidebarItem
3941  * @extends Roo.bootstrap.NavItem
3942  * Bootstrap Navbar.NavSidebarItem class
3943  * @constructor
3944  * Create a new Navbar Button
3945  * @param {Object} config The config object
3946  */
3947 Roo.bootstrap.NavSidebarItem = function(config){
3948     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3949     this.addEvents({
3950         // raw events
3951         /**
3952          * @event click
3953          * The raw click event for the entire grid.
3954          * @param {Roo.EventObject} e
3955          */
3956         "click" : true,
3957          /**
3958             * @event changed
3959             * Fires when the active item active state changes
3960             * @param {Roo.bootstrap.NavSidebarItem} this
3961             * @param {boolean} state the new state
3962              
3963          */
3964         'changed': true
3965     });
3966    
3967 };
3968
3969 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3970     
3971     
3972     getAutoCreate : function(){
3973         
3974         
3975         var a = {
3976                 tag: 'a',
3977                 href : this.href || '#',
3978                 cls: '',
3979                 html : '',
3980                 cn : []
3981         };
3982         var cfg = {
3983             tag: 'li',
3984             cls: '',
3985             cn: [ a ]
3986         }
3987         var span = {
3988             tag: 'span',
3989             html : this.html || ''
3990         }
3991         
3992         
3993         if (this.active) {
3994             cfg.cls += ' active';
3995         }
3996         
3997         // left icon..
3998         if (this.glyphicon || this.icon) {
3999             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4000             a.cn.push({ tag : 'i', cls : c }) ;
4001         }
4002         // html..
4003         a.cn.push(span);
4004         // then badge..
4005         if (this.badge !== '') {
4006             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4007         }
4008         // fi
4009         if (this.menu) {
4010             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4011             a.cls += 'dropdown-toggle treeview' ;
4012             
4013         }
4014         
4015         
4016         
4017         return cfg;
4018          
4019            
4020     }
4021    
4022      
4023  
4024 });
4025  
4026
4027  /*
4028  * - LGPL
4029  *
4030  * row
4031  * 
4032  */
4033
4034 /**
4035  * @class Roo.bootstrap.Row
4036  * @extends Roo.bootstrap.Component
4037  * Bootstrap Row class (contains columns...)
4038  * 
4039  * @constructor
4040  * Create a new Row
4041  * @param {Object} config The config object
4042  */
4043
4044 Roo.bootstrap.Row = function(config){
4045     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4046 };
4047
4048 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4049     
4050     getAutoCreate : function(){
4051        return {
4052             cls: 'row clearfix'
4053        };
4054     }
4055     
4056     
4057 });
4058
4059  
4060
4061  /*
4062  * - LGPL
4063  *
4064  * element
4065  * 
4066  */
4067
4068 /**
4069  * @class Roo.bootstrap.Element
4070  * @extends Roo.bootstrap.Component
4071  * Bootstrap Element class
4072  * @cfg {String} html contents of the element
4073  * @cfg {String} tag tag of the element
4074  * @cfg {String} cls class of the element
4075  * 
4076  * @constructor
4077  * Create a new Element
4078  * @param {Object} config The config object
4079  */
4080
4081 Roo.bootstrap.Element = function(config){
4082     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4083 };
4084
4085 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4086     
4087     tag: 'div',
4088     cls: '',
4089     html: '',
4090      
4091     
4092     getAutoCreate : function(){
4093         
4094         var cfg = {
4095             tag: this.tag,
4096             cls: this.cls,
4097             html: this.html
4098         }
4099         
4100         
4101         
4102         return cfg;
4103     }
4104    
4105 });
4106
4107  
4108
4109  /*
4110  * - LGPL
4111  *
4112  * pagination
4113  * 
4114  */
4115
4116 /**
4117  * @class Roo.bootstrap.Pagination
4118  * @extends Roo.bootstrap.Component
4119  * Bootstrap Pagination class
4120  * @cfg {String} size xs | sm | md | lg
4121  * @cfg {Boolean} inverse false | true
4122  * 
4123  * @constructor
4124  * Create a new Pagination
4125  * @param {Object} config The config object
4126  */
4127
4128 Roo.bootstrap.Pagination = function(config){
4129     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4130 };
4131
4132 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4133     
4134     cls: false,
4135     size: false,
4136     inverse: false,
4137     
4138     getAutoCreate : function(){
4139         var cfg = {
4140             tag: 'ul',
4141                 cls: 'pagination'
4142         };
4143         if (this.inverse) {
4144             cfg.cls += ' inverse';
4145         }
4146         if (this.html) {
4147             cfg.html=this.html;
4148         }
4149         if (this.cls) {
4150             cfg.cls += " " + this.cls;
4151         }
4152         return cfg;
4153     }
4154    
4155 });
4156
4157  
4158
4159  /*
4160  * - LGPL
4161  *
4162  * Pagination item
4163  * 
4164  */
4165
4166
4167 /**
4168  * @class Roo.bootstrap.PaginationItem
4169  * @extends Roo.bootstrap.Component
4170  * Bootstrap PaginationItem class
4171  * @cfg {String} html text
4172  * @cfg {String} href the link
4173  * @cfg {Boolean} preventDefault (true | false) default true
4174  * @cfg {Boolean} active (true | false) default false
4175  * @cfg {Boolean} disabled default false
4176  * 
4177  * 
4178  * @constructor
4179  * Create a new PaginationItem
4180  * @param {Object} config The config object
4181  */
4182
4183
4184 Roo.bootstrap.PaginationItem = function(config){
4185     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4186     this.addEvents({
4187         // raw events
4188         /**
4189          * @event click
4190          * The raw click event for the entire grid.
4191          * @param {Roo.EventObject} e
4192          */
4193         "click" : true
4194     });
4195 };
4196
4197 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4198     
4199     href : false,
4200     html : false,
4201     preventDefault: true,
4202     active : false,
4203     cls : false,
4204     disabled: false,
4205     
4206     getAutoCreate : function(){
4207         var cfg= {
4208             tag: 'li',
4209             cn: [
4210                 {
4211                     tag : 'a',
4212                     href : this.href ? this.href : '#',
4213                     html : this.html ? this.html : ''
4214                 }
4215             ]
4216         };
4217         
4218         if(this.cls){
4219             cfg.cls = this.cls;
4220         }
4221         
4222         if(this.disabled){
4223             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4224         }
4225         
4226         if(this.active){
4227             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4228         }
4229         
4230         return cfg;
4231     },
4232     
4233     initEvents: function() {
4234         
4235         this.el.on('click', this.onClick, this);
4236         
4237     },
4238     onClick : function(e)
4239     {
4240         Roo.log('PaginationItem on click ');
4241         if(this.preventDefault){
4242             e.preventDefault();
4243         }
4244         
4245         if(this.disabled){
4246             return;
4247         }
4248         
4249         this.fireEvent('click', this, e);
4250     }
4251    
4252 });
4253
4254  
4255
4256  /*
4257  * - LGPL
4258  *
4259  * slider
4260  * 
4261  */
4262
4263
4264 /**
4265  * @class Roo.bootstrap.Slider
4266  * @extends Roo.bootstrap.Component
4267  * Bootstrap Slider class
4268  *    
4269  * @constructor
4270  * Create a new Slider
4271  * @param {Object} config The config object
4272  */
4273
4274 Roo.bootstrap.Slider = function(config){
4275     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4276 };
4277
4278 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4279     
4280     getAutoCreate : function(){
4281         
4282         var cfg = {
4283             tag: 'div',
4284             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4285             cn: [
4286                 {
4287                     tag: 'a',
4288                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4289                 }
4290             ]
4291         }
4292         
4293         return cfg;
4294     }
4295    
4296 });
4297
4298  /*
4299  * Based on:
4300  * Ext JS Library 1.1.1
4301  * Copyright(c) 2006-2007, Ext JS, LLC.
4302  *
4303  * Originally Released Under LGPL - original licence link has changed is not relivant.
4304  *
4305  * Fork - LGPL
4306  * <script type="text/javascript">
4307  */
4308  
4309
4310 /**
4311  * @class Roo.grid.ColumnModel
4312  * @extends Roo.util.Observable
4313  * This is the default implementation of a ColumnModel used by the Grid. It defines
4314  * the columns in the grid.
4315  * <br>Usage:<br>
4316  <pre><code>
4317  var colModel = new Roo.grid.ColumnModel([
4318         {header: "Ticker", width: 60, sortable: true, locked: true},
4319         {header: "Company Name", width: 150, sortable: true},
4320         {header: "Market Cap.", width: 100, sortable: true},
4321         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4322         {header: "Employees", width: 100, sortable: true, resizable: false}
4323  ]);
4324  </code></pre>
4325  * <p>
4326  
4327  * The config options listed for this class are options which may appear in each
4328  * individual column definition.
4329  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4330  * @constructor
4331  * @param {Object} config An Array of column config objects. See this class's
4332  * config objects for details.
4333 */
4334 Roo.grid.ColumnModel = function(config){
4335         /**
4336      * The config passed into the constructor
4337      */
4338     this.config = config;
4339     this.lookup = {};
4340
4341     // if no id, create one
4342     // if the column does not have a dataIndex mapping,
4343     // map it to the order it is in the config
4344     for(var i = 0, len = config.length; i < len; i++){
4345         var c = config[i];
4346         if(typeof c.dataIndex == "undefined"){
4347             c.dataIndex = i;
4348         }
4349         if(typeof c.renderer == "string"){
4350             c.renderer = Roo.util.Format[c.renderer];
4351         }
4352         if(typeof c.id == "undefined"){
4353             c.id = Roo.id();
4354         }
4355         if(c.editor && c.editor.xtype){
4356             c.editor  = Roo.factory(c.editor, Roo.grid);
4357         }
4358         if(c.editor && c.editor.isFormField){
4359             c.editor = new Roo.grid.GridEditor(c.editor);
4360         }
4361         this.lookup[c.id] = c;
4362     }
4363
4364     /**
4365      * The width of columns which have no width specified (defaults to 100)
4366      * @type Number
4367      */
4368     this.defaultWidth = 100;
4369
4370     /**
4371      * Default sortable of columns which have no sortable specified (defaults to false)
4372      * @type Boolean
4373      */
4374     this.defaultSortable = false;
4375
4376     this.addEvents({
4377         /**
4378              * @event widthchange
4379              * Fires when the width of a column changes.
4380              * @param {ColumnModel} this
4381              * @param {Number} columnIndex The column index
4382              * @param {Number} newWidth The new width
4383              */
4384             "widthchange": true,
4385         /**
4386              * @event headerchange
4387              * Fires when the text of a header changes.
4388              * @param {ColumnModel} this
4389              * @param {Number} columnIndex The column index
4390              * @param {Number} newText The new header text
4391              */
4392             "headerchange": true,
4393         /**
4394              * @event hiddenchange
4395              * Fires when a column is hidden or "unhidden".
4396              * @param {ColumnModel} this
4397              * @param {Number} columnIndex The column index
4398              * @param {Boolean} hidden true if hidden, false otherwise
4399              */
4400             "hiddenchange": true,
4401             /**
4402          * @event columnmoved
4403          * Fires when a column is moved.
4404          * @param {ColumnModel} this
4405          * @param {Number} oldIndex
4406          * @param {Number} newIndex
4407          */
4408         "columnmoved" : true,
4409         /**
4410          * @event columlockchange
4411          * Fires when a column's locked state is changed
4412          * @param {ColumnModel} this
4413          * @param {Number} colIndex
4414          * @param {Boolean} locked true if locked
4415          */
4416         "columnlockchange" : true
4417     });
4418     Roo.grid.ColumnModel.superclass.constructor.call(this);
4419 };
4420 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4421     /**
4422      * @cfg {String} header The header text to display in the Grid view.
4423      */
4424     /**
4425      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4426      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4427      * specified, the column's index is used as an index into the Record's data Array.
4428      */
4429     /**
4430      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4431      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4432      */
4433     /**
4434      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4435      * Defaults to the value of the {@link #defaultSortable} property.
4436      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4437      */
4438     /**
4439      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4440      */
4441     /**
4442      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4443      */
4444     /**
4445      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4446      */
4447     /**
4448      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4449      */
4450     /**
4451      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4452      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4453      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4454      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4455      */
4456        /**
4457      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4458      */
4459     /**
4460      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4461      */
4462     /**
4463      * @cfg {String} cursor (Optional)
4464      */
4465     /**
4466      * @cfg {String} tooltip (Optional)
4467      */
4468     /**
4469      * Returns the id of the column at the specified index.
4470      * @param {Number} index The column index
4471      * @return {String} the id
4472      */
4473     getColumnId : function(index){
4474         return this.config[index].id;
4475     },
4476
4477     /**
4478      * Returns the column for a specified id.
4479      * @param {String} id The column id
4480      * @return {Object} the column
4481      */
4482     getColumnById : function(id){
4483         return this.lookup[id];
4484     },
4485
4486     
4487     /**
4488      * Returns the column for a specified dataIndex.
4489      * @param {String} dataIndex The column dataIndex
4490      * @return {Object|Boolean} the column or false if not found
4491      */
4492     getColumnByDataIndex: function(dataIndex){
4493         var index = this.findColumnIndex(dataIndex);
4494         return index > -1 ? this.config[index] : false;
4495     },
4496     
4497     /**
4498      * Returns the index for a specified column id.
4499      * @param {String} id The column id
4500      * @return {Number} the index, or -1 if not found
4501      */
4502     getIndexById : function(id){
4503         for(var i = 0, len = this.config.length; i < len; i++){
4504             if(this.config[i].id == id){
4505                 return i;
4506             }
4507         }
4508         return -1;
4509     },
4510     
4511     /**
4512      * Returns the index for a specified column dataIndex.
4513      * @param {String} dataIndex The column dataIndex
4514      * @return {Number} the index, or -1 if not found
4515      */
4516     
4517     findColumnIndex : function(dataIndex){
4518         for(var i = 0, len = this.config.length; i < len; i++){
4519             if(this.config[i].dataIndex == dataIndex){
4520                 return i;
4521             }
4522         }
4523         return -1;
4524     },
4525     
4526     
4527     moveColumn : function(oldIndex, newIndex){
4528         var c = this.config[oldIndex];
4529         this.config.splice(oldIndex, 1);
4530         this.config.splice(newIndex, 0, c);
4531         this.dataMap = null;
4532         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4533     },
4534
4535     isLocked : function(colIndex){
4536         return this.config[colIndex].locked === true;
4537     },
4538
4539     setLocked : function(colIndex, value, suppressEvent){
4540         if(this.isLocked(colIndex) == value){
4541             return;
4542         }
4543         this.config[colIndex].locked = value;
4544         if(!suppressEvent){
4545             this.fireEvent("columnlockchange", this, colIndex, value);
4546         }
4547     },
4548
4549     getTotalLockedWidth : function(){
4550         var totalWidth = 0;
4551         for(var i = 0; i < this.config.length; i++){
4552             if(this.isLocked(i) && !this.isHidden(i)){
4553                 this.totalWidth += this.getColumnWidth(i);
4554             }
4555         }
4556         return totalWidth;
4557     },
4558
4559     getLockedCount : function(){
4560         for(var i = 0, len = this.config.length; i < len; i++){
4561             if(!this.isLocked(i)){
4562                 return i;
4563             }
4564         }
4565     },
4566
4567     /**
4568      * Returns the number of columns.
4569      * @return {Number}
4570      */
4571     getColumnCount : function(visibleOnly){
4572         if(visibleOnly === true){
4573             var c = 0;
4574             for(var i = 0, len = this.config.length; i < len; i++){
4575                 if(!this.isHidden(i)){
4576                     c++;
4577                 }
4578             }
4579             return c;
4580         }
4581         return this.config.length;
4582     },
4583
4584     /**
4585      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4586      * @param {Function} fn
4587      * @param {Object} scope (optional)
4588      * @return {Array} result
4589      */
4590     getColumnsBy : function(fn, scope){
4591         var r = [];
4592         for(var i = 0, len = this.config.length; i < len; i++){
4593             var c = this.config[i];
4594             if(fn.call(scope||this, c, i) === true){
4595                 r[r.length] = c;
4596             }
4597         }
4598         return r;
4599     },
4600
4601     /**
4602      * Returns true if the specified column is sortable.
4603      * @param {Number} col The column index
4604      * @return {Boolean}
4605      */
4606     isSortable : function(col){
4607         if(typeof this.config[col].sortable == "undefined"){
4608             return this.defaultSortable;
4609         }
4610         return this.config[col].sortable;
4611     },
4612
4613     /**
4614      * Returns the rendering (formatting) function defined for the column.
4615      * @param {Number} col The column index.
4616      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4617      */
4618     getRenderer : function(col){
4619         if(!this.config[col].renderer){
4620             return Roo.grid.ColumnModel.defaultRenderer;
4621         }
4622         return this.config[col].renderer;
4623     },
4624
4625     /**
4626      * Sets the rendering (formatting) function for a column.
4627      * @param {Number} col The column index
4628      * @param {Function} fn The function to use to process the cell's raw data
4629      * to return HTML markup for the grid view. The render function is called with
4630      * the following parameters:<ul>
4631      * <li>Data value.</li>
4632      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4633      * <li>css A CSS style string to apply to the table cell.</li>
4634      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4635      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4636      * <li>Row index</li>
4637      * <li>Column index</li>
4638      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4639      */
4640     setRenderer : function(col, fn){
4641         this.config[col].renderer = fn;
4642     },
4643
4644     /**
4645      * Returns the width for the specified column.
4646      * @param {Number} col The column index
4647      * @return {Number}
4648      */
4649     getColumnWidth : function(col){
4650         return this.config[col].width * 1 || this.defaultWidth;
4651     },
4652
4653     /**
4654      * Sets the width for a column.
4655      * @param {Number} col The column index
4656      * @param {Number} width The new width
4657      */
4658     setColumnWidth : function(col, width, suppressEvent){
4659         this.config[col].width = width;
4660         this.totalWidth = null;
4661         if(!suppressEvent){
4662              this.fireEvent("widthchange", this, col, width);
4663         }
4664     },
4665
4666     /**
4667      * Returns the total width of all columns.
4668      * @param {Boolean} includeHidden True to include hidden column widths
4669      * @return {Number}
4670      */
4671     getTotalWidth : function(includeHidden){
4672         if(!this.totalWidth){
4673             this.totalWidth = 0;
4674             for(var i = 0, len = this.config.length; i < len; i++){
4675                 if(includeHidden || !this.isHidden(i)){
4676                     this.totalWidth += this.getColumnWidth(i);
4677                 }
4678             }
4679         }
4680         return this.totalWidth;
4681     },
4682
4683     /**
4684      * Returns the header for the specified column.
4685      * @param {Number} col The column index
4686      * @return {String}
4687      */
4688     getColumnHeader : function(col){
4689         return this.config[col].header;
4690     },
4691
4692     /**
4693      * Sets the header for a column.
4694      * @param {Number} col The column index
4695      * @param {String} header The new header
4696      */
4697     setColumnHeader : function(col, header){
4698         this.config[col].header = header;
4699         this.fireEvent("headerchange", this, col, header);
4700     },
4701
4702     /**
4703      * Returns the tooltip for the specified column.
4704      * @param {Number} col The column index
4705      * @return {String}
4706      */
4707     getColumnTooltip : function(col){
4708             return this.config[col].tooltip;
4709     },
4710     /**
4711      * Sets the tooltip for a column.
4712      * @param {Number} col The column index
4713      * @param {String} tooltip The new tooltip
4714      */
4715     setColumnTooltip : function(col, tooltip){
4716             this.config[col].tooltip = tooltip;
4717     },
4718
4719     /**
4720      * Returns the dataIndex for the specified column.
4721      * @param {Number} col The column index
4722      * @return {Number}
4723      */
4724     getDataIndex : function(col){
4725         return this.config[col].dataIndex;
4726     },
4727
4728     /**
4729      * Sets the dataIndex for a column.
4730      * @param {Number} col The column index
4731      * @param {Number} dataIndex The new dataIndex
4732      */
4733     setDataIndex : function(col, dataIndex){
4734         this.config[col].dataIndex = dataIndex;
4735     },
4736
4737     
4738     
4739     /**
4740      * Returns true if the cell is editable.
4741      * @param {Number} colIndex The column index
4742      * @param {Number} rowIndex The row index
4743      * @return {Boolean}
4744      */
4745     isCellEditable : function(colIndex, rowIndex){
4746         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4747     },
4748
4749     /**
4750      * Returns the editor defined for the cell/column.
4751      * return false or null to disable editing.
4752      * @param {Number} colIndex The column index
4753      * @param {Number} rowIndex The row index
4754      * @return {Object}
4755      */
4756     getCellEditor : function(colIndex, rowIndex){
4757         return this.config[colIndex].editor;
4758     },
4759
4760     /**
4761      * Sets if a column is editable.
4762      * @param {Number} col The column index
4763      * @param {Boolean} editable True if the column is editable
4764      */
4765     setEditable : function(col, editable){
4766         this.config[col].editable = editable;
4767     },
4768
4769
4770     /**
4771      * Returns true if the column is hidden.
4772      * @param {Number} colIndex The column index
4773      * @return {Boolean}
4774      */
4775     isHidden : function(colIndex){
4776         return this.config[colIndex].hidden;
4777     },
4778
4779
4780     /**
4781      * Returns true if the column width cannot be changed
4782      */
4783     isFixed : function(colIndex){
4784         return this.config[colIndex].fixed;
4785     },
4786
4787     /**
4788      * Returns true if the column can be resized
4789      * @return {Boolean}
4790      */
4791     isResizable : function(colIndex){
4792         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4793     },
4794     /**
4795      * Sets if a column is hidden.
4796      * @param {Number} colIndex The column index
4797      * @param {Boolean} hidden True if the column is hidden
4798      */
4799     setHidden : function(colIndex, hidden){
4800         this.config[colIndex].hidden = hidden;
4801         this.totalWidth = null;
4802         this.fireEvent("hiddenchange", this, colIndex, hidden);
4803     },
4804
4805     /**
4806      * Sets the editor for a column.
4807      * @param {Number} col The column index
4808      * @param {Object} editor The editor object
4809      */
4810     setEditor : function(col, editor){
4811         this.config[col].editor = editor;
4812     }
4813 });
4814
4815 Roo.grid.ColumnModel.defaultRenderer = function(value){
4816         if(typeof value == "string" && value.length < 1){
4817             return "&#160;";
4818         }
4819         return value;
4820 };
4821
4822 // Alias for backwards compatibility
4823 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4824 /*
4825  * Based on:
4826  * Ext JS Library 1.1.1
4827  * Copyright(c) 2006-2007, Ext JS, LLC.
4828  *
4829  * Originally Released Under LGPL - original licence link has changed is not relivant.
4830  *
4831  * Fork - LGPL
4832  * <script type="text/javascript">
4833  */
4834  
4835 /**
4836  * @class Roo.LoadMask
4837  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4838  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4839  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4840  * element's UpdateManager load indicator and will be destroyed after the initial load.
4841  * @constructor
4842  * Create a new LoadMask
4843  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4844  * @param {Object} config The config object
4845  */
4846 Roo.LoadMask = function(el, config){
4847     this.el = Roo.get(el);
4848     Roo.apply(this, config);
4849     if(this.store){
4850         this.store.on('beforeload', this.onBeforeLoad, this);
4851         this.store.on('load', this.onLoad, this);
4852         this.store.on('loadexception', this.onLoadException, this);
4853         this.removeMask = false;
4854     }else{
4855         var um = this.el.getUpdateManager();
4856         um.showLoadIndicator = false; // disable the default indicator
4857         um.on('beforeupdate', this.onBeforeLoad, this);
4858         um.on('update', this.onLoad, this);
4859         um.on('failure', this.onLoad, this);
4860         this.removeMask = true;
4861     }
4862 };
4863
4864 Roo.LoadMask.prototype = {
4865     /**
4866      * @cfg {Boolean} removeMask
4867      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4868      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4869      */
4870     /**
4871      * @cfg {String} msg
4872      * The text to display in a centered loading message box (defaults to 'Loading...')
4873      */
4874     msg : 'Loading...',
4875     /**
4876      * @cfg {String} msgCls
4877      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4878      */
4879     msgCls : 'x-mask-loading',
4880
4881     /**
4882      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4883      * @type Boolean
4884      */
4885     disabled: false,
4886
4887     /**
4888      * Disables the mask to prevent it from being displayed
4889      */
4890     disable : function(){
4891        this.disabled = true;
4892     },
4893
4894     /**
4895      * Enables the mask so that it can be displayed
4896      */
4897     enable : function(){
4898         this.disabled = false;
4899     },
4900     
4901     onLoadException : function()
4902     {
4903         Roo.log(arguments);
4904         
4905         if (typeof(arguments[3]) != 'undefined') {
4906             Roo.MessageBox.alert("Error loading",arguments[3]);
4907         } 
4908         /*
4909         try {
4910             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4911                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4912             }   
4913         } catch(e) {
4914             
4915         }
4916         */
4917     
4918         
4919         
4920         this.el.unmask(this.removeMask);
4921     },
4922     // private
4923     onLoad : function()
4924     {
4925         this.el.unmask(this.removeMask);
4926     },
4927
4928     // private
4929     onBeforeLoad : function(){
4930         if(!this.disabled){
4931             this.el.mask(this.msg, this.msgCls);
4932         }
4933     },
4934
4935     // private
4936     destroy : function(){
4937         if(this.store){
4938             this.store.un('beforeload', this.onBeforeLoad, this);
4939             this.store.un('load', this.onLoad, this);
4940             this.store.un('loadexception', this.onLoadException, this);
4941         }else{
4942             var um = this.el.getUpdateManager();
4943             um.un('beforeupdate', this.onBeforeLoad, this);
4944             um.un('update', this.onLoad, this);
4945             um.un('failure', this.onLoad, this);
4946         }
4947     }
4948 };/*
4949  * - LGPL
4950  *
4951  * table
4952  * 
4953  */
4954
4955 /**
4956  * @class Roo.bootstrap.Table
4957  * @extends Roo.bootstrap.Component
4958  * Bootstrap Table class
4959  * @cfg {String} cls table class
4960  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4961  * @cfg {String} bgcolor Specifies the background color for a table
4962  * @cfg {Number} border Specifies whether the table cells should have borders or not
4963  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4964  * @cfg {Number} cellspacing Specifies the space between cells
4965  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4966  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4967  * @cfg {String} sortable Specifies that the table should be sortable
4968  * @cfg {String} summary Specifies a summary of the content of a table
4969  * @cfg {Number} width Specifies the width of a table
4970  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4971  * 
4972  * @cfg {boolean} striped Should the rows be alternative striped
4973  * @cfg {boolean} bordered Add borders to the table
4974  * @cfg {boolean} hover Add hover highlighting
4975  * @cfg {boolean} condensed Format condensed
4976  * @cfg {boolean} responsive Format condensed
4977  * @cfg {Boolean} loadMask (true|false) default false
4978  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4979  * @cfg {Boolean} thead (true|false) generate thead, default true
4980  * @cfg {Boolean} RowSelection (true|false) default false
4981  * @cfg {Boolean} CellSelection (true|false) default false
4982  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4983  
4984  * 
4985  * @constructor
4986  * Create a new Table
4987  * @param {Object} config The config object
4988  */
4989
4990 Roo.bootstrap.Table = function(config){
4991     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4992     
4993     if (this.sm) {
4994         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4995         this.sm = this.selModel;
4996         this.sm.xmodule = this.xmodule || false;
4997     }
4998     if (this.cm && typeof(this.cm.config) == 'undefined') {
4999         this.colModel = new Roo.grid.ColumnModel(this.cm);
5000         this.cm = this.colModel;
5001         this.cm.xmodule = this.xmodule || false;
5002     }
5003     if (this.store) {
5004         this.store= Roo.factory(this.store, Roo.data);
5005         this.ds = this.store;
5006         this.ds.xmodule = this.xmodule || false;
5007          
5008     }
5009     if (this.footer && this.store) {
5010         this.footer.dataSource = this.ds;
5011         this.footer = Roo.factory(this.footer);
5012     }
5013     
5014     /** @private */
5015     this.addEvents({
5016         /**
5017          * @event cellclick
5018          * Fires when a cell is clicked
5019          * @param {Roo.bootstrap.Table} this
5020          * @param {Roo.Element} el
5021          * @param {Number} rowIndex
5022          * @param {Number} columnIndex
5023          * @param {Roo.EventObject} e
5024          */
5025         "cellclick" : true,
5026         /**
5027          * @event celldblclick
5028          * Fires when a cell is double clicked
5029          * @param {Roo.bootstrap.Table} this
5030          * @param {Roo.Element} el
5031          * @param {Number} rowIndex
5032          * @param {Number} columnIndex
5033          * @param {Roo.EventObject} e
5034          */
5035         "celldblclick" : true,
5036         /**
5037          * @event rowclick
5038          * Fires when a row is clicked
5039          * @param {Roo.bootstrap.Table} this
5040          * @param {Roo.Element} el
5041          * @param {Number} rowIndex
5042          * @param {Roo.EventObject} e
5043          */
5044         "rowclick" : true,
5045         /**
5046          * @event rowdblclick
5047          * Fires when a row is double clicked
5048          * @param {Roo.bootstrap.Table} this
5049          * @param {Roo.Element} el
5050          * @param {Number} rowIndex
5051          * @param {Roo.EventObject} e
5052          */
5053         "rowdblclick" : true,
5054         /**
5055          * @event mouseover
5056          * Fires when a mouseover occur
5057          * @param {Roo.bootstrap.Table} this
5058          * @param {Roo.Element} el
5059          * @param {Number} rowIndex
5060          * @param {Number} columnIndex
5061          * @param {Roo.EventObject} e
5062          */
5063         "mouseover" : true,
5064         /**
5065          * @event mouseout
5066          * Fires when a mouseout occur
5067          * @param {Roo.bootstrap.Table} this
5068          * @param {Roo.Element} el
5069          * @param {Number} rowIndex
5070          * @param {Number} columnIndex
5071          * @param {Roo.EventObject} e
5072          */
5073         "mouseout" : true,
5074         /**
5075          * @event rowclass
5076          * Fires when a row is rendered, so you can change add a style to it.
5077          * @param {Roo.bootstrap.Table} this
5078          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5079          */
5080         'rowclass' : true,
5081           /**
5082          * @event rowsrendered
5083          * Fires when all the  rows have been rendered
5084          * @param {Roo.bootstrap.Table} this
5085          */
5086         'rowsrendered' : true
5087         
5088     });
5089 };
5090
5091 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5092     
5093     cls: false,
5094     align: false,
5095     bgcolor: false,
5096     border: false,
5097     cellpadding: false,
5098     cellspacing: false,
5099     frame: false,
5100     rules: false,
5101     sortable: false,
5102     summary: false,
5103     width: false,
5104     striped : false,
5105     bordered: false,
5106     hover:  false,
5107     condensed : false,
5108     responsive : false,
5109     sm : false,
5110     cm : false,
5111     store : false,
5112     loadMask : false,
5113     tfoot : true,
5114     thead : true,
5115     RowSelection : false,
5116     CellSelection : false,
5117     layout : false,
5118     
5119     // Roo.Element - the tbody
5120     mainBody: false, 
5121     
5122     getAutoCreate : function(){
5123         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5124         
5125         cfg = {
5126             tag: 'table',
5127             cls : 'table',
5128             cn : []
5129         }
5130             
5131         if (this.striped) {
5132             cfg.cls += ' table-striped';
5133         }
5134         
5135         if (this.hover) {
5136             cfg.cls += ' table-hover';
5137         }
5138         if (this.bordered) {
5139             cfg.cls += ' table-bordered';
5140         }
5141         if (this.condensed) {
5142             cfg.cls += ' table-condensed';
5143         }
5144         if (this.responsive) {
5145             cfg.cls += ' table-responsive';
5146         }
5147         
5148         if (this.cls) {
5149             cfg.cls+=  ' ' +this.cls;
5150         }
5151         
5152         // this lot should be simplifed...
5153         
5154         if (this.align) {
5155             cfg.align=this.align;
5156         }
5157         if (this.bgcolor) {
5158             cfg.bgcolor=this.bgcolor;
5159         }
5160         if (this.border) {
5161             cfg.border=this.border;
5162         }
5163         if (this.cellpadding) {
5164             cfg.cellpadding=this.cellpadding;
5165         }
5166         if (this.cellspacing) {
5167             cfg.cellspacing=this.cellspacing;
5168         }
5169         if (this.frame) {
5170             cfg.frame=this.frame;
5171         }
5172         if (this.rules) {
5173             cfg.rules=this.rules;
5174         }
5175         if (this.sortable) {
5176             cfg.sortable=this.sortable;
5177         }
5178         if (this.summary) {
5179             cfg.summary=this.summary;
5180         }
5181         if (this.width) {
5182             cfg.width=this.width;
5183         }
5184         if (this.layout) {
5185             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5186         }
5187         
5188         if(this.store || this.cm){
5189             if(this.thead){
5190                 cfg.cn.push(this.renderHeader());
5191             }
5192             
5193             cfg.cn.push(this.renderBody());
5194             
5195             if(this.tfoot){
5196                 cfg.cn.push(this.renderFooter());
5197             }
5198             
5199             cfg.cls+=  ' TableGrid';
5200         }
5201         
5202         return { cn : [ cfg ] };
5203     },
5204     
5205     initEvents : function()
5206     {   
5207         if(!this.store || !this.cm){
5208             return;
5209         }
5210         
5211         //Roo.log('initEvents with ds!!!!');
5212         
5213         this.mainBody = this.el.select('tbody', true).first();
5214         
5215         
5216         var _this = this;
5217         
5218         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5219             e.on('click', _this.sort, _this);
5220         });
5221         
5222         this.el.on("click", this.onClick, this);
5223         this.el.on("dblclick", this.onDblClick, this);
5224         
5225         // why is this done????? = it breaks dialogs??
5226         //this.parent().el.setStyle('position', 'relative');
5227         
5228         
5229         if (this.footer) {
5230             this.footer.parentId = this.id;
5231             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5232         }
5233         
5234         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5235         
5236         this.store.on('load', this.onLoad, this);
5237         this.store.on('beforeload', this.onBeforeLoad, this);
5238         this.store.on('update', this.onUpdate, this);
5239         this.store.on('add', this.onAdd, this);
5240         
5241     },
5242     
5243     onMouseover : function(e, el)
5244     {
5245         var cell = Roo.get(el);
5246         
5247         if(!cell){
5248             return;
5249         }
5250         
5251         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5252             cell = cell.findParent('td', false, true);
5253         }
5254         
5255         var row = cell.findParent('tr', false, true);
5256         var cellIndex = cell.dom.cellIndex;
5257         var rowIndex = row.dom.rowIndex - 1; // start from 0
5258         
5259         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5260         
5261     },
5262     
5263     onMouseout : function(e, el)
5264     {
5265         var cell = Roo.get(el);
5266         
5267         if(!cell){
5268             return;
5269         }
5270         
5271         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5272             cell = cell.findParent('td', false, true);
5273         }
5274         
5275         var row = cell.findParent('tr', false, true);
5276         var cellIndex = cell.dom.cellIndex;
5277         var rowIndex = row.dom.rowIndex - 1; // start from 0
5278         
5279         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5280         
5281     },
5282     
5283     onClick : function(e, el)
5284     {
5285         var cell = Roo.get(el);
5286         
5287         if(!cell || (!this.CellSelection && !this.RowSelection)){
5288             return;
5289         }
5290         
5291         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5292             cell = cell.findParent('td', false, true);
5293         }
5294         
5295         if(!cell || typeof(cell) == 'undefined'){
5296             return;
5297         }
5298         
5299         var row = cell.findParent('tr', false, true);
5300         
5301         if(!row || typeof(row) == 'undefined'){
5302             return;
5303         }
5304         
5305         var cellIndex = cell.dom.cellIndex;
5306         var rowIndex = this.getRowIndex(row);
5307         
5308         if(this.CellSelection){
5309             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5310         }
5311         
5312         if(this.RowSelection){
5313             this.fireEvent('rowclick', this, row, rowIndex, e);
5314         }
5315         
5316         
5317     },
5318     
5319     onDblClick : function(e,el)
5320     {
5321         var cell = Roo.get(el);
5322         
5323         if(!cell || (!this.CellSelection && !this.RowSelection)){
5324             return;
5325         }
5326         
5327         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5328             cell = cell.findParent('td', false, true);
5329         }
5330         
5331         if(!cell || typeof(cell) == 'undefined'){
5332             return;
5333         }
5334         
5335         var row = cell.findParent('tr', false, true);
5336         
5337         if(!row || typeof(row) == 'undefined'){
5338             return;
5339         }
5340         
5341         var cellIndex = cell.dom.cellIndex;
5342         var rowIndex = this.getRowIndex(row);
5343         
5344         if(this.CellSelection){
5345             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5346         }
5347         
5348         if(this.RowSelection){
5349             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5350         }
5351     },
5352     
5353     sort : function(e,el)
5354     {
5355         var col = Roo.get(el)
5356         
5357         if(!col.hasClass('sortable')){
5358             return;
5359         }
5360         
5361         var sort = col.attr('sort');
5362         var dir = 'ASC';
5363         
5364         if(col.hasClass('glyphicon-arrow-up')){
5365             dir = 'DESC';
5366         }
5367         
5368         this.store.sortInfo = {field : sort, direction : dir};
5369         
5370         if (this.footer) {
5371             Roo.log("calling footer first");
5372             this.footer.onClick('first');
5373         } else {
5374         
5375             this.store.load({ params : { start : 0 } });
5376         }
5377     },
5378     
5379     renderHeader : function()
5380     {
5381         var header = {
5382             tag: 'thead',
5383             cn : []
5384         };
5385         
5386         var cm = this.cm;
5387         
5388         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5389             
5390             var config = cm.config[i];
5391                     
5392             var c = {
5393                 tag: 'th',
5394                 style : '',
5395                 html: cm.getColumnHeader(i)
5396             };
5397             
5398             if(typeof(config.tooltip) != 'undefined'){
5399                 c.tooltip = config.tooltip;
5400             }
5401             
5402             if(typeof(config.hidden) != 'undefined' && config.hidden){
5403                 c.style += ' display:none;';
5404             }
5405             
5406             if(typeof(config.dataIndex) != 'undefined'){
5407                 c.sort = config.dataIndex;
5408             }
5409             
5410             if(typeof(config.sortable) != 'undefined' && config.sortable){
5411                 c.cls = 'sortable';
5412             }
5413             
5414             if(typeof(config.align) != 'undefined' && config.align.length){
5415                 c.style += ' text-align:' + config.align + ';';
5416             }
5417             
5418             if(typeof(config.width) != 'undefined'){
5419                 c.style += ' width:' + config.width + 'px;';
5420             }
5421             
5422             header.cn.push(c)
5423         }
5424         
5425         return header;
5426     },
5427     
5428     renderBody : function()
5429     {
5430         var body = {
5431             tag: 'tbody',
5432             cn : [
5433                 {
5434                     tag: 'tr',
5435                     cn : [
5436                         {
5437                             tag : 'td',
5438                             colspan :  this.cm.getColumnCount()
5439                         }
5440                     ]
5441                 }
5442             ]
5443         };
5444         
5445         return body;
5446     },
5447     
5448     renderFooter : function()
5449     {
5450         var footer = {
5451             tag: 'tfoot',
5452             cn : [
5453                 {
5454                     tag: 'tr',
5455                     cn : [
5456                         {
5457                             tag : 'td',
5458                             colspan :  this.cm.getColumnCount()
5459                         }
5460                     ]
5461                 }
5462             ]
5463         };
5464         
5465         return footer;
5466     },
5467     
5468     
5469     
5470     onLoad : function()
5471     {
5472         Roo.log('ds onload');
5473         this.clear();
5474         
5475         var _this = this;
5476         var cm = this.cm;
5477         var ds = this.store;
5478         
5479         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5480             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5481             
5482             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5483                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5484             }
5485             
5486             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5487                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5488             }
5489         });
5490         
5491         var tbody =  this.mainBody;
5492               
5493         if(ds.getCount() > 0){
5494             ds.data.each(function(d,rowIndex){
5495                 var row =  this.renderRow(cm, ds, rowIndex);
5496                 
5497                 tbody.createChild(row);
5498                 
5499                 var _this = this;
5500                 
5501                 if(row.cellObjects.length){
5502                     Roo.each(row.cellObjects, function(r){
5503                         _this.renderCellObject(r);
5504                     })
5505                 }
5506                 
5507             }, this);
5508         }
5509         
5510         Roo.each(this.el.select('tbody td', true).elements, function(e){
5511             e.on('mouseover', _this.onMouseover, _this);
5512         });
5513         
5514         Roo.each(this.el.select('tbody td', true).elements, function(e){
5515             e.on('mouseout', _this.onMouseout, _this);
5516         });
5517         this.fireEvent('rowsrendered', this);
5518         //if(this.loadMask){
5519         //    this.maskEl.hide();
5520         //}
5521     },
5522     
5523     
5524     onUpdate : function(ds,record)
5525     {
5526         this.refreshRow(record);
5527     },
5528     
5529     onRemove : function(ds, record, index, isUpdate){
5530         if(isUpdate !== true){
5531             this.fireEvent("beforerowremoved", this, index, record);
5532         }
5533         var bt = this.mainBody.dom;
5534         
5535         var rows = this.el.select('tbody > tr', true).elements;
5536         
5537         if(typeof(rows[index]) != 'undefined'){
5538             bt.removeChild(rows[index].dom);
5539         }
5540         
5541 //        if(bt.rows[index]){
5542 //            bt.removeChild(bt.rows[index]);
5543 //        }
5544         
5545         if(isUpdate !== true){
5546             //this.stripeRows(index);
5547             //this.syncRowHeights(index, index);
5548             //this.layout();
5549             this.fireEvent("rowremoved", this, index, record);
5550         }
5551     },
5552     
5553     onAdd : function(ds, records, rowIndex)
5554     {
5555         //Roo.log('on Add called');
5556         // - note this does not handle multiple adding very well..
5557         var bt = this.mainBody.dom;
5558         for (var i =0 ; i < records.length;i++) {
5559             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5560             //Roo.log(records[i]);
5561             //Roo.log(this.store.getAt(rowIndex+i));
5562             this.insertRow(this.store, rowIndex + i, false);
5563             return;
5564         }
5565         
5566     },
5567     
5568     
5569     refreshRow : function(record){
5570         var ds = this.store, index;
5571         if(typeof record == 'number'){
5572             index = record;
5573             record = ds.getAt(index);
5574         }else{
5575             index = ds.indexOf(record);
5576         }
5577         this.insertRow(ds, index, true);
5578         this.onRemove(ds, record, index+1, true);
5579         //this.syncRowHeights(index, index);
5580         //this.layout();
5581         this.fireEvent("rowupdated", this, index, record);
5582     },
5583     
5584     insertRow : function(dm, rowIndex, isUpdate){
5585         
5586         if(!isUpdate){
5587             this.fireEvent("beforerowsinserted", this, rowIndex);
5588         }
5589             //var s = this.getScrollState();
5590         var row = this.renderRow(this.cm, this.store, rowIndex);
5591         // insert before rowIndex..
5592         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5593         
5594         var _this = this;
5595                 
5596         if(row.cellObjects.length){
5597             Roo.each(row.cellObjects, function(r){
5598                 _this.renderCellObject(r);
5599             })
5600         }
5601             
5602         if(!isUpdate){
5603             this.fireEvent("rowsinserted", this, rowIndex);
5604             //this.syncRowHeights(firstRow, lastRow);
5605             //this.stripeRows(firstRow);
5606             //this.layout();
5607         }
5608         
5609     },
5610     
5611     
5612     getRowDom : function(rowIndex)
5613     {
5614         var rows = this.el.select('tbody > tr', true).elements;
5615         
5616         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5617         
5618     },
5619     // returns the object tree for a tr..
5620   
5621     
5622     renderRow : function(cm, ds, rowIndex) 
5623     {
5624         
5625         var d = ds.getAt(rowIndex);
5626         
5627         var row = {
5628             tag : 'tr',
5629             cn : []
5630         };
5631             
5632         var cellObjects = [];
5633         
5634         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5635             var config = cm.config[i];
5636             
5637             var renderer = cm.getRenderer(i);
5638             var value = '';
5639             var id = false;
5640             
5641             if(typeof(renderer) !== 'undefined'){
5642                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5643             }
5644             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5645             // and are rendered into the cells after the row is rendered - using the id for the element.
5646             
5647             if(typeof(value) === 'object'){
5648                 id = Roo.id();
5649                 cellObjects.push({
5650                     container : id,
5651                     cfg : value 
5652                 })
5653             }
5654             
5655             var rowcfg = {
5656                 record: d,
5657                 rowIndex : rowIndex,
5658                 colIndex : i,
5659                 rowClass : ''
5660             }
5661
5662             this.fireEvent('rowclass', this, rowcfg);
5663             
5664             var td = {
5665                 tag: 'td',
5666                 cls : rowcfg.rowClass,
5667                 style: '',
5668                 html: (typeof(value) === 'object') ? '' : value
5669             };
5670             
5671             if (id) {
5672                 td.id = id;
5673             }
5674             
5675             if(typeof(config.hidden) != 'undefined' && config.hidden){
5676                 td.style += ' display:none;';
5677             }
5678             
5679             if(typeof(config.align) != 'undefined' && config.align.length){
5680                 td.style += ' text-align:' + config.align + ';';
5681             }
5682             
5683             if(typeof(config.width) != 'undefined'){
5684                 td.style += ' width:' +  config.width + 'px;';
5685             }
5686             
5687             if(typeof(config.cursor) != 'undefined'){
5688                 td.style += ' cursor:' +  config.cursor + ';';
5689             }
5690              
5691             row.cn.push(td);
5692            
5693         }
5694         
5695         row.cellObjects = cellObjects;
5696         
5697         return row;
5698           
5699     },
5700     
5701     
5702     
5703     onBeforeLoad : function()
5704     {
5705         //Roo.log('ds onBeforeLoad');
5706         
5707         //this.clear();
5708         
5709         //if(this.loadMask){
5710         //    this.maskEl.show();
5711         //}
5712     },
5713      /**
5714      * Remove all rows
5715      */
5716     clear : function()
5717     {
5718         this.el.select('tbody', true).first().dom.innerHTML = '';
5719     },
5720     /**
5721      * Show or hide a row.
5722      * @param {Number} rowIndex to show or hide
5723      * @param {Boolean} state hide
5724      */
5725     setRowVisibility : function(rowIndex, state)
5726     {
5727         var bt = this.mainBody.dom;
5728         
5729         var rows = this.el.select('tbody > tr', true).elements;
5730         
5731         if(typeof(rows[rowIndex]) == 'undefined'){
5732             return;
5733         }
5734         rows[rowIndex].dom.style.display = state ? '' : 'none';
5735     },
5736     
5737     
5738     getSelectionModel : function(){
5739         if(!this.selModel){
5740             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5741         }
5742         return this.selModel;
5743     },
5744     /*
5745      * Render the Roo.bootstrap object from renderder
5746      */
5747     renderCellObject : function(r)
5748     {
5749         var _this = this;
5750         
5751         var t = r.cfg.render(r.container);
5752         
5753         if(r.cfg.cn){
5754             Roo.each(r.cfg.cn, function(c){
5755                 var child = {
5756                     container: t.getChildContainer(),
5757                     cfg: c
5758                 }
5759                 _this.renderCellObject(child);
5760             })
5761         }
5762     },
5763     
5764     getRowIndex : function(row)
5765     {
5766         var rowIndex = -1;
5767         
5768         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
5769             if(el != row){
5770                 return;
5771             }
5772             
5773             rowIndex = index;
5774         });
5775         
5776         return rowIndex;
5777     }
5778    
5779 });
5780
5781  
5782
5783  /*
5784  * - LGPL
5785  *
5786  * table cell
5787  * 
5788  */
5789
5790 /**
5791  * @class Roo.bootstrap.TableCell
5792  * @extends Roo.bootstrap.Component
5793  * Bootstrap TableCell class
5794  * @cfg {String} html cell contain text
5795  * @cfg {String} cls cell class
5796  * @cfg {String} tag cell tag (td|th) default td
5797  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5798  * @cfg {String} align Aligns the content in a cell
5799  * @cfg {String} axis Categorizes cells
5800  * @cfg {String} bgcolor Specifies the background color of a cell
5801  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5802  * @cfg {Number} colspan Specifies the number of columns a cell should span
5803  * @cfg {String} headers Specifies one or more header cells a cell is related to
5804  * @cfg {Number} height Sets the height of a cell
5805  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5806  * @cfg {Number} rowspan Sets the number of rows a cell should span
5807  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5808  * @cfg {String} valign Vertical aligns the content in a cell
5809  * @cfg {Number} width Specifies the width of a cell
5810  * 
5811  * @constructor
5812  * Create a new TableCell
5813  * @param {Object} config The config object
5814  */
5815
5816 Roo.bootstrap.TableCell = function(config){
5817     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5818 };
5819
5820 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5821     
5822     html: false,
5823     cls: false,
5824     tag: false,
5825     abbr: false,
5826     align: false,
5827     axis: false,
5828     bgcolor: false,
5829     charoff: false,
5830     colspan: false,
5831     headers: false,
5832     height: false,
5833     nowrap: false,
5834     rowspan: false,
5835     scope: false,
5836     valign: false,
5837     width: false,
5838     
5839     
5840     getAutoCreate : function(){
5841         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5842         
5843         cfg = {
5844             tag: 'td'
5845         }
5846         
5847         if(this.tag){
5848             cfg.tag = this.tag;
5849         }
5850         
5851         if (this.html) {
5852             cfg.html=this.html
5853         }
5854         if (this.cls) {
5855             cfg.cls=this.cls
5856         }
5857         if (this.abbr) {
5858             cfg.abbr=this.abbr
5859         }
5860         if (this.align) {
5861             cfg.align=this.align
5862         }
5863         if (this.axis) {
5864             cfg.axis=this.axis
5865         }
5866         if (this.bgcolor) {
5867             cfg.bgcolor=this.bgcolor
5868         }
5869         if (this.charoff) {
5870             cfg.charoff=this.charoff
5871         }
5872         if (this.colspan) {
5873             cfg.colspan=this.colspan
5874         }
5875         if (this.headers) {
5876             cfg.headers=this.headers
5877         }
5878         if (this.height) {
5879             cfg.height=this.height
5880         }
5881         if (this.nowrap) {
5882             cfg.nowrap=this.nowrap
5883         }
5884         if (this.rowspan) {
5885             cfg.rowspan=this.rowspan
5886         }
5887         if (this.scope) {
5888             cfg.scope=this.scope
5889         }
5890         if (this.valign) {
5891             cfg.valign=this.valign
5892         }
5893         if (this.width) {
5894             cfg.width=this.width
5895         }
5896         
5897         
5898         return cfg;
5899     }
5900    
5901 });
5902
5903  
5904
5905  /*
5906  * - LGPL
5907  *
5908  * table row
5909  * 
5910  */
5911
5912 /**
5913  * @class Roo.bootstrap.TableRow
5914  * @extends Roo.bootstrap.Component
5915  * Bootstrap TableRow class
5916  * @cfg {String} cls row class
5917  * @cfg {String} align Aligns the content in a table row
5918  * @cfg {String} bgcolor Specifies a background color for a table row
5919  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5920  * @cfg {String} valign Vertical aligns the content in a table row
5921  * 
5922  * @constructor
5923  * Create a new TableRow
5924  * @param {Object} config The config object
5925  */
5926
5927 Roo.bootstrap.TableRow = function(config){
5928     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5929 };
5930
5931 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5932     
5933     cls: false,
5934     align: false,
5935     bgcolor: false,
5936     charoff: false,
5937     valign: false,
5938     
5939     getAutoCreate : function(){
5940         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5941         
5942         cfg = {
5943             tag: 'tr'
5944         }
5945             
5946         if(this.cls){
5947             cfg.cls = this.cls;
5948         }
5949         if(this.align){
5950             cfg.align = this.align;
5951         }
5952         if(this.bgcolor){
5953             cfg.bgcolor = this.bgcolor;
5954         }
5955         if(this.charoff){
5956             cfg.charoff = this.charoff;
5957         }
5958         if(this.valign){
5959             cfg.valign = this.valign;
5960         }
5961         
5962         return cfg;
5963     }
5964    
5965 });
5966
5967  
5968
5969  /*
5970  * - LGPL
5971  *
5972  * table body
5973  * 
5974  */
5975
5976 /**
5977  * @class Roo.bootstrap.TableBody
5978  * @extends Roo.bootstrap.Component
5979  * Bootstrap TableBody class
5980  * @cfg {String} cls element class
5981  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5982  * @cfg {String} align Aligns the content inside the element
5983  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5984  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5985  * 
5986  * @constructor
5987  * Create a new TableBody
5988  * @param {Object} config The config object
5989  */
5990
5991 Roo.bootstrap.TableBody = function(config){
5992     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5993 };
5994
5995 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5996     
5997     cls: false,
5998     tag: false,
5999     align: false,
6000     charoff: false,
6001     valign: false,
6002     
6003     getAutoCreate : function(){
6004         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6005         
6006         cfg = {
6007             tag: 'tbody'
6008         }
6009             
6010         if (this.cls) {
6011             cfg.cls=this.cls
6012         }
6013         if(this.tag){
6014             cfg.tag = this.tag;
6015         }
6016         
6017         if(this.align){
6018             cfg.align = this.align;
6019         }
6020         if(this.charoff){
6021             cfg.charoff = this.charoff;
6022         }
6023         if(this.valign){
6024             cfg.valign = this.valign;
6025         }
6026         
6027         return cfg;
6028     }
6029     
6030     
6031 //    initEvents : function()
6032 //    {
6033 //        
6034 //        if(!this.store){
6035 //            return;
6036 //        }
6037 //        
6038 //        this.store = Roo.factory(this.store, Roo.data);
6039 //        this.store.on('load', this.onLoad, this);
6040 //        
6041 //        this.store.load();
6042 //        
6043 //    },
6044 //    
6045 //    onLoad: function () 
6046 //    {   
6047 //        this.fireEvent('load', this);
6048 //    }
6049 //    
6050 //   
6051 });
6052
6053  
6054
6055  /*
6056  * Based on:
6057  * Ext JS Library 1.1.1
6058  * Copyright(c) 2006-2007, Ext JS, LLC.
6059  *
6060  * Originally Released Under LGPL - original licence link has changed is not relivant.
6061  *
6062  * Fork - LGPL
6063  * <script type="text/javascript">
6064  */
6065
6066 // as we use this in bootstrap.
6067 Roo.namespace('Roo.form');
6068  /**
6069  * @class Roo.form.Action
6070  * Internal Class used to handle form actions
6071  * @constructor
6072  * @param {Roo.form.BasicForm} el The form element or its id
6073  * @param {Object} config Configuration options
6074  */
6075
6076  
6077  
6078 // define the action interface
6079 Roo.form.Action = function(form, options){
6080     this.form = form;
6081     this.options = options || {};
6082 };
6083 /**
6084  * Client Validation Failed
6085  * @const 
6086  */
6087 Roo.form.Action.CLIENT_INVALID = 'client';
6088 /**
6089  * Server Validation Failed
6090  * @const 
6091  */
6092 Roo.form.Action.SERVER_INVALID = 'server';
6093  /**
6094  * Connect to Server Failed
6095  * @const 
6096  */
6097 Roo.form.Action.CONNECT_FAILURE = 'connect';
6098 /**
6099  * Reading Data from Server Failed
6100  * @const 
6101  */
6102 Roo.form.Action.LOAD_FAILURE = 'load';
6103
6104 Roo.form.Action.prototype = {
6105     type : 'default',
6106     failureType : undefined,
6107     response : undefined,
6108     result : undefined,
6109
6110     // interface method
6111     run : function(options){
6112
6113     },
6114
6115     // interface method
6116     success : function(response){
6117
6118     },
6119
6120     // interface method
6121     handleResponse : function(response){
6122
6123     },
6124
6125     // default connection failure
6126     failure : function(response){
6127         
6128         this.response = response;
6129         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6130         this.form.afterAction(this, false);
6131     },
6132
6133     processResponse : function(response){
6134         this.response = response;
6135         if(!response.responseText){
6136             return true;
6137         }
6138         this.result = this.handleResponse(response);
6139         return this.result;
6140     },
6141
6142     // utility functions used internally
6143     getUrl : function(appendParams){
6144         var url = this.options.url || this.form.url || this.form.el.dom.action;
6145         if(appendParams){
6146             var p = this.getParams();
6147             if(p){
6148                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6149             }
6150         }
6151         return url;
6152     },
6153
6154     getMethod : function(){
6155         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6156     },
6157
6158     getParams : function(){
6159         var bp = this.form.baseParams;
6160         var p = this.options.params;
6161         if(p){
6162             if(typeof p == "object"){
6163                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6164             }else if(typeof p == 'string' && bp){
6165                 p += '&' + Roo.urlEncode(bp);
6166             }
6167         }else if(bp){
6168             p = Roo.urlEncode(bp);
6169         }
6170         return p;
6171     },
6172
6173     createCallback : function(){
6174         return {
6175             success: this.success,
6176             failure: this.failure,
6177             scope: this,
6178             timeout: (this.form.timeout*1000),
6179             upload: this.form.fileUpload ? this.success : undefined
6180         };
6181     }
6182 };
6183
6184 Roo.form.Action.Submit = function(form, options){
6185     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6186 };
6187
6188 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6189     type : 'submit',
6190
6191     haveProgress : false,
6192     uploadComplete : false,
6193     
6194     // uploadProgress indicator.
6195     uploadProgress : function()
6196     {
6197         if (!this.form.progressUrl) {
6198             return;
6199         }
6200         
6201         if (!this.haveProgress) {
6202             Roo.MessageBox.progress("Uploading", "Uploading");
6203         }
6204         if (this.uploadComplete) {
6205            Roo.MessageBox.hide();
6206            return;
6207         }
6208         
6209         this.haveProgress = true;
6210    
6211         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6212         
6213         var c = new Roo.data.Connection();
6214         c.request({
6215             url : this.form.progressUrl,
6216             params: {
6217                 id : uid
6218             },
6219             method: 'GET',
6220             success : function(req){
6221                //console.log(data);
6222                 var rdata = false;
6223                 var edata;
6224                 try  {
6225                    rdata = Roo.decode(req.responseText)
6226                 } catch (e) {
6227                     Roo.log("Invalid data from server..");
6228                     Roo.log(edata);
6229                     return;
6230                 }
6231                 if (!rdata || !rdata.success) {
6232                     Roo.log(rdata);
6233                     Roo.MessageBox.alert(Roo.encode(rdata));
6234                     return;
6235                 }
6236                 var data = rdata.data;
6237                 
6238                 if (this.uploadComplete) {
6239                    Roo.MessageBox.hide();
6240                    return;
6241                 }
6242                    
6243                 if (data){
6244                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6245                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6246                     );
6247                 }
6248                 this.uploadProgress.defer(2000,this);
6249             },
6250        
6251             failure: function(data) {
6252                 Roo.log('progress url failed ');
6253                 Roo.log(data);
6254             },
6255             scope : this
6256         });
6257            
6258     },
6259     
6260     
6261     run : function()
6262     {
6263         // run get Values on the form, so it syncs any secondary forms.
6264         this.form.getValues();
6265         
6266         var o = this.options;
6267         var method = this.getMethod();
6268         var isPost = method == 'POST';
6269         if(o.clientValidation === false || this.form.isValid()){
6270             
6271             if (this.form.progressUrl) {
6272                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6273                     (new Date() * 1) + '' + Math.random());
6274                     
6275             } 
6276             
6277             
6278             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6279                 form:this.form.el.dom,
6280                 url:this.getUrl(!isPost),
6281                 method: method,
6282                 params:isPost ? this.getParams() : null,
6283                 isUpload: this.form.fileUpload
6284             }));
6285             
6286             this.uploadProgress();
6287
6288         }else if (o.clientValidation !== false){ // client validation failed
6289             this.failureType = Roo.form.Action.CLIENT_INVALID;
6290             this.form.afterAction(this, false);
6291         }
6292     },
6293
6294     success : function(response)
6295     {
6296         this.uploadComplete= true;
6297         if (this.haveProgress) {
6298             Roo.MessageBox.hide();
6299         }
6300         
6301         
6302         var result = this.processResponse(response);
6303         if(result === true || result.success){
6304             this.form.afterAction(this, true);
6305             return;
6306         }
6307         if(result.errors){
6308             this.form.markInvalid(result.errors);
6309             this.failureType = Roo.form.Action.SERVER_INVALID;
6310         }
6311         this.form.afterAction(this, false);
6312     },
6313     failure : function(response)
6314     {
6315         this.uploadComplete= true;
6316         if (this.haveProgress) {
6317             Roo.MessageBox.hide();
6318         }
6319         
6320         this.response = response;
6321         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6322         this.form.afterAction(this, false);
6323     },
6324     
6325     handleResponse : function(response){
6326         if(this.form.errorReader){
6327             var rs = this.form.errorReader.read(response);
6328             var errors = [];
6329             if(rs.records){
6330                 for(var i = 0, len = rs.records.length; i < len; i++) {
6331                     var r = rs.records[i];
6332                     errors[i] = r.data;
6333                 }
6334             }
6335             if(errors.length < 1){
6336                 errors = null;
6337             }
6338             return {
6339                 success : rs.success,
6340                 errors : errors
6341             };
6342         }
6343         var ret = false;
6344         try {
6345             ret = Roo.decode(response.responseText);
6346         } catch (e) {
6347             ret = {
6348                 success: false,
6349                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6350                 errors : []
6351             };
6352         }
6353         return ret;
6354         
6355     }
6356 });
6357
6358
6359 Roo.form.Action.Load = function(form, options){
6360     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6361     this.reader = this.form.reader;
6362 };
6363
6364 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6365     type : 'load',
6366
6367     run : function(){
6368         
6369         Roo.Ajax.request(Roo.apply(
6370                 this.createCallback(), {
6371                     method:this.getMethod(),
6372                     url:this.getUrl(false),
6373                     params:this.getParams()
6374         }));
6375     },
6376
6377     success : function(response){
6378         
6379         var result = this.processResponse(response);
6380         if(result === true || !result.success || !result.data){
6381             this.failureType = Roo.form.Action.LOAD_FAILURE;
6382             this.form.afterAction(this, false);
6383             return;
6384         }
6385         this.form.clearInvalid();
6386         this.form.setValues(result.data);
6387         this.form.afterAction(this, true);
6388     },
6389
6390     handleResponse : function(response){
6391         if(this.form.reader){
6392             var rs = this.form.reader.read(response);
6393             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6394             return {
6395                 success : rs.success,
6396                 data : data
6397             };
6398         }
6399         return Roo.decode(response.responseText);
6400     }
6401 });
6402
6403 Roo.form.Action.ACTION_TYPES = {
6404     'load' : Roo.form.Action.Load,
6405     'submit' : Roo.form.Action.Submit
6406 };/*
6407  * - LGPL
6408  *
6409  * form
6410  * 
6411  */
6412
6413 /**
6414  * @class Roo.bootstrap.Form
6415  * @extends Roo.bootstrap.Component
6416  * Bootstrap Form class
6417  * @cfg {String} method  GET | POST (default POST)
6418  * @cfg {String} labelAlign top | left (default top)
6419  * @cfg {String} align left  | right - for navbars
6420  * @cfg {Boolean} loadMask load mask when submit (default true)
6421
6422  * 
6423  * @constructor
6424  * Create a new Form
6425  * @param {Object} config The config object
6426  */
6427
6428
6429 Roo.bootstrap.Form = function(config){
6430     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6431     this.addEvents({
6432         /**
6433          * @event clientvalidation
6434          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6435          * @param {Form} this
6436          * @param {Boolean} valid true if the form has passed client-side validation
6437          */
6438         clientvalidation: true,
6439         /**
6440          * @event beforeaction
6441          * Fires before any action is performed. Return false to cancel the action.
6442          * @param {Form} this
6443          * @param {Action} action The action to be performed
6444          */
6445         beforeaction: true,
6446         /**
6447          * @event actionfailed
6448          * Fires when an action fails.
6449          * @param {Form} this
6450          * @param {Action} action The action that failed
6451          */
6452         actionfailed : true,
6453         /**
6454          * @event actioncomplete
6455          * Fires when an action is completed.
6456          * @param {Form} this
6457          * @param {Action} action The action that completed
6458          */
6459         actioncomplete : true
6460     });
6461     
6462 };
6463
6464 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6465       
6466      /**
6467      * @cfg {String} method
6468      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6469      */
6470     method : 'POST',
6471     /**
6472      * @cfg {String} url
6473      * The URL to use for form actions if one isn't supplied in the action options.
6474      */
6475     /**
6476      * @cfg {Boolean} fileUpload
6477      * Set to true if this form is a file upload.
6478      */
6479      
6480     /**
6481      * @cfg {Object} baseParams
6482      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6483      */
6484       
6485     /**
6486      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6487      */
6488     timeout: 30,
6489     /**
6490      * @cfg {Sting} align (left|right) for navbar forms
6491      */
6492     align : 'left',
6493
6494     // private
6495     activeAction : null,
6496  
6497     /**
6498      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6499      * element by passing it or its id or mask the form itself by passing in true.
6500      * @type Mixed
6501      */
6502     waitMsgTarget : false,
6503     
6504     loadMask : true,
6505     
6506     getAutoCreate : function(){
6507         
6508         var cfg = {
6509             tag: 'form',
6510             method : this.method || 'POST',
6511             id : this.id || Roo.id(),
6512             cls : ''
6513         }
6514         if (this.parent().xtype.match(/^Nav/)) {
6515             cfg.cls = 'navbar-form navbar-' + this.align;
6516             
6517         }
6518         
6519         if (this.labelAlign == 'left' ) {
6520             cfg.cls += ' form-horizontal';
6521         }
6522         
6523         
6524         return cfg;
6525     },
6526     initEvents : function()
6527     {
6528         this.el.on('submit', this.onSubmit, this);
6529         // this was added as random key presses on the form where triggering form submit.
6530         this.el.on('keypress', function(e) {
6531             if (e.getCharCode() != 13) {
6532                 return true;
6533             }
6534             // we might need to allow it for textareas.. and some other items.
6535             // check e.getTarget().
6536             
6537             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6538                 return true;
6539             }
6540         
6541             Roo.log("keypress blocked");
6542             
6543             e.preventDefault();
6544             return false;
6545         });
6546         
6547     },
6548     // private
6549     onSubmit : function(e){
6550         e.stopEvent();
6551     },
6552     
6553      /**
6554      * Returns true if client-side validation on the form is successful.
6555      * @return Boolean
6556      */
6557     isValid : function(){
6558         var items = this.getItems();
6559         var valid = true;
6560         items.each(function(f){
6561            if(!f.validate()){
6562                valid = false;
6563                
6564            }
6565         });
6566         return valid;
6567     },
6568     /**
6569      * Returns true if any fields in this form have changed since their original load.
6570      * @return Boolean
6571      */
6572     isDirty : function(){
6573         var dirty = false;
6574         var items = this.getItems();
6575         items.each(function(f){
6576            if(f.isDirty()){
6577                dirty = true;
6578                return false;
6579            }
6580            return true;
6581         });
6582         return dirty;
6583     },
6584      /**
6585      * Performs a predefined action (submit or load) or custom actions you define on this form.
6586      * @param {String} actionName The name of the action type
6587      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6588      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6589      * accept other config options):
6590      * <pre>
6591 Property          Type             Description
6592 ----------------  ---------------  ----------------------------------------------------------------------------------
6593 url               String           The url for the action (defaults to the form's url)
6594 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6595 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6596 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6597                                    validate the form on the client (defaults to false)
6598      * </pre>
6599      * @return {BasicForm} this
6600      */
6601     doAction : function(action, options){
6602         if(typeof action == 'string'){
6603             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6604         }
6605         if(this.fireEvent('beforeaction', this, action) !== false){
6606             this.beforeAction(action);
6607             action.run.defer(100, action);
6608         }
6609         return this;
6610     },
6611     
6612     // private
6613     beforeAction : function(action){
6614         var o = action.options;
6615         
6616         if(this.loadMask){
6617             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6618         }
6619         // not really supported yet.. ??
6620         
6621         //if(this.waitMsgTarget === true){
6622         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6623         //}else if(this.waitMsgTarget){
6624         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6625         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6626         //}else {
6627         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6628        // }
6629          
6630     },
6631
6632     // private
6633     afterAction : function(action, success){
6634         this.activeAction = null;
6635         var o = action.options;
6636         
6637         //if(this.waitMsgTarget === true){
6638             this.el.unmask();
6639         //}else if(this.waitMsgTarget){
6640         //    this.waitMsgTarget.unmask();
6641         //}else{
6642         //    Roo.MessageBox.updateProgress(1);
6643         //    Roo.MessageBox.hide();
6644        // }
6645         // 
6646         if(success){
6647             if(o.reset){
6648                 this.reset();
6649             }
6650             Roo.callback(o.success, o.scope, [this, action]);
6651             this.fireEvent('actioncomplete', this, action);
6652             
6653         }else{
6654             
6655             // failure condition..
6656             // we have a scenario where updates need confirming.
6657             // eg. if a locking scenario exists..
6658             // we look for { errors : { needs_confirm : true }} in the response.
6659             if (
6660                 (typeof(action.result) != 'undefined')  &&
6661                 (typeof(action.result.errors) != 'undefined')  &&
6662                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6663            ){
6664                 var _t = this;
6665                 Roo.log("not supported yet");
6666                  /*
6667                 
6668                 Roo.MessageBox.confirm(
6669                     "Change requires confirmation",
6670                     action.result.errorMsg,
6671                     function(r) {
6672                         if (r != 'yes') {
6673                             return;
6674                         }
6675                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6676                     }
6677                     
6678                 );
6679                 */
6680                 
6681                 
6682                 return;
6683             }
6684             
6685             Roo.callback(o.failure, o.scope, [this, action]);
6686             // show an error message if no failed handler is set..
6687             if (!this.hasListener('actionfailed')) {
6688                 Roo.log("need to add dialog support");
6689                 /*
6690                 Roo.MessageBox.alert("Error",
6691                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6692                         action.result.errorMsg :
6693                         "Saving Failed, please check your entries or try again"
6694                 );
6695                 */
6696             }
6697             
6698             this.fireEvent('actionfailed', this, action);
6699         }
6700         
6701     },
6702     /**
6703      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6704      * @param {String} id The value to search for
6705      * @return Field
6706      */
6707     findField : function(id){
6708         var items = this.getItems();
6709         var field = items.get(id);
6710         if(!field){
6711              items.each(function(f){
6712                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6713                     field = f;
6714                     return false;
6715                 }
6716                 return true;
6717             });
6718         }
6719         return field || null;
6720     },
6721      /**
6722      * Mark fields in this form invalid in bulk.
6723      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6724      * @return {BasicForm} this
6725      */
6726     markInvalid : function(errors){
6727         if(errors instanceof Array){
6728             for(var i = 0, len = errors.length; i < len; i++){
6729                 var fieldError = errors[i];
6730                 var f = this.findField(fieldError.id);
6731                 if(f){
6732                     f.markInvalid(fieldError.msg);
6733                 }
6734             }
6735         }else{
6736             var field, id;
6737             for(id in errors){
6738                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6739                     field.markInvalid(errors[id]);
6740                 }
6741             }
6742         }
6743         //Roo.each(this.childForms || [], function (f) {
6744         //    f.markInvalid(errors);
6745         //});
6746         
6747         return this;
6748     },
6749
6750     /**
6751      * Set values for fields in this form in bulk.
6752      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6753      * @return {BasicForm} this
6754      */
6755     setValues : function(values){
6756         if(values instanceof Array){ // array of objects
6757             for(var i = 0, len = values.length; i < len; i++){
6758                 var v = values[i];
6759                 var f = this.findField(v.id);
6760                 if(f){
6761                     f.setValue(v.value);
6762                     if(this.trackResetOnLoad){
6763                         f.originalValue = f.getValue();
6764                     }
6765                 }
6766             }
6767         }else{ // object hash
6768             var field, id;
6769             for(id in values){
6770                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6771                     
6772                     if (field.setFromData && 
6773                         field.valueField && 
6774                         field.displayField &&
6775                         // combos' with local stores can 
6776                         // be queried via setValue()
6777                         // to set their value..
6778                         (field.store && !field.store.isLocal)
6779                         ) {
6780                         // it's a combo
6781                         var sd = { };
6782                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6783                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6784                         field.setFromData(sd);
6785                         
6786                     } else {
6787                         field.setValue(values[id]);
6788                     }
6789                     
6790                     
6791                     if(this.trackResetOnLoad){
6792                         field.originalValue = field.getValue();
6793                     }
6794                 }
6795             }
6796         }
6797          
6798         //Roo.each(this.childForms || [], function (f) {
6799         //    f.setValues(values);
6800         //});
6801                 
6802         return this;
6803     },
6804
6805     /**
6806      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6807      * they are returned as an array.
6808      * @param {Boolean} asString
6809      * @return {Object}
6810      */
6811     getValues : function(asString){
6812         //if (this.childForms) {
6813             // copy values from the child forms
6814         //    Roo.each(this.childForms, function (f) {
6815         //        this.setValues(f.getValues());
6816         //    }, this);
6817         //}
6818         
6819         
6820         
6821         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6822         if(asString === true){
6823             return fs;
6824         }
6825         return Roo.urlDecode(fs);
6826     },
6827     
6828     /**
6829      * Returns the fields in this form as an object with key/value pairs. 
6830      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6831      * @return {Object}
6832      */
6833     getFieldValues : function(with_hidden)
6834     {
6835         var items = this.getItems();
6836         var ret = {};
6837         items.each(function(f){
6838             if (!f.getName()) {
6839                 return;
6840             }
6841             var v = f.getValue();
6842             if (f.inputType =='radio') {
6843                 if (typeof(ret[f.getName()]) == 'undefined') {
6844                     ret[f.getName()] = ''; // empty..
6845                 }
6846                 
6847                 if (!f.el.dom.checked) {
6848                     return;
6849                     
6850                 }
6851                 v = f.el.dom.value;
6852                 
6853             }
6854             
6855             // not sure if this supported any more..
6856             if ((typeof(v) == 'object') && f.getRawValue) {
6857                 v = f.getRawValue() ; // dates..
6858             }
6859             // combo boxes where name != hiddenName...
6860             if (f.name != f.getName()) {
6861                 ret[f.name] = f.getRawValue();
6862             }
6863             ret[f.getName()] = v;
6864         });
6865         
6866         return ret;
6867     },
6868
6869     /**
6870      * Clears all invalid messages in this form.
6871      * @return {BasicForm} this
6872      */
6873     clearInvalid : function(){
6874         var items = this.getItems();
6875         
6876         items.each(function(f){
6877            f.clearInvalid();
6878         });
6879         
6880         
6881         
6882         return this;
6883     },
6884
6885     /**
6886      * Resets this form.
6887      * @return {BasicForm} this
6888      */
6889     reset : function(){
6890         var items = this.getItems();
6891         items.each(function(f){
6892             f.reset();
6893         });
6894         
6895         Roo.each(this.childForms || [], function (f) {
6896             f.reset();
6897         });
6898        
6899         
6900         return this;
6901     },
6902     getItems : function()
6903     {
6904         var r=new Roo.util.MixedCollection(false, function(o){
6905             return o.id || (o.id = Roo.id());
6906         });
6907         var iter = function(el) {
6908             if (el.inputEl) {
6909                 r.add(el);
6910             }
6911             if (!el.items) {
6912                 return;
6913             }
6914             Roo.each(el.items,function(e) {
6915                 iter(e);
6916             });
6917             
6918             
6919         };
6920         iter(this);
6921         return r;
6922         
6923         
6924         
6925         
6926     }
6927     
6928 });
6929
6930  
6931 /*
6932  * Based on:
6933  * Ext JS Library 1.1.1
6934  * Copyright(c) 2006-2007, Ext JS, LLC.
6935  *
6936  * Originally Released Under LGPL - original licence link has changed is not relivant.
6937  *
6938  * Fork - LGPL
6939  * <script type="text/javascript">
6940  */
6941 /**
6942  * @class Roo.form.VTypes
6943  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6944  * @singleton
6945  */
6946 Roo.form.VTypes = function(){
6947     // closure these in so they are only created once.
6948     var alpha = /^[a-zA-Z_]+$/;
6949     var alphanum = /^[a-zA-Z0-9_]+$/;
6950     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6951     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6952
6953     // All these messages and functions are configurable
6954     return {
6955         /**
6956          * The function used to validate email addresses
6957          * @param {String} value The email address
6958          */
6959         'email' : function(v){
6960             return email.test(v);
6961         },
6962         /**
6963          * The error text to display when the email validation function returns false
6964          * @type String
6965          */
6966         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6967         /**
6968          * The keystroke filter mask to be applied on email input
6969          * @type RegExp
6970          */
6971         'emailMask' : /[a-z0-9_\.\-@]/i,
6972
6973         /**
6974          * The function used to validate URLs
6975          * @param {String} value The URL
6976          */
6977         'url' : function(v){
6978             return url.test(v);
6979         },
6980         /**
6981          * The error text to display when the url validation function returns false
6982          * @type String
6983          */
6984         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6985         
6986         /**
6987          * The function used to validate alpha values
6988          * @param {String} value The value
6989          */
6990         'alpha' : function(v){
6991             return alpha.test(v);
6992         },
6993         /**
6994          * The error text to display when the alpha validation function returns false
6995          * @type String
6996          */
6997         'alphaText' : 'This field should only contain letters and _',
6998         /**
6999          * The keystroke filter mask to be applied on alpha input
7000          * @type RegExp
7001          */
7002         'alphaMask' : /[a-z_]/i,
7003
7004         /**
7005          * The function used to validate alphanumeric values
7006          * @param {String} value The value
7007          */
7008         'alphanum' : function(v){
7009             return alphanum.test(v);
7010         },
7011         /**
7012          * The error text to display when the alphanumeric validation function returns false
7013          * @type String
7014          */
7015         'alphanumText' : 'This field should only contain letters, numbers and _',
7016         /**
7017          * The keystroke filter mask to be applied on alphanumeric input
7018          * @type RegExp
7019          */
7020         'alphanumMask' : /[a-z0-9_]/i
7021     };
7022 }();/*
7023  * - LGPL
7024  *
7025  * Input
7026  * 
7027  */
7028
7029 /**
7030  * @class Roo.bootstrap.Input
7031  * @extends Roo.bootstrap.Component
7032  * Bootstrap Input class
7033  * @cfg {Boolean} disabled is it disabled
7034  * @cfg {String} fieldLabel - the label associated
7035  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7036  * @cfg {String} name name of the input
7037  * @cfg {string} fieldLabel - the label associated
7038  * @cfg {string}  inputType - input / file submit ...
7039  * @cfg {string} placeholder - placeholder to put in text.
7040  * @cfg {string}  before - input group add on before
7041  * @cfg {string} after - input group add on after
7042  * @cfg {string} size - (lg|sm) or leave empty..
7043  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7044  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7045  * @cfg {Number} md colspan out of 12 for computer-sized screens
7046  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7047  * @cfg {string} value default value of the input
7048  * @cfg {Number} labelWidth set the width of label (0-12)
7049  * @cfg {String} labelAlign (top|left)
7050  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7051  * @cfg {String} align (left|center|right) Default left
7052  * 
7053  * 
7054  * @constructor
7055  * Create a new Input
7056  * @param {Object} config The config object
7057  */
7058
7059 Roo.bootstrap.Input = function(config){
7060     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7061    
7062         this.addEvents({
7063             /**
7064              * @event focus
7065              * Fires when this field receives input focus.
7066              * @param {Roo.form.Field} this
7067              */
7068             focus : true,
7069             /**
7070              * @event blur
7071              * Fires when this field loses input focus.
7072              * @param {Roo.form.Field} this
7073              */
7074             blur : true,
7075             /**
7076              * @event specialkey
7077              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7078              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7079              * @param {Roo.form.Field} this
7080              * @param {Roo.EventObject} e The event object
7081              */
7082             specialkey : true,
7083             /**
7084              * @event change
7085              * Fires just before the field blurs if the field value has changed.
7086              * @param {Roo.form.Field} this
7087              * @param {Mixed} newValue The new value
7088              * @param {Mixed} oldValue The original value
7089              */
7090             change : true,
7091             /**
7092              * @event invalid
7093              * Fires after the field has been marked as invalid.
7094              * @param {Roo.form.Field} this
7095              * @param {String} msg The validation message
7096              */
7097             invalid : true,
7098             /**
7099              * @event valid
7100              * Fires after the field has been validated with no errors.
7101              * @param {Roo.form.Field} this
7102              */
7103             valid : true,
7104              /**
7105              * @event keyup
7106              * Fires after the key up
7107              * @param {Roo.form.Field} this
7108              * @param {Roo.EventObject}  e The event Object
7109              */
7110             keyup : true
7111         });
7112 };
7113
7114 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7115      /**
7116      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7117       automatic validation (defaults to "keyup").
7118      */
7119     validationEvent : "keyup",
7120      /**
7121      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7122      */
7123     validateOnBlur : true,
7124     /**
7125      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7126      */
7127     validationDelay : 250,
7128      /**
7129      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7130      */
7131     focusClass : "x-form-focus",  // not needed???
7132     
7133        
7134     /**
7135      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7136      */
7137     invalidClass : "has-error",
7138     
7139     /**
7140      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7141      */
7142     selectOnFocus : false,
7143     
7144      /**
7145      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7146      */
7147     maskRe : null,
7148        /**
7149      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7150      */
7151     vtype : null,
7152     
7153       /**
7154      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7155      */
7156     disableKeyFilter : false,
7157     
7158        /**
7159      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7160      */
7161     disabled : false,
7162      /**
7163      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7164      */
7165     allowBlank : true,
7166     /**
7167      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7168      */
7169     blankText : "This field is required",
7170     
7171      /**
7172      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7173      */
7174     minLength : 0,
7175     /**
7176      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7177      */
7178     maxLength : Number.MAX_VALUE,
7179     /**
7180      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7181      */
7182     minLengthText : "The minimum length for this field is {0}",
7183     /**
7184      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7185      */
7186     maxLengthText : "The maximum length for this field is {0}",
7187   
7188     
7189     /**
7190      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7191      * If available, this function will be called only after the basic validators all return true, and will be passed the
7192      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7193      */
7194     validator : null,
7195     /**
7196      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7197      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7198      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7199      */
7200     regex : null,
7201     /**
7202      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7203      */
7204     regexText : "",
7205     
7206     
7207     
7208     fieldLabel : '',
7209     inputType : 'text',
7210     
7211     name : false,
7212     placeholder: false,
7213     before : false,
7214     after : false,
7215     size : false,
7216     // private
7217     hasFocus : false,
7218     preventMark: false,
7219     isFormField : true,
7220     value : '',
7221     labelWidth : 2,
7222     labelAlign : false,
7223     readOnly : false,
7224     align : false,
7225     formatedValue : false,
7226     
7227     parentLabelAlign : function()
7228     {
7229         var parent = this;
7230         while (parent.parent()) {
7231             parent = parent.parent();
7232             if (typeof(parent.labelAlign) !='undefined') {
7233                 return parent.labelAlign;
7234             }
7235         }
7236         return 'left';
7237         
7238     },
7239     
7240     getAutoCreate : function(){
7241         
7242         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7243         
7244         var id = Roo.id();
7245         
7246         var cfg = {};
7247         
7248         if(this.inputType != 'hidden'){
7249             cfg.cls = 'form-group' //input-group
7250         }
7251         
7252         var input =  {
7253             tag: 'input',
7254             id : id,
7255             type : this.inputType,
7256             value : this.value,
7257             cls : 'form-control',
7258             placeholder : this.placeholder || ''
7259             
7260         };
7261         
7262         if(this.align){
7263             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7264         }
7265         
7266         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7267             input.maxLength = this.maxLength;
7268         }
7269         
7270         if (this.disabled) {
7271             input.disabled=true;
7272         }
7273         
7274         if (this.readOnly) {
7275             input.readonly=true;
7276         }
7277         
7278         if (this.name) {
7279             input.name = this.name;
7280         }
7281         if (this.size) {
7282             input.cls += ' input-' + this.size;
7283         }
7284         var settings=this;
7285         ['xs','sm','md','lg'].map(function(size){
7286             if (settings[size]) {
7287                 cfg.cls += ' col-' + size + '-' + settings[size];
7288             }
7289         });
7290         
7291         var inputblock = input;
7292         
7293         if (this.before || this.after) {
7294             
7295             inputblock = {
7296                 cls : 'input-group',
7297                 cn :  [] 
7298             };
7299             if (this.before && typeof(this.before) == 'string') {
7300                 
7301                 inputblock.cn.push({
7302                     tag :'span',
7303                     cls : 'roo-input-before input-group-addon',
7304                     html : this.before
7305                 });
7306             }
7307             if (this.before && typeof(this.before) == 'object') {
7308                 this.before = Roo.factory(this.before);
7309                 Roo.log(this.before);
7310                 inputblock.cn.push({
7311                     tag :'span',
7312                     cls : 'roo-input-before input-group-' +
7313                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7314                 });
7315             }
7316             
7317             inputblock.cn.push(input);
7318             
7319             if (this.after && typeof(this.after) == 'string') {
7320                 inputblock.cn.push({
7321                     tag :'span',
7322                     cls : 'roo-input-after input-group-addon',
7323                     html : this.after
7324                 });
7325             }
7326             if (this.after && typeof(this.after) == 'object') {
7327                 this.after = Roo.factory(this.after);
7328                 Roo.log(this.after);
7329                 inputblock.cn.push({
7330                     tag :'span',
7331                     cls : 'roo-input-after input-group-' +
7332                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7333                 });
7334             }
7335         };
7336         
7337         if (align ==='left' && this.fieldLabel.length) {
7338                 Roo.log("left and has label");
7339                 cfg.cn = [
7340                     
7341                     {
7342                         tag: 'label',
7343                         'for' :  id,
7344                         cls : 'control-label col-sm-' + this.labelWidth,
7345                         html : this.fieldLabel
7346                         
7347                     },
7348                     {
7349                         cls : "col-sm-" + (12 - this.labelWidth), 
7350                         cn: [
7351                             inputblock
7352                         ]
7353                     }
7354                     
7355                 ];
7356         } else if ( this.fieldLabel.length) {
7357                 Roo.log(" label");
7358                  cfg.cn = [
7359                    
7360                     {
7361                         tag: 'label',
7362                         //cls : 'input-group-addon',
7363                         html : this.fieldLabel
7364                         
7365                     },
7366                     
7367                     inputblock
7368                     
7369                 ];
7370
7371         } else {
7372             
7373                 Roo.log(" no label && no align");
7374                 cfg.cn = [
7375                     
7376                         inputblock
7377                     
7378                 ];
7379                 
7380                 
7381         };
7382         Roo.log('input-parentType: ' + this.parentType);
7383         
7384         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7385            cfg.cls += ' navbar-form';
7386            Roo.log(cfg);
7387         }
7388         
7389         return cfg;
7390         
7391     },
7392     /**
7393      * return the real input element.
7394      */
7395     inputEl: function ()
7396     {
7397         return this.el.select('input.form-control',true).first();
7398     },
7399     
7400     tooltipEl : function()
7401     {
7402         return this.inputEl();
7403     },
7404     
7405     setDisabled : function(v)
7406     {
7407         var i  = this.inputEl().dom;
7408         if (!v) {
7409             i.removeAttribute('disabled');
7410             return;
7411             
7412         }
7413         i.setAttribute('disabled','true');
7414     },
7415     initEvents : function()
7416     {
7417           
7418         this.inputEl().on("keydown" , this.fireKey,  this);
7419         this.inputEl().on("focus", this.onFocus,  this);
7420         this.inputEl().on("blur", this.onBlur,  this);
7421         
7422         this.inputEl().relayEvent('keyup', this);
7423
7424         // reference to original value for reset
7425         this.originalValue = this.getValue();
7426         //Roo.form.TextField.superclass.initEvents.call(this);
7427         if(this.validationEvent == 'keyup'){
7428             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7429             this.inputEl().on('keyup', this.filterValidation, this);
7430         }
7431         else if(this.validationEvent !== false){
7432             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7433         }
7434         
7435         if(this.selectOnFocus){
7436             this.on("focus", this.preFocus, this);
7437             
7438         }
7439         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7440             this.inputEl().on("keypress", this.filterKeys, this);
7441         }
7442        /* if(this.grow){
7443             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7444             this.el.on("click", this.autoSize,  this);
7445         }
7446         */
7447         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7448             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7449         }
7450         
7451         if (typeof(this.before) == 'object') {
7452             this.before.render(this.el.select('.roo-input-before',true).first());
7453         }
7454         if (typeof(this.after) == 'object') {
7455             this.after.render(this.el.select('.roo-input-after',true).first());
7456         }
7457         
7458         
7459     },
7460     filterValidation : function(e){
7461         if(!e.isNavKeyPress()){
7462             this.validationTask.delay(this.validationDelay);
7463         }
7464     },
7465      /**
7466      * Validates the field value
7467      * @return {Boolean} True if the value is valid, else false
7468      */
7469     validate : function(){
7470         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7471         if(this.disabled || this.validateValue(this.getRawValue())){
7472             this.clearInvalid();
7473             return true;
7474         }
7475         return false;
7476     },
7477     
7478     
7479     /**
7480      * Validates a value according to the field's validation rules and marks the field as invalid
7481      * if the validation fails
7482      * @param {Mixed} value The value to validate
7483      * @return {Boolean} True if the value is valid, else false
7484      */
7485     validateValue : function(value){
7486         if(value.length < 1)  { // if it's blank
7487              if(this.allowBlank){
7488                 this.clearInvalid();
7489                 return true;
7490              }else{
7491                 this.markInvalid(this.blankText);
7492                 return false;
7493              }
7494         }
7495         if(value.length < this.minLength){
7496             this.markInvalid(String.format(this.minLengthText, this.minLength));
7497             return false;
7498         }
7499         if(value.length > this.maxLength){
7500             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7501             return false;
7502         }
7503         if(this.vtype){
7504             var vt = Roo.form.VTypes;
7505             if(!vt[this.vtype](value, this)){
7506                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7507                 return false;
7508             }
7509         }
7510         if(typeof this.validator == "function"){
7511             var msg = this.validator(value);
7512             if(msg !== true){
7513                 this.markInvalid(msg);
7514                 return false;
7515             }
7516         }
7517         if(this.regex && !this.regex.test(value)){
7518             this.markInvalid(this.regexText);
7519             return false;
7520         }
7521         return true;
7522     },
7523
7524     
7525     
7526      // private
7527     fireKey : function(e){
7528         //Roo.log('field ' + e.getKey());
7529         if(e.isNavKeyPress()){
7530             this.fireEvent("specialkey", this, e);
7531         }
7532     },
7533     focus : function (selectText){
7534         if(this.rendered){
7535             this.inputEl().focus();
7536             if(selectText === true){
7537                 this.inputEl().dom.select();
7538             }
7539         }
7540         return this;
7541     } ,
7542     
7543     onFocus : function(){
7544         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7545            // this.el.addClass(this.focusClass);
7546         }
7547         if(!this.hasFocus){
7548             this.hasFocus = true;
7549             this.startValue = this.getValue();
7550             this.fireEvent("focus", this);
7551         }
7552     },
7553     
7554     beforeBlur : Roo.emptyFn,
7555
7556     
7557     // private
7558     onBlur : function(){
7559         this.beforeBlur();
7560         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7561             //this.el.removeClass(this.focusClass);
7562         }
7563         this.hasFocus = false;
7564         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7565             this.validate();
7566         }
7567         var v = this.getValue();
7568         if(String(v) !== String(this.startValue)){
7569             this.fireEvent('change', this, v, this.startValue);
7570         }
7571         this.fireEvent("blur", this);
7572     },
7573     
7574     /**
7575      * Resets the current field value to the originally loaded value and clears any validation messages
7576      */
7577     reset : function(){
7578         this.setValue(this.originalValue);
7579         this.clearInvalid();
7580     },
7581      /**
7582      * Returns the name of the field
7583      * @return {Mixed} name The name field
7584      */
7585     getName: function(){
7586         return this.name;
7587     },
7588      /**
7589      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7590      * @return {Mixed} value The field value
7591      */
7592     getValue : function(){
7593         
7594         var v = this.inputEl().getValue();
7595         
7596         return v;
7597     },
7598     /**
7599      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7600      * @return {Mixed} value The field value
7601      */
7602     getRawValue : function(){
7603         var v = this.inputEl().getValue();
7604         
7605         return v;
7606     },
7607     
7608     /**
7609      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7610      * @param {Mixed} value The value to set
7611      */
7612     setRawValue : function(v){
7613         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7614     },
7615     
7616     selectText : function(start, end){
7617         var v = this.getRawValue();
7618         if(v.length > 0){
7619             start = start === undefined ? 0 : start;
7620             end = end === undefined ? v.length : end;
7621             var d = this.inputEl().dom;
7622             if(d.setSelectionRange){
7623                 d.setSelectionRange(start, end);
7624             }else if(d.createTextRange){
7625                 var range = d.createTextRange();
7626                 range.moveStart("character", start);
7627                 range.moveEnd("character", v.length-end);
7628                 range.select();
7629             }
7630         }
7631     },
7632     
7633     /**
7634      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7635      * @param {Mixed} value The value to set
7636      */
7637     setValue : function(v){
7638         this.value = v;
7639         if(this.rendered){
7640             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7641             this.validate();
7642         }
7643     },
7644     
7645     /*
7646     processValue : function(value){
7647         if(this.stripCharsRe){
7648             var newValue = value.replace(this.stripCharsRe, '');
7649             if(newValue !== value){
7650                 this.setRawValue(newValue);
7651                 return newValue;
7652             }
7653         }
7654         return value;
7655     },
7656   */
7657     preFocus : function(){
7658         
7659         if(this.selectOnFocus){
7660             this.inputEl().dom.select();
7661         }
7662     },
7663     filterKeys : function(e){
7664         var k = e.getKey();
7665         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7666             return;
7667         }
7668         var c = e.getCharCode(), cc = String.fromCharCode(c);
7669         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7670             return;
7671         }
7672         if(!this.maskRe.test(cc)){
7673             e.stopEvent();
7674         }
7675     },
7676      /**
7677      * Clear any invalid styles/messages for this field
7678      */
7679     clearInvalid : function(){
7680         
7681         if(!this.el || this.preventMark){ // not rendered
7682             return;
7683         }
7684         this.el.removeClass(this.invalidClass);
7685         /*
7686         switch(this.msgTarget){
7687             case 'qtip':
7688                 this.el.dom.qtip = '';
7689                 break;
7690             case 'title':
7691                 this.el.dom.title = '';
7692                 break;
7693             case 'under':
7694                 if(this.errorEl){
7695                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7696                 }
7697                 break;
7698             case 'side':
7699                 if(this.errorIcon){
7700                     this.errorIcon.dom.qtip = '';
7701                     this.errorIcon.hide();
7702                     this.un('resize', this.alignErrorIcon, this);
7703                 }
7704                 break;
7705             default:
7706                 var t = Roo.getDom(this.msgTarget);
7707                 t.innerHTML = '';
7708                 t.style.display = 'none';
7709                 break;
7710         }
7711         */
7712         this.fireEvent('valid', this);
7713     },
7714      /**
7715      * Mark this field as invalid
7716      * @param {String} msg The validation message
7717      */
7718     markInvalid : function(msg){
7719         if(!this.el  || this.preventMark){ // not rendered
7720             return;
7721         }
7722         this.el.addClass(this.invalidClass);
7723         /*
7724         msg = msg || this.invalidText;
7725         switch(this.msgTarget){
7726             case 'qtip':
7727                 this.el.dom.qtip = msg;
7728                 this.el.dom.qclass = 'x-form-invalid-tip';
7729                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7730                     Roo.QuickTips.enable();
7731                 }
7732                 break;
7733             case 'title':
7734                 this.el.dom.title = msg;
7735                 break;
7736             case 'under':
7737                 if(!this.errorEl){
7738                     var elp = this.el.findParent('.x-form-element', 5, true);
7739                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7740                     this.errorEl.setWidth(elp.getWidth(true)-20);
7741                 }
7742                 this.errorEl.update(msg);
7743                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7744                 break;
7745             case 'side':
7746                 if(!this.errorIcon){
7747                     var elp = this.el.findParent('.x-form-element', 5, true);
7748                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7749                 }
7750                 this.alignErrorIcon();
7751                 this.errorIcon.dom.qtip = msg;
7752                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7753                 this.errorIcon.show();
7754                 this.on('resize', this.alignErrorIcon, this);
7755                 break;
7756             default:
7757                 var t = Roo.getDom(this.msgTarget);
7758                 t.innerHTML = msg;
7759                 t.style.display = this.msgDisplay;
7760                 break;
7761         }
7762         */
7763         this.fireEvent('invalid', this, msg);
7764     },
7765     // private
7766     SafariOnKeyDown : function(event)
7767     {
7768         // this is a workaround for a password hang bug on chrome/ webkit.
7769         
7770         var isSelectAll = false;
7771         
7772         if(this.inputEl().dom.selectionEnd > 0){
7773             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7774         }
7775         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7776             event.preventDefault();
7777             this.setValue('');
7778             return;
7779         }
7780         
7781         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
7782             
7783             event.preventDefault();
7784             // this is very hacky as keydown always get's upper case.
7785             //
7786             var cc = String.fromCharCode(event.getCharCode());
7787             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7788             
7789         }
7790     },
7791     adjustWidth : function(tag, w){
7792         tag = tag.toLowerCase();
7793         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7794             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7795                 if(tag == 'input'){
7796                     return w + 2;
7797                 }
7798                 if(tag == 'textarea'){
7799                     return w-2;
7800                 }
7801             }else if(Roo.isOpera){
7802                 if(tag == 'input'){
7803                     return w + 2;
7804                 }
7805                 if(tag == 'textarea'){
7806                     return w-2;
7807                 }
7808             }
7809         }
7810         return w;
7811     }
7812     
7813 });
7814
7815  
7816 /*
7817  * - LGPL
7818  *
7819  * Input
7820  * 
7821  */
7822
7823 /**
7824  * @class Roo.bootstrap.TextArea
7825  * @extends Roo.bootstrap.Input
7826  * Bootstrap TextArea class
7827  * @cfg {Number} cols Specifies the visible width of a text area
7828  * @cfg {Number} rows Specifies the visible number of lines in a text area
7829  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7830  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7831  * @cfg {string} html text
7832  * 
7833  * @constructor
7834  * Create a new TextArea
7835  * @param {Object} config The config object
7836  */
7837
7838 Roo.bootstrap.TextArea = function(config){
7839     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7840    
7841 };
7842
7843 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7844      
7845     cols : false,
7846     rows : 5,
7847     readOnly : false,
7848     warp : 'soft',
7849     resize : false,
7850     value: false,
7851     html: false,
7852     
7853     getAutoCreate : function(){
7854         
7855         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7856         
7857         var id = Roo.id();
7858         
7859         var cfg = {};
7860         
7861         var input =  {
7862             tag: 'textarea',
7863             id : id,
7864             warp : this.warp,
7865             rows : this.rows,
7866             value : this.value || '',
7867             html: this.html || '',
7868             cls : 'form-control',
7869             placeholder : this.placeholder || '' 
7870             
7871         };
7872         
7873         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7874             input.maxLength = this.maxLength;
7875         }
7876         
7877         if(this.resize){
7878             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7879         }
7880         
7881         if(this.cols){
7882             input.cols = this.cols;
7883         }
7884         
7885         if (this.readOnly) {
7886             input.readonly = true;
7887         }
7888         
7889         if (this.name) {
7890             input.name = this.name;
7891         }
7892         
7893         if (this.size) {
7894             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7895         }
7896         
7897         var settings=this;
7898         ['xs','sm','md','lg'].map(function(size){
7899             if (settings[size]) {
7900                 cfg.cls += ' col-' + size + '-' + settings[size];
7901             }
7902         });
7903         
7904         var inputblock = input;
7905         
7906         if (this.before || this.after) {
7907             
7908             inputblock = {
7909                 cls : 'input-group',
7910                 cn :  [] 
7911             };
7912             if (this.before) {
7913                 inputblock.cn.push({
7914                     tag :'span',
7915                     cls : 'input-group-addon',
7916                     html : this.before
7917                 });
7918             }
7919             inputblock.cn.push(input);
7920             if (this.after) {
7921                 inputblock.cn.push({
7922                     tag :'span',
7923                     cls : 'input-group-addon',
7924                     html : this.after
7925                 });
7926             }
7927             
7928         }
7929         
7930         if (align ==='left' && this.fieldLabel.length) {
7931                 Roo.log("left and has label");
7932                 cfg.cn = [
7933                     
7934                     {
7935                         tag: 'label',
7936                         'for' :  id,
7937                         cls : 'control-label col-sm-' + this.labelWidth,
7938                         html : this.fieldLabel
7939                         
7940                     },
7941                     {
7942                         cls : "col-sm-" + (12 - this.labelWidth), 
7943                         cn: [
7944                             inputblock
7945                         ]
7946                     }
7947                     
7948                 ];
7949         } else if ( this.fieldLabel.length) {
7950                 Roo.log(" label");
7951                  cfg.cn = [
7952                    
7953                     {
7954                         tag: 'label',
7955                         //cls : 'input-group-addon',
7956                         html : this.fieldLabel
7957                         
7958                     },
7959                     
7960                     inputblock
7961                     
7962                 ];
7963
7964         } else {
7965             
7966                    Roo.log(" no label && no align");
7967                 cfg.cn = [
7968                     
7969                         inputblock
7970                     
7971                 ];
7972                 
7973                 
7974         }
7975         
7976         if (this.disabled) {
7977             input.disabled=true;
7978         }
7979         
7980         return cfg;
7981         
7982     },
7983     /**
7984      * return the real textarea element.
7985      */
7986     inputEl: function ()
7987     {
7988         return this.el.select('textarea.form-control',true).first();
7989     }
7990 });
7991
7992  
7993 /*
7994  * - LGPL
7995  *
7996  * trigger field - base class for combo..
7997  * 
7998  */
7999  
8000 /**
8001  * @class Roo.bootstrap.TriggerField
8002  * @extends Roo.bootstrap.Input
8003  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8004  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8005  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8006  * for which you can provide a custom implementation.  For example:
8007  * <pre><code>
8008 var trigger = new Roo.bootstrap.TriggerField();
8009 trigger.onTriggerClick = myTriggerFn;
8010 trigger.applyTo('my-field');
8011 </code></pre>
8012  *
8013  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8014  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8015  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8016  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8017  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8018
8019  * @constructor
8020  * Create a new TriggerField.
8021  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8022  * to the base TextField)
8023  */
8024 Roo.bootstrap.TriggerField = function(config){
8025     this.mimicing = false;
8026     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8027 };
8028
8029 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8030     /**
8031      * @cfg {String} triggerClass A CSS class to apply to the trigger
8032      */
8033      /**
8034      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8035      */
8036     hideTrigger:false,
8037
8038     /** @cfg {Boolean} grow @hide */
8039     /** @cfg {Number} growMin @hide */
8040     /** @cfg {Number} growMax @hide */
8041
8042     /**
8043      * @hide 
8044      * @method
8045      */
8046     autoSize: Roo.emptyFn,
8047     // private
8048     monitorTab : true,
8049     // private
8050     deferHeight : true,
8051
8052     
8053     actionMode : 'wrap',
8054     
8055     caret : false,
8056     
8057     
8058     getAutoCreate : function(){
8059        
8060         var align = this.labelAlign || this.parentLabelAlign();
8061         
8062         var id = Roo.id();
8063         
8064         var cfg = {
8065             cls: 'form-group' //input-group
8066         };
8067         
8068         
8069         var input =  {
8070             tag: 'input',
8071             id : id,
8072             type : this.inputType,
8073             cls : 'form-control',
8074             autocomplete: 'false',
8075             placeholder : this.placeholder || '' 
8076             
8077         };
8078         if (this.name) {
8079             input.name = this.name;
8080         }
8081         if (this.size) {
8082             input.cls += ' input-' + this.size;
8083         }
8084         
8085         if (this.disabled) {
8086             input.disabled=true;
8087         }
8088         
8089         var inputblock = input;
8090         
8091         if (this.before || this.after) {
8092             
8093             inputblock = {
8094                 cls : 'input-group',
8095                 cn :  [] 
8096             };
8097             if (this.before) {
8098                 inputblock.cn.push({
8099                     tag :'span',
8100                     cls : 'input-group-addon',
8101                     html : this.before
8102                 });
8103             }
8104             inputblock.cn.push(input);
8105             if (this.after) {
8106                 inputblock.cn.push({
8107                     tag :'span',
8108                     cls : 'input-group-addon',
8109                     html : this.after
8110                 });
8111             }
8112             
8113         };
8114         
8115         var box = {
8116             tag: 'div',
8117             cn: [
8118                 {
8119                     tag: 'input',
8120                     type : 'hidden',
8121                     cls: 'form-hidden-field'
8122                 },
8123                 inputblock
8124             ]
8125             
8126         };
8127         
8128         if(this.multiple){
8129             Roo.log('multiple');
8130             
8131             box = {
8132                 tag: 'div',
8133                 cn: [
8134                     {
8135                         tag: 'input',
8136                         type : 'hidden',
8137                         cls: 'form-hidden-field'
8138                     },
8139                     {
8140                         tag: 'ul',
8141                         cls: 'select2-choices',
8142                         cn:[
8143                             {
8144                                 tag: 'li',
8145                                 cls: 'select2-search-field',
8146                                 cn: [
8147
8148                                     inputblock
8149                                 ]
8150                             }
8151                         ]
8152                     }
8153                 ]
8154             }
8155         };
8156         
8157         var combobox = {
8158             cls: 'select2-container input-group',
8159             cn: [
8160                 box
8161 //                {
8162 //                    tag: 'ul',
8163 //                    cls: 'typeahead typeahead-long dropdown-menu',
8164 //                    style: 'display:none'
8165 //                }
8166             ]
8167         };
8168         
8169         if(!this.multiple && this.showToggleBtn){
8170             
8171             var caret = {
8172                         tag: 'span',
8173                         cls: 'caret'
8174              };
8175             if (this.caret != false) {
8176                 caret = {
8177                      tag: 'i',
8178                      cls: 'fa fa-' + this.caret
8179                 };
8180                 
8181             }
8182             
8183             combobox.cn.push({
8184                 tag :'span',
8185                 cls : 'input-group-addon btn dropdown-toggle',
8186                 cn : [
8187                     caret,
8188                     {
8189                         tag: 'span',
8190                         cls: 'combobox-clear',
8191                         cn  : [
8192                             {
8193                                 tag : 'i',
8194                                 cls: 'icon-remove'
8195                             }
8196                         ]
8197                     }
8198                 ]
8199
8200             })
8201         }
8202         
8203         if(this.multiple){
8204             combobox.cls += ' select2-container-multi';
8205         }
8206         
8207         if (align ==='left' && this.fieldLabel.length) {
8208             
8209                 Roo.log("left and has label");
8210                 cfg.cn = [
8211                     
8212                     {
8213                         tag: 'label',
8214                         'for' :  id,
8215                         cls : 'control-label col-sm-' + this.labelWidth,
8216                         html : this.fieldLabel
8217                         
8218                     },
8219                     {
8220                         cls : "col-sm-" + (12 - this.labelWidth), 
8221                         cn: [
8222                             combobox
8223                         ]
8224                     }
8225                     
8226                 ];
8227         } else if ( this.fieldLabel.length) {
8228                 Roo.log(" label");
8229                  cfg.cn = [
8230                    
8231                     {
8232                         tag: 'label',
8233                         //cls : 'input-group-addon',
8234                         html : this.fieldLabel
8235                         
8236                     },
8237                     
8238                     combobox
8239                     
8240                 ];
8241
8242         } else {
8243             
8244                 Roo.log(" no label && no align");
8245                 cfg = combobox
8246                      
8247                 
8248         }
8249          
8250         var settings=this;
8251         ['xs','sm','md','lg'].map(function(size){
8252             if (settings[size]) {
8253                 cfg.cls += ' col-' + size + '-' + settings[size];
8254             }
8255         });
8256         
8257         return cfg;
8258         
8259     },
8260     
8261     
8262     
8263     // private
8264     onResize : function(w, h){
8265 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8266 //        if(typeof w == 'number'){
8267 //            var x = w - this.trigger.getWidth();
8268 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8269 //            this.trigger.setStyle('left', x+'px');
8270 //        }
8271     },
8272
8273     // private
8274     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8275
8276     // private
8277     getResizeEl : function(){
8278         return this.inputEl();
8279     },
8280
8281     // private
8282     getPositionEl : function(){
8283         return this.inputEl();
8284     },
8285
8286     // private
8287     alignErrorIcon : function(){
8288         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8289     },
8290
8291     // private
8292     initEvents : function(){
8293         
8294         this.createList();
8295         
8296         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8297         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8298         if(!this.multiple && this.showToggleBtn){
8299             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8300             if(this.hideTrigger){
8301                 this.trigger.setDisplayed(false);
8302             }
8303             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8304         }
8305         
8306         if(this.multiple){
8307             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8308         }
8309         
8310         //this.trigger.addClassOnOver('x-form-trigger-over');
8311         //this.trigger.addClassOnClick('x-form-trigger-click');
8312         
8313         //if(!this.width){
8314         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8315         //}
8316     },
8317     
8318     createList : function()
8319     {
8320         this.list = Roo.get(document.body).createChild({
8321             tag: 'ul',
8322             cls: 'typeahead typeahead-long dropdown-menu',
8323             style: 'display:none'
8324         });
8325         
8326         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8327         
8328     },
8329
8330     // private
8331     initTrigger : function(){
8332        
8333     },
8334
8335     // private
8336     onDestroy : function(){
8337         if(this.trigger){
8338             this.trigger.removeAllListeners();
8339           //  this.trigger.remove();
8340         }
8341         //if(this.wrap){
8342         //    this.wrap.remove();
8343         //}
8344         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8345     },
8346
8347     // private
8348     onFocus : function(){
8349         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8350         /*
8351         if(!this.mimicing){
8352             this.wrap.addClass('x-trigger-wrap-focus');
8353             this.mimicing = true;
8354             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8355             if(this.monitorTab){
8356                 this.el.on("keydown", this.checkTab, this);
8357             }
8358         }
8359         */
8360     },
8361
8362     // private
8363     checkTab : function(e){
8364         if(e.getKey() == e.TAB){
8365             this.triggerBlur();
8366         }
8367     },
8368
8369     // private
8370     onBlur : function(){
8371         // do nothing
8372     },
8373
8374     // private
8375     mimicBlur : function(e, t){
8376         /*
8377         if(!this.wrap.contains(t) && this.validateBlur()){
8378             this.triggerBlur();
8379         }
8380         */
8381     },
8382
8383     // private
8384     triggerBlur : function(){
8385         this.mimicing = false;
8386         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8387         if(this.monitorTab){
8388             this.el.un("keydown", this.checkTab, this);
8389         }
8390         //this.wrap.removeClass('x-trigger-wrap-focus');
8391         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8392     },
8393
8394     // private
8395     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8396     validateBlur : function(e, t){
8397         return true;
8398     },
8399
8400     // private
8401     onDisable : function(){
8402         this.inputEl().dom.disabled = true;
8403         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8404         //if(this.wrap){
8405         //    this.wrap.addClass('x-item-disabled');
8406         //}
8407     },
8408
8409     // private
8410     onEnable : function(){
8411         this.inputEl().dom.disabled = false;
8412         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8413         //if(this.wrap){
8414         //    this.el.removeClass('x-item-disabled');
8415         //}
8416     },
8417
8418     // private
8419     onShow : function(){
8420         var ae = this.getActionEl();
8421         
8422         if(ae){
8423             ae.dom.style.display = '';
8424             ae.dom.style.visibility = 'visible';
8425         }
8426     },
8427
8428     // private
8429     
8430     onHide : function(){
8431         var ae = this.getActionEl();
8432         ae.dom.style.display = 'none';
8433     },
8434
8435     /**
8436      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8437      * by an implementing function.
8438      * @method
8439      * @param {EventObject} e
8440      */
8441     onTriggerClick : Roo.emptyFn
8442 });
8443  /*
8444  * Based on:
8445  * Ext JS Library 1.1.1
8446  * Copyright(c) 2006-2007, Ext JS, LLC.
8447  *
8448  * Originally Released Under LGPL - original licence link has changed is not relivant.
8449  *
8450  * Fork - LGPL
8451  * <script type="text/javascript">
8452  */
8453
8454
8455 /**
8456  * @class Roo.data.SortTypes
8457  * @singleton
8458  * Defines the default sorting (casting?) comparison functions used when sorting data.
8459  */
8460 Roo.data.SortTypes = {
8461     /**
8462      * Default sort that does nothing
8463      * @param {Mixed} s The value being converted
8464      * @return {Mixed} The comparison value
8465      */
8466     none : function(s){
8467         return s;
8468     },
8469     
8470     /**
8471      * The regular expression used to strip tags
8472      * @type {RegExp}
8473      * @property
8474      */
8475     stripTagsRE : /<\/?[^>]+>/gi,
8476     
8477     /**
8478      * Strips all HTML tags to sort on text only
8479      * @param {Mixed} s The value being converted
8480      * @return {String} The comparison value
8481      */
8482     asText : function(s){
8483         return String(s).replace(this.stripTagsRE, "");
8484     },
8485     
8486     /**
8487      * Strips all HTML tags to sort on text only - Case insensitive
8488      * @param {Mixed} s The value being converted
8489      * @return {String} The comparison value
8490      */
8491     asUCText : function(s){
8492         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8493     },
8494     
8495     /**
8496      * Case insensitive string
8497      * @param {Mixed} s The value being converted
8498      * @return {String} The comparison value
8499      */
8500     asUCString : function(s) {
8501         return String(s).toUpperCase();
8502     },
8503     
8504     /**
8505      * Date sorting
8506      * @param {Mixed} s The value being converted
8507      * @return {Number} The comparison value
8508      */
8509     asDate : function(s) {
8510         if(!s){
8511             return 0;
8512         }
8513         if(s instanceof Date){
8514             return s.getTime();
8515         }
8516         return Date.parse(String(s));
8517     },
8518     
8519     /**
8520      * Float sorting
8521      * @param {Mixed} s The value being converted
8522      * @return {Float} The comparison value
8523      */
8524     asFloat : function(s) {
8525         var val = parseFloat(String(s).replace(/,/g, ""));
8526         if(isNaN(val)) val = 0;
8527         return val;
8528     },
8529     
8530     /**
8531      * Integer sorting
8532      * @param {Mixed} s The value being converted
8533      * @return {Number} The comparison value
8534      */
8535     asInt : function(s) {
8536         var val = parseInt(String(s).replace(/,/g, ""));
8537         if(isNaN(val)) val = 0;
8538         return val;
8539     }
8540 };/*
8541  * Based on:
8542  * Ext JS Library 1.1.1
8543  * Copyright(c) 2006-2007, Ext JS, LLC.
8544  *
8545  * Originally Released Under LGPL - original licence link has changed is not relivant.
8546  *
8547  * Fork - LGPL
8548  * <script type="text/javascript">
8549  */
8550
8551 /**
8552 * @class Roo.data.Record
8553  * Instances of this class encapsulate both record <em>definition</em> information, and record
8554  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8555  * to access Records cached in an {@link Roo.data.Store} object.<br>
8556  * <p>
8557  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8558  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8559  * objects.<br>
8560  * <p>
8561  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8562  * @constructor
8563  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8564  * {@link #create}. The parameters are the same.
8565  * @param {Array} data An associative Array of data values keyed by the field name.
8566  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8567  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8568  * not specified an integer id is generated.
8569  */
8570 Roo.data.Record = function(data, id){
8571     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8572     this.data = data;
8573 };
8574
8575 /**
8576  * Generate a constructor for a specific record layout.
8577  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8578  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8579  * Each field definition object may contain the following properties: <ul>
8580  * <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,
8581  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8582  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8583  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8584  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8585  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8586  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8587  * this may be omitted.</p></li>
8588  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8589  * <ul><li>auto (Default, implies no conversion)</li>
8590  * <li>string</li>
8591  * <li>int</li>
8592  * <li>float</li>
8593  * <li>boolean</li>
8594  * <li>date</li></ul></p></li>
8595  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8596  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8597  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8598  * by the Reader into an object that will be stored in the Record. It is passed the
8599  * following parameters:<ul>
8600  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8601  * </ul></p></li>
8602  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8603  * </ul>
8604  * <br>usage:<br><pre><code>
8605 var TopicRecord = Roo.data.Record.create(
8606     {name: 'title', mapping: 'topic_title'},
8607     {name: 'author', mapping: 'username'},
8608     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8609     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8610     {name: 'lastPoster', mapping: 'user2'},
8611     {name: 'excerpt', mapping: 'post_text'}
8612 );
8613
8614 var myNewRecord = new TopicRecord({
8615     title: 'Do my job please',
8616     author: 'noobie',
8617     totalPosts: 1,
8618     lastPost: new Date(),
8619     lastPoster: 'Animal',
8620     excerpt: 'No way dude!'
8621 });
8622 myStore.add(myNewRecord);
8623 </code></pre>
8624  * @method create
8625  * @static
8626  */
8627 Roo.data.Record.create = function(o){
8628     var f = function(){
8629         f.superclass.constructor.apply(this, arguments);
8630     };
8631     Roo.extend(f, Roo.data.Record);
8632     var p = f.prototype;
8633     p.fields = new Roo.util.MixedCollection(false, function(field){
8634         return field.name;
8635     });
8636     for(var i = 0, len = o.length; i < len; i++){
8637         p.fields.add(new Roo.data.Field(o[i]));
8638     }
8639     f.getField = function(name){
8640         return p.fields.get(name);  
8641     };
8642     return f;
8643 };
8644
8645 Roo.data.Record.AUTO_ID = 1000;
8646 Roo.data.Record.EDIT = 'edit';
8647 Roo.data.Record.REJECT = 'reject';
8648 Roo.data.Record.COMMIT = 'commit';
8649
8650 Roo.data.Record.prototype = {
8651     /**
8652      * Readonly flag - true if this record has been modified.
8653      * @type Boolean
8654      */
8655     dirty : false,
8656     editing : false,
8657     error: null,
8658     modified: null,
8659
8660     // private
8661     join : function(store){
8662         this.store = store;
8663     },
8664
8665     /**
8666      * Set the named field to the specified value.
8667      * @param {String} name The name of the field to set.
8668      * @param {Object} value The value to set the field to.
8669      */
8670     set : function(name, value){
8671         if(this.data[name] == value){
8672             return;
8673         }
8674         this.dirty = true;
8675         if(!this.modified){
8676             this.modified = {};
8677         }
8678         if(typeof this.modified[name] == 'undefined'){
8679             this.modified[name] = this.data[name];
8680         }
8681         this.data[name] = value;
8682         if(!this.editing && this.store){
8683             this.store.afterEdit(this);
8684         }       
8685     },
8686
8687     /**
8688      * Get the value of the named field.
8689      * @param {String} name The name of the field to get the value of.
8690      * @return {Object} The value of the field.
8691      */
8692     get : function(name){
8693         return this.data[name]; 
8694     },
8695
8696     // private
8697     beginEdit : function(){
8698         this.editing = true;
8699         this.modified = {}; 
8700     },
8701
8702     // private
8703     cancelEdit : function(){
8704         this.editing = false;
8705         delete this.modified;
8706     },
8707
8708     // private
8709     endEdit : function(){
8710         this.editing = false;
8711         if(this.dirty && this.store){
8712             this.store.afterEdit(this);
8713         }
8714     },
8715
8716     /**
8717      * Usually called by the {@link Roo.data.Store} which owns the Record.
8718      * Rejects all changes made to the Record since either creation, or the last commit operation.
8719      * Modified fields are reverted to their original values.
8720      * <p>
8721      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8722      * of reject operations.
8723      */
8724     reject : function(){
8725         var m = this.modified;
8726         for(var n in m){
8727             if(typeof m[n] != "function"){
8728                 this.data[n] = m[n];
8729             }
8730         }
8731         this.dirty = false;
8732         delete this.modified;
8733         this.editing = false;
8734         if(this.store){
8735             this.store.afterReject(this);
8736         }
8737     },
8738
8739     /**
8740      * Usually called by the {@link Roo.data.Store} which owns the Record.
8741      * Commits all changes made to the Record since either creation, or the last commit operation.
8742      * <p>
8743      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8744      * of commit operations.
8745      */
8746     commit : function(){
8747         this.dirty = false;
8748         delete this.modified;
8749         this.editing = false;
8750         if(this.store){
8751             this.store.afterCommit(this);
8752         }
8753     },
8754
8755     // private
8756     hasError : function(){
8757         return this.error != null;
8758     },
8759
8760     // private
8761     clearError : function(){
8762         this.error = null;
8763     },
8764
8765     /**
8766      * Creates a copy of this record.
8767      * @param {String} id (optional) A new record id if you don't want to use this record's id
8768      * @return {Record}
8769      */
8770     copy : function(newId) {
8771         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8772     }
8773 };/*
8774  * Based on:
8775  * Ext JS Library 1.1.1
8776  * Copyright(c) 2006-2007, Ext JS, LLC.
8777  *
8778  * Originally Released Under LGPL - original licence link has changed is not relivant.
8779  *
8780  * Fork - LGPL
8781  * <script type="text/javascript">
8782  */
8783
8784
8785
8786 /**
8787  * @class Roo.data.Store
8788  * @extends Roo.util.Observable
8789  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8790  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8791  * <p>
8792  * 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
8793  * has no knowledge of the format of the data returned by the Proxy.<br>
8794  * <p>
8795  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8796  * instances from the data object. These records are cached and made available through accessor functions.
8797  * @constructor
8798  * Creates a new Store.
8799  * @param {Object} config A config object containing the objects needed for the Store to access data,
8800  * and read the data into Records.
8801  */
8802 Roo.data.Store = function(config){
8803     this.data = new Roo.util.MixedCollection(false);
8804     this.data.getKey = function(o){
8805         return o.id;
8806     };
8807     this.baseParams = {};
8808     // private
8809     this.paramNames = {
8810         "start" : "start",
8811         "limit" : "limit",
8812         "sort" : "sort",
8813         "dir" : "dir",
8814         "multisort" : "_multisort"
8815     };
8816
8817     if(config && config.data){
8818         this.inlineData = config.data;
8819         delete config.data;
8820     }
8821
8822     Roo.apply(this, config);
8823     
8824     if(this.reader){ // reader passed
8825         this.reader = Roo.factory(this.reader, Roo.data);
8826         this.reader.xmodule = this.xmodule || false;
8827         if(!this.recordType){
8828             this.recordType = this.reader.recordType;
8829         }
8830         if(this.reader.onMetaChange){
8831             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8832         }
8833     }
8834
8835     if(this.recordType){
8836         this.fields = this.recordType.prototype.fields;
8837     }
8838     this.modified = [];
8839
8840     this.addEvents({
8841         /**
8842          * @event datachanged
8843          * Fires when the data cache has changed, and a widget which is using this Store
8844          * as a Record cache should refresh its view.
8845          * @param {Store} this
8846          */
8847         datachanged : true,
8848         /**
8849          * @event metachange
8850          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8851          * @param {Store} this
8852          * @param {Object} meta The JSON metadata
8853          */
8854         metachange : true,
8855         /**
8856          * @event add
8857          * Fires when Records have been added to the Store
8858          * @param {Store} this
8859          * @param {Roo.data.Record[]} records The array of Records added
8860          * @param {Number} index The index at which the record(s) were added
8861          */
8862         add : true,
8863         /**
8864          * @event remove
8865          * Fires when a Record has been removed from the Store
8866          * @param {Store} this
8867          * @param {Roo.data.Record} record The Record that was removed
8868          * @param {Number} index The index at which the record was removed
8869          */
8870         remove : true,
8871         /**
8872          * @event update
8873          * Fires when a Record has been updated
8874          * @param {Store} this
8875          * @param {Roo.data.Record} record The Record that was updated
8876          * @param {String} operation The update operation being performed.  Value may be one of:
8877          * <pre><code>
8878  Roo.data.Record.EDIT
8879  Roo.data.Record.REJECT
8880  Roo.data.Record.COMMIT
8881          * </code></pre>
8882          */
8883         update : true,
8884         /**
8885          * @event clear
8886          * Fires when the data cache has been cleared.
8887          * @param {Store} this
8888          */
8889         clear : true,
8890         /**
8891          * @event beforeload
8892          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8893          * the load action will be canceled.
8894          * @param {Store} this
8895          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8896          */
8897         beforeload : true,
8898         /**
8899          * @event beforeloadadd
8900          * Fires after a new set of Records has been loaded.
8901          * @param {Store} this
8902          * @param {Roo.data.Record[]} records The Records that were loaded
8903          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8904          */
8905         beforeloadadd : true,
8906         /**
8907          * @event load
8908          * Fires after a new set of Records has been loaded, before they are added to the store.
8909          * @param {Store} this
8910          * @param {Roo.data.Record[]} records The Records that were loaded
8911          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8912          * @params {Object} return from reader
8913          */
8914         load : true,
8915         /**
8916          * @event loadexception
8917          * Fires if an exception occurs in the Proxy during loading.
8918          * Called with the signature of the Proxy's "loadexception" event.
8919          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8920          * 
8921          * @param {Proxy} 
8922          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8923          * @param {Object} load options 
8924          * @param {Object} jsonData from your request (normally this contains the Exception)
8925          */
8926         loadexception : true
8927     });
8928     
8929     if(this.proxy){
8930         this.proxy = Roo.factory(this.proxy, Roo.data);
8931         this.proxy.xmodule = this.xmodule || false;
8932         this.relayEvents(this.proxy,  ["loadexception"]);
8933     }
8934     this.sortToggle = {};
8935     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8936
8937     Roo.data.Store.superclass.constructor.call(this);
8938
8939     if(this.inlineData){
8940         this.loadData(this.inlineData);
8941         delete this.inlineData;
8942     }
8943 };
8944
8945 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8946      /**
8947     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8948     * without a remote query - used by combo/forms at present.
8949     */
8950     
8951     /**
8952     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8953     */
8954     /**
8955     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8956     */
8957     /**
8958     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8959     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8960     */
8961     /**
8962     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8963     * on any HTTP request
8964     */
8965     /**
8966     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8967     */
8968     /**
8969     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8970     */
8971     multiSort: false,
8972     /**
8973     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8974     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8975     */
8976     remoteSort : false,
8977
8978     /**
8979     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8980      * loaded or when a record is removed. (defaults to false).
8981     */
8982     pruneModifiedRecords : false,
8983
8984     // private
8985     lastOptions : null,
8986
8987     /**
8988      * Add Records to the Store and fires the add event.
8989      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8990      */
8991     add : function(records){
8992         records = [].concat(records);
8993         for(var i = 0, len = records.length; i < len; i++){
8994             records[i].join(this);
8995         }
8996         var index = this.data.length;
8997         this.data.addAll(records);
8998         this.fireEvent("add", this, records, index);
8999     },
9000
9001     /**
9002      * Remove a Record from the Store and fires the remove event.
9003      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9004      */
9005     remove : function(record){
9006         var index = this.data.indexOf(record);
9007         this.data.removeAt(index);
9008         if(this.pruneModifiedRecords){
9009             this.modified.remove(record);
9010         }
9011         this.fireEvent("remove", this, record, index);
9012     },
9013
9014     /**
9015      * Remove all Records from the Store and fires the clear event.
9016      */
9017     removeAll : function(){
9018         this.data.clear();
9019         if(this.pruneModifiedRecords){
9020             this.modified = [];
9021         }
9022         this.fireEvent("clear", this);
9023     },
9024
9025     /**
9026      * Inserts Records to the Store at the given index and fires the add event.
9027      * @param {Number} index The start index at which to insert the passed Records.
9028      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9029      */
9030     insert : function(index, records){
9031         records = [].concat(records);
9032         for(var i = 0, len = records.length; i < len; i++){
9033             this.data.insert(index, records[i]);
9034             records[i].join(this);
9035         }
9036         this.fireEvent("add", this, records, index);
9037     },
9038
9039     /**
9040      * Get the index within the cache of the passed Record.
9041      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9042      * @return {Number} The index of the passed Record. Returns -1 if not found.
9043      */
9044     indexOf : function(record){
9045         return this.data.indexOf(record);
9046     },
9047
9048     /**
9049      * Get the index within the cache of the Record with the passed id.
9050      * @param {String} id The id of the Record to find.
9051      * @return {Number} The index of the Record. Returns -1 if not found.
9052      */
9053     indexOfId : function(id){
9054         return this.data.indexOfKey(id);
9055     },
9056
9057     /**
9058      * Get the Record with the specified id.
9059      * @param {String} id The id of the Record to find.
9060      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9061      */
9062     getById : function(id){
9063         return this.data.key(id);
9064     },
9065
9066     /**
9067      * Get the Record at the specified index.
9068      * @param {Number} index The index of the Record to find.
9069      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9070      */
9071     getAt : function(index){
9072         return this.data.itemAt(index);
9073     },
9074
9075     /**
9076      * Returns a range of Records between specified indices.
9077      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9078      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9079      * @return {Roo.data.Record[]} An array of Records
9080      */
9081     getRange : function(start, end){
9082         return this.data.getRange(start, end);
9083     },
9084
9085     // private
9086     storeOptions : function(o){
9087         o = Roo.apply({}, o);
9088         delete o.callback;
9089         delete o.scope;
9090         this.lastOptions = o;
9091     },
9092
9093     /**
9094      * Loads the Record cache from the configured Proxy using the configured Reader.
9095      * <p>
9096      * If using remote paging, then the first load call must specify the <em>start</em>
9097      * and <em>limit</em> properties in the options.params property to establish the initial
9098      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9099      * <p>
9100      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9101      * and this call will return before the new data has been loaded. Perform any post-processing
9102      * in a callback function, or in a "load" event handler.</strong>
9103      * <p>
9104      * @param {Object} options An object containing properties which control loading options:<ul>
9105      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9106      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9107      * passed the following arguments:<ul>
9108      * <li>r : Roo.data.Record[]</li>
9109      * <li>options: Options object from the load call</li>
9110      * <li>success: Boolean success indicator</li></ul></li>
9111      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9112      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9113      * </ul>
9114      */
9115     load : function(options){
9116         options = options || {};
9117         if(this.fireEvent("beforeload", this, options) !== false){
9118             this.storeOptions(options);
9119             var p = Roo.apply(options.params || {}, this.baseParams);
9120             // if meta was not loaded from remote source.. try requesting it.
9121             if (!this.reader.metaFromRemote) {
9122                 p._requestMeta = 1;
9123             }
9124             if(this.sortInfo && this.remoteSort){
9125                 var pn = this.paramNames;
9126                 p[pn["sort"]] = this.sortInfo.field;
9127                 p[pn["dir"]] = this.sortInfo.direction;
9128             }
9129             if (this.multiSort) {
9130                 var pn = this.paramNames;
9131                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9132             }
9133             
9134             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9135         }
9136     },
9137
9138     /**
9139      * Reloads the Record cache from the configured Proxy using the configured Reader and
9140      * the options from the last load operation performed.
9141      * @param {Object} options (optional) An object containing properties which may override the options
9142      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9143      * the most recently used options are reused).
9144      */
9145     reload : function(options){
9146         this.load(Roo.applyIf(options||{}, this.lastOptions));
9147     },
9148
9149     // private
9150     // Called as a callback by the Reader during a load operation.
9151     loadRecords : function(o, options, success){
9152         if(!o || success === false){
9153             if(success !== false){
9154                 this.fireEvent("load", this, [], options, o);
9155             }
9156             if(options.callback){
9157                 options.callback.call(options.scope || this, [], options, false);
9158             }
9159             return;
9160         }
9161         // if data returned failure - throw an exception.
9162         if (o.success === false) {
9163             // show a message if no listener is registered.
9164             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9165                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9166             }
9167             // loadmask wil be hooked into this..
9168             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9169             return;
9170         }
9171         var r = o.records, t = o.totalRecords || r.length;
9172         
9173         this.fireEvent("beforeloadadd", this, r, options, o);
9174         
9175         if(!options || options.add !== true){
9176             if(this.pruneModifiedRecords){
9177                 this.modified = [];
9178             }
9179             for(var i = 0, len = r.length; i < len; i++){
9180                 r[i].join(this);
9181             }
9182             if(this.snapshot){
9183                 this.data = this.snapshot;
9184                 delete this.snapshot;
9185             }
9186             this.data.clear();
9187             this.data.addAll(r);
9188             this.totalLength = t;
9189             this.applySort();
9190             this.fireEvent("datachanged", this);
9191         }else{
9192             this.totalLength = Math.max(t, this.data.length+r.length);
9193             this.add(r);
9194         }
9195         this.fireEvent("load", this, r, options, o);
9196         if(options.callback){
9197             options.callback.call(options.scope || this, r, options, true);
9198         }
9199     },
9200
9201
9202     /**
9203      * Loads data from a passed data block. A Reader which understands the format of the data
9204      * must have been configured in the constructor.
9205      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9206      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9207      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9208      */
9209     loadData : function(o, append){
9210         var r = this.reader.readRecords(o);
9211         this.loadRecords(r, {add: append}, true);
9212     },
9213
9214     /**
9215      * Gets the number of cached records.
9216      * <p>
9217      * <em>If using paging, this may not be the total size of the dataset. If the data object
9218      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9219      * the data set size</em>
9220      */
9221     getCount : function(){
9222         return this.data.length || 0;
9223     },
9224
9225     /**
9226      * Gets the total number of records in the dataset as returned by the server.
9227      * <p>
9228      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9229      * the dataset size</em>
9230      */
9231     getTotalCount : function(){
9232         return this.totalLength || 0;
9233     },
9234
9235     /**
9236      * Returns the sort state of the Store as an object with two properties:
9237      * <pre><code>
9238  field {String} The name of the field by which the Records are sorted
9239  direction {String} The sort order, "ASC" or "DESC"
9240      * </code></pre>
9241      */
9242     getSortState : function(){
9243         return this.sortInfo;
9244     },
9245
9246     // private
9247     applySort : function(){
9248         if(this.sortInfo && !this.remoteSort){
9249             var s = this.sortInfo, f = s.field;
9250             var st = this.fields.get(f).sortType;
9251             var fn = function(r1, r2){
9252                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9253                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9254             };
9255             this.data.sort(s.direction, fn);
9256             if(this.snapshot && this.snapshot != this.data){
9257                 this.snapshot.sort(s.direction, fn);
9258             }
9259         }
9260     },
9261
9262     /**
9263      * Sets the default sort column and order to be used by the next load operation.
9264      * @param {String} fieldName The name of the field to sort by.
9265      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9266      */
9267     setDefaultSort : function(field, dir){
9268         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9269     },
9270
9271     /**
9272      * Sort the Records.
9273      * If remote sorting is used, the sort is performed on the server, and the cache is
9274      * reloaded. If local sorting is used, the cache is sorted internally.
9275      * @param {String} fieldName The name of the field to sort by.
9276      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9277      */
9278     sort : function(fieldName, dir){
9279         var f = this.fields.get(fieldName);
9280         if(!dir){
9281             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9282             
9283             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9284                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9285             }else{
9286                 dir = f.sortDir;
9287             }
9288         }
9289         this.sortToggle[f.name] = dir;
9290         this.sortInfo = {field: f.name, direction: dir};
9291         if(!this.remoteSort){
9292             this.applySort();
9293             this.fireEvent("datachanged", this);
9294         }else{
9295             this.load(this.lastOptions);
9296         }
9297     },
9298
9299     /**
9300      * Calls the specified function for each of the Records in the cache.
9301      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9302      * Returning <em>false</em> aborts and exits the iteration.
9303      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9304      */
9305     each : function(fn, scope){
9306         this.data.each(fn, scope);
9307     },
9308
9309     /**
9310      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9311      * (e.g., during paging).
9312      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9313      */
9314     getModifiedRecords : function(){
9315         return this.modified;
9316     },
9317
9318     // private
9319     createFilterFn : function(property, value, anyMatch){
9320         if(!value.exec){ // not a regex
9321             value = String(value);
9322             if(value.length == 0){
9323                 return false;
9324             }
9325             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9326         }
9327         return function(r){
9328             return value.test(r.data[property]);
9329         };
9330     },
9331
9332     /**
9333      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9334      * @param {String} property A field on your records
9335      * @param {Number} start The record index to start at (defaults to 0)
9336      * @param {Number} end The last record index to include (defaults to length - 1)
9337      * @return {Number} The sum
9338      */
9339     sum : function(property, start, end){
9340         var rs = this.data.items, v = 0;
9341         start = start || 0;
9342         end = (end || end === 0) ? end : rs.length-1;
9343
9344         for(var i = start; i <= end; i++){
9345             v += (rs[i].data[property] || 0);
9346         }
9347         return v;
9348     },
9349
9350     /**
9351      * Filter the records by a specified property.
9352      * @param {String} field A field on your records
9353      * @param {String/RegExp} value Either a string that the field
9354      * should start with or a RegExp to test against the field
9355      * @param {Boolean} anyMatch True to match any part not just the beginning
9356      */
9357     filter : function(property, value, anyMatch){
9358         var fn = this.createFilterFn(property, value, anyMatch);
9359         return fn ? this.filterBy(fn) : this.clearFilter();
9360     },
9361
9362     /**
9363      * Filter by a function. The specified function will be called with each
9364      * record in this data source. If the function returns true the record is included,
9365      * otherwise it is filtered.
9366      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9367      * @param {Object} scope (optional) The scope of the function (defaults to this)
9368      */
9369     filterBy : function(fn, scope){
9370         this.snapshot = this.snapshot || this.data;
9371         this.data = this.queryBy(fn, scope||this);
9372         this.fireEvent("datachanged", this);
9373     },
9374
9375     /**
9376      * Query the records by a specified property.
9377      * @param {String} field A field on your records
9378      * @param {String/RegExp} value Either a string that the field
9379      * should start with or a RegExp to test against the field
9380      * @param {Boolean} anyMatch True to match any part not just the beginning
9381      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9382      */
9383     query : function(property, value, anyMatch){
9384         var fn = this.createFilterFn(property, value, anyMatch);
9385         return fn ? this.queryBy(fn) : this.data.clone();
9386     },
9387
9388     /**
9389      * Query by a function. The specified function will be called with each
9390      * record in this data source. If the function returns true the record is included
9391      * in the results.
9392      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9393      * @param {Object} scope (optional) The scope of the function (defaults to this)
9394       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9395      **/
9396     queryBy : function(fn, scope){
9397         var data = this.snapshot || this.data;
9398         return data.filterBy(fn, scope||this);
9399     },
9400
9401     /**
9402      * Collects unique values for a particular dataIndex from this store.
9403      * @param {String} dataIndex The property to collect
9404      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9405      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9406      * @return {Array} An array of the unique values
9407      **/
9408     collect : function(dataIndex, allowNull, bypassFilter){
9409         var d = (bypassFilter === true && this.snapshot) ?
9410                 this.snapshot.items : this.data.items;
9411         var v, sv, r = [], l = {};
9412         for(var i = 0, len = d.length; i < len; i++){
9413             v = d[i].data[dataIndex];
9414             sv = String(v);
9415             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9416                 l[sv] = true;
9417                 r[r.length] = v;
9418             }
9419         }
9420         return r;
9421     },
9422
9423     /**
9424      * Revert to a view of the Record cache with no filtering applied.
9425      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9426      */
9427     clearFilter : function(suppressEvent){
9428         if(this.snapshot && this.snapshot != this.data){
9429             this.data = this.snapshot;
9430             delete this.snapshot;
9431             if(suppressEvent !== true){
9432                 this.fireEvent("datachanged", this);
9433             }
9434         }
9435     },
9436
9437     // private
9438     afterEdit : function(record){
9439         if(this.modified.indexOf(record) == -1){
9440             this.modified.push(record);
9441         }
9442         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9443     },
9444     
9445     // private
9446     afterReject : function(record){
9447         this.modified.remove(record);
9448         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9449     },
9450
9451     // private
9452     afterCommit : function(record){
9453         this.modified.remove(record);
9454         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9455     },
9456
9457     /**
9458      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9459      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9460      */
9461     commitChanges : function(){
9462         var m = this.modified.slice(0);
9463         this.modified = [];
9464         for(var i = 0, len = m.length; i < len; i++){
9465             m[i].commit();
9466         }
9467     },
9468
9469     /**
9470      * Cancel outstanding changes on all changed records.
9471      */
9472     rejectChanges : function(){
9473         var m = this.modified.slice(0);
9474         this.modified = [];
9475         for(var i = 0, len = m.length; i < len; i++){
9476             m[i].reject();
9477         }
9478     },
9479
9480     onMetaChange : function(meta, rtype, o){
9481         this.recordType = rtype;
9482         this.fields = rtype.prototype.fields;
9483         delete this.snapshot;
9484         this.sortInfo = meta.sortInfo || this.sortInfo;
9485         this.modified = [];
9486         this.fireEvent('metachange', this, this.reader.meta);
9487     },
9488     
9489     moveIndex : function(data, type)
9490     {
9491         var index = this.indexOf(data);
9492         
9493         var newIndex = index + type;
9494         
9495         this.remove(data);
9496         
9497         this.insert(newIndex, data);
9498         
9499     }
9500 });/*
9501  * Based on:
9502  * Ext JS Library 1.1.1
9503  * Copyright(c) 2006-2007, Ext JS, LLC.
9504  *
9505  * Originally Released Under LGPL - original licence link has changed is not relivant.
9506  *
9507  * Fork - LGPL
9508  * <script type="text/javascript">
9509  */
9510
9511 /**
9512  * @class Roo.data.SimpleStore
9513  * @extends Roo.data.Store
9514  * Small helper class to make creating Stores from Array data easier.
9515  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9516  * @cfg {Array} fields An array of field definition objects, or field name strings.
9517  * @cfg {Array} data The multi-dimensional array of data
9518  * @constructor
9519  * @param {Object} config
9520  */
9521 Roo.data.SimpleStore = function(config){
9522     Roo.data.SimpleStore.superclass.constructor.call(this, {
9523         isLocal : true,
9524         reader: new Roo.data.ArrayReader({
9525                 id: config.id
9526             },
9527             Roo.data.Record.create(config.fields)
9528         ),
9529         proxy : new Roo.data.MemoryProxy(config.data)
9530     });
9531     this.load();
9532 };
9533 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9534  * Based on:
9535  * Ext JS Library 1.1.1
9536  * Copyright(c) 2006-2007, Ext JS, LLC.
9537  *
9538  * Originally Released Under LGPL - original licence link has changed is not relivant.
9539  *
9540  * Fork - LGPL
9541  * <script type="text/javascript">
9542  */
9543
9544 /**
9545 /**
9546  * @extends Roo.data.Store
9547  * @class Roo.data.JsonStore
9548  * Small helper class to make creating Stores for JSON data easier. <br/>
9549 <pre><code>
9550 var store = new Roo.data.JsonStore({
9551     url: 'get-images.php',
9552     root: 'images',
9553     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9554 });
9555 </code></pre>
9556  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9557  * JsonReader and HttpProxy (unless inline data is provided).</b>
9558  * @cfg {Array} fields An array of field definition objects, or field name strings.
9559  * @constructor
9560  * @param {Object} config
9561  */
9562 Roo.data.JsonStore = function(c){
9563     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9564         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9565         reader: new Roo.data.JsonReader(c, c.fields)
9566     }));
9567 };
9568 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9569  * Based on:
9570  * Ext JS Library 1.1.1
9571  * Copyright(c) 2006-2007, Ext JS, LLC.
9572  *
9573  * Originally Released Under LGPL - original licence link has changed is not relivant.
9574  *
9575  * Fork - LGPL
9576  * <script type="text/javascript">
9577  */
9578
9579  
9580 Roo.data.Field = function(config){
9581     if(typeof config == "string"){
9582         config = {name: config};
9583     }
9584     Roo.apply(this, config);
9585     
9586     if(!this.type){
9587         this.type = "auto";
9588     }
9589     
9590     var st = Roo.data.SortTypes;
9591     // named sortTypes are supported, here we look them up
9592     if(typeof this.sortType == "string"){
9593         this.sortType = st[this.sortType];
9594     }
9595     
9596     // set default sortType for strings and dates
9597     if(!this.sortType){
9598         switch(this.type){
9599             case "string":
9600                 this.sortType = st.asUCString;
9601                 break;
9602             case "date":
9603                 this.sortType = st.asDate;
9604                 break;
9605             default:
9606                 this.sortType = st.none;
9607         }
9608     }
9609
9610     // define once
9611     var stripRe = /[\$,%]/g;
9612
9613     // prebuilt conversion function for this field, instead of
9614     // switching every time we're reading a value
9615     if(!this.convert){
9616         var cv, dateFormat = this.dateFormat;
9617         switch(this.type){
9618             case "":
9619             case "auto":
9620             case undefined:
9621                 cv = function(v){ return v; };
9622                 break;
9623             case "string":
9624                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9625                 break;
9626             case "int":
9627                 cv = function(v){
9628                     return v !== undefined && v !== null && v !== '' ?
9629                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9630                     };
9631                 break;
9632             case "float":
9633                 cv = function(v){
9634                     return v !== undefined && v !== null && v !== '' ?
9635                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9636                     };
9637                 break;
9638             case "bool":
9639             case "boolean":
9640                 cv = function(v){ return v === true || v === "true" || v == 1; };
9641                 break;
9642             case "date":
9643                 cv = function(v){
9644                     if(!v){
9645                         return '';
9646                     }
9647                     if(v instanceof Date){
9648                         return v;
9649                     }
9650                     if(dateFormat){
9651                         if(dateFormat == "timestamp"){
9652                             return new Date(v*1000);
9653                         }
9654                         return Date.parseDate(v, dateFormat);
9655                     }
9656                     var parsed = Date.parse(v);
9657                     return parsed ? new Date(parsed) : null;
9658                 };
9659              break;
9660             
9661         }
9662         this.convert = cv;
9663     }
9664 };
9665
9666 Roo.data.Field.prototype = {
9667     dateFormat: null,
9668     defaultValue: "",
9669     mapping: null,
9670     sortType : null,
9671     sortDir : "ASC"
9672 };/*
9673  * Based on:
9674  * Ext JS Library 1.1.1
9675  * Copyright(c) 2006-2007, Ext JS, LLC.
9676  *
9677  * Originally Released Under LGPL - original licence link has changed is not relivant.
9678  *
9679  * Fork - LGPL
9680  * <script type="text/javascript">
9681  */
9682  
9683 // Base class for reading structured data from a data source.  This class is intended to be
9684 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9685
9686 /**
9687  * @class Roo.data.DataReader
9688  * Base class for reading structured data from a data source.  This class is intended to be
9689  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9690  */
9691
9692 Roo.data.DataReader = function(meta, recordType){
9693     
9694     this.meta = meta;
9695     
9696     this.recordType = recordType instanceof Array ? 
9697         Roo.data.Record.create(recordType) : recordType;
9698 };
9699
9700 Roo.data.DataReader.prototype = {
9701      /**
9702      * Create an empty record
9703      * @param {Object} data (optional) - overlay some values
9704      * @return {Roo.data.Record} record created.
9705      */
9706     newRow :  function(d) {
9707         var da =  {};
9708         this.recordType.prototype.fields.each(function(c) {
9709             switch( c.type) {
9710                 case 'int' : da[c.name] = 0; break;
9711                 case 'date' : da[c.name] = new Date(); break;
9712                 case 'float' : da[c.name] = 0.0; break;
9713                 case 'boolean' : da[c.name] = false; break;
9714                 default : da[c.name] = ""; break;
9715             }
9716             
9717         });
9718         return new this.recordType(Roo.apply(da, d));
9719     }
9720     
9721 };/*
9722  * Based on:
9723  * Ext JS Library 1.1.1
9724  * Copyright(c) 2006-2007, Ext JS, LLC.
9725  *
9726  * Originally Released Under LGPL - original licence link has changed is not relivant.
9727  *
9728  * Fork - LGPL
9729  * <script type="text/javascript">
9730  */
9731
9732 /**
9733  * @class Roo.data.DataProxy
9734  * @extends Roo.data.Observable
9735  * This class is an abstract base class for implementations which provide retrieval of
9736  * unformatted data objects.<br>
9737  * <p>
9738  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9739  * (of the appropriate type which knows how to parse the data object) to provide a block of
9740  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9741  * <p>
9742  * Custom implementations must implement the load method as described in
9743  * {@link Roo.data.HttpProxy#load}.
9744  */
9745 Roo.data.DataProxy = function(){
9746     this.addEvents({
9747         /**
9748          * @event beforeload
9749          * Fires before a network request is made to retrieve a data object.
9750          * @param {Object} This DataProxy object.
9751          * @param {Object} params The params parameter to the load function.
9752          */
9753         beforeload : true,
9754         /**
9755          * @event load
9756          * Fires before the load method's callback is called.
9757          * @param {Object} This DataProxy object.
9758          * @param {Object} o The data object.
9759          * @param {Object} arg The callback argument object passed to the load function.
9760          */
9761         load : true,
9762         /**
9763          * @event loadexception
9764          * Fires if an Exception occurs during data retrieval.
9765          * @param {Object} This DataProxy object.
9766          * @param {Object} o The data object.
9767          * @param {Object} arg The callback argument object passed to the load function.
9768          * @param {Object} e The Exception.
9769          */
9770         loadexception : true
9771     });
9772     Roo.data.DataProxy.superclass.constructor.call(this);
9773 };
9774
9775 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9776
9777     /**
9778      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9779      */
9780 /*
9781  * Based on:
9782  * Ext JS Library 1.1.1
9783  * Copyright(c) 2006-2007, Ext JS, LLC.
9784  *
9785  * Originally Released Under LGPL - original licence link has changed is not relivant.
9786  *
9787  * Fork - LGPL
9788  * <script type="text/javascript">
9789  */
9790 /**
9791  * @class Roo.data.MemoryProxy
9792  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9793  * to the Reader when its load method is called.
9794  * @constructor
9795  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9796  */
9797 Roo.data.MemoryProxy = function(data){
9798     if (data.data) {
9799         data = data.data;
9800     }
9801     Roo.data.MemoryProxy.superclass.constructor.call(this);
9802     this.data = data;
9803 };
9804
9805 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9806     /**
9807      * Load data from the requested source (in this case an in-memory
9808      * data object passed to the constructor), read the data object into
9809      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9810      * process that block using the passed callback.
9811      * @param {Object} params This parameter is not used by the MemoryProxy class.
9812      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9813      * object into a block of Roo.data.Records.
9814      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9815      * The function must be passed <ul>
9816      * <li>The Record block object</li>
9817      * <li>The "arg" argument from the load function</li>
9818      * <li>A boolean success indicator</li>
9819      * </ul>
9820      * @param {Object} scope The scope in which to call the callback
9821      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9822      */
9823     load : function(params, reader, callback, scope, arg){
9824         params = params || {};
9825         var result;
9826         try {
9827             result = reader.readRecords(this.data);
9828         }catch(e){
9829             this.fireEvent("loadexception", this, arg, null, e);
9830             callback.call(scope, null, arg, false);
9831             return;
9832         }
9833         callback.call(scope, result, arg, true);
9834     },
9835     
9836     // private
9837     update : function(params, records){
9838         
9839     }
9840 });/*
9841  * Based on:
9842  * Ext JS Library 1.1.1
9843  * Copyright(c) 2006-2007, Ext JS, LLC.
9844  *
9845  * Originally Released Under LGPL - original licence link has changed is not relivant.
9846  *
9847  * Fork - LGPL
9848  * <script type="text/javascript">
9849  */
9850 /**
9851  * @class Roo.data.HttpProxy
9852  * @extends Roo.data.DataProxy
9853  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9854  * configured to reference a certain URL.<br><br>
9855  * <p>
9856  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9857  * from which the running page was served.<br><br>
9858  * <p>
9859  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9860  * <p>
9861  * Be aware that to enable the browser to parse an XML document, the server must set
9862  * the Content-Type header in the HTTP response to "text/xml".
9863  * @constructor
9864  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9865  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9866  * will be used to make the request.
9867  */
9868 Roo.data.HttpProxy = function(conn){
9869     Roo.data.HttpProxy.superclass.constructor.call(this);
9870     // is conn a conn config or a real conn?
9871     this.conn = conn;
9872     this.useAjax = !conn || !conn.events;
9873   
9874 };
9875
9876 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9877     // thse are take from connection...
9878     
9879     /**
9880      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9881      */
9882     /**
9883      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9884      * extra parameters to each request made by this object. (defaults to undefined)
9885      */
9886     /**
9887      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9888      *  to each request made by this object. (defaults to undefined)
9889      */
9890     /**
9891      * @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)
9892      */
9893     /**
9894      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9895      */
9896      /**
9897      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9898      * @type Boolean
9899      */
9900   
9901
9902     /**
9903      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9904      * @type Boolean
9905      */
9906     /**
9907      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9908      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9909      * a finer-grained basis than the DataProxy events.
9910      */
9911     getConnection : function(){
9912         return this.useAjax ? Roo.Ajax : this.conn;
9913     },
9914
9915     /**
9916      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9917      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9918      * process that block using the passed callback.
9919      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9920      * for the request to the remote server.
9921      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9922      * object into a block of Roo.data.Records.
9923      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9924      * The function must be passed <ul>
9925      * <li>The Record block object</li>
9926      * <li>The "arg" argument from the load function</li>
9927      * <li>A boolean success indicator</li>
9928      * </ul>
9929      * @param {Object} scope The scope in which to call the callback
9930      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9931      */
9932     load : function(params, reader, callback, scope, arg){
9933         if(this.fireEvent("beforeload", this, params) !== false){
9934             var  o = {
9935                 params : params || {},
9936                 request: {
9937                     callback : callback,
9938                     scope : scope,
9939                     arg : arg
9940                 },
9941                 reader: reader,
9942                 callback : this.loadResponse,
9943                 scope: this
9944             };
9945             if(this.useAjax){
9946                 Roo.applyIf(o, this.conn);
9947                 if(this.activeRequest){
9948                     Roo.Ajax.abort(this.activeRequest);
9949                 }
9950                 this.activeRequest = Roo.Ajax.request(o);
9951             }else{
9952                 this.conn.request(o);
9953             }
9954         }else{
9955             callback.call(scope||this, null, arg, false);
9956         }
9957     },
9958
9959     // private
9960     loadResponse : function(o, success, response){
9961         delete this.activeRequest;
9962         if(!success){
9963             this.fireEvent("loadexception", this, o, response);
9964             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9965             return;
9966         }
9967         var result;
9968         try {
9969             result = o.reader.read(response);
9970         }catch(e){
9971             this.fireEvent("loadexception", this, o, response, e);
9972             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9973             return;
9974         }
9975         
9976         this.fireEvent("load", this, o, o.request.arg);
9977         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9978     },
9979
9980     // private
9981     update : function(dataSet){
9982
9983     },
9984
9985     // private
9986     updateResponse : function(dataSet){
9987
9988     }
9989 });/*
9990  * Based on:
9991  * Ext JS Library 1.1.1
9992  * Copyright(c) 2006-2007, Ext JS, LLC.
9993  *
9994  * Originally Released Under LGPL - original licence link has changed is not relivant.
9995  *
9996  * Fork - LGPL
9997  * <script type="text/javascript">
9998  */
9999
10000 /**
10001  * @class Roo.data.ScriptTagProxy
10002  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10003  * other than the originating domain of the running page.<br><br>
10004  * <p>
10005  * <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
10006  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10007  * <p>
10008  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10009  * source code that is used as the source inside a &lt;script> tag.<br><br>
10010  * <p>
10011  * In order for the browser to process the returned data, the server must wrap the data object
10012  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10013  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10014  * depending on whether the callback name was passed:
10015  * <p>
10016  * <pre><code>
10017 boolean scriptTag = false;
10018 String cb = request.getParameter("callback");
10019 if (cb != null) {
10020     scriptTag = true;
10021     response.setContentType("text/javascript");
10022 } else {
10023     response.setContentType("application/x-json");
10024 }
10025 Writer out = response.getWriter();
10026 if (scriptTag) {
10027     out.write(cb + "(");
10028 }
10029 out.print(dataBlock.toJsonString());
10030 if (scriptTag) {
10031     out.write(");");
10032 }
10033 </pre></code>
10034  *
10035  * @constructor
10036  * @param {Object} config A configuration object.
10037  */
10038 Roo.data.ScriptTagProxy = function(config){
10039     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10040     Roo.apply(this, config);
10041     this.head = document.getElementsByTagName("head")[0];
10042 };
10043
10044 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10045
10046 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10047     /**
10048      * @cfg {String} url The URL from which to request the data object.
10049      */
10050     /**
10051      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10052      */
10053     timeout : 30000,
10054     /**
10055      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10056      * the server the name of the callback function set up by the load call to process the returned data object.
10057      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10058      * javascript output which calls this named function passing the data object as its only parameter.
10059      */
10060     callbackParam : "callback",
10061     /**
10062      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10063      * name to the request.
10064      */
10065     nocache : true,
10066
10067     /**
10068      * Load data from the configured URL, read the data object into
10069      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10070      * process that block using the passed callback.
10071      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10072      * for the request to the remote server.
10073      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10074      * object into a block of Roo.data.Records.
10075      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10076      * The function must be passed <ul>
10077      * <li>The Record block object</li>
10078      * <li>The "arg" argument from the load function</li>
10079      * <li>A boolean success indicator</li>
10080      * </ul>
10081      * @param {Object} scope The scope in which to call the callback
10082      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10083      */
10084     load : function(params, reader, callback, scope, arg){
10085         if(this.fireEvent("beforeload", this, params) !== false){
10086
10087             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10088
10089             var url = this.url;
10090             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10091             if(this.nocache){
10092                 url += "&_dc=" + (new Date().getTime());
10093             }
10094             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10095             var trans = {
10096                 id : transId,
10097                 cb : "stcCallback"+transId,
10098                 scriptId : "stcScript"+transId,
10099                 params : params,
10100                 arg : arg,
10101                 url : url,
10102                 callback : callback,
10103                 scope : scope,
10104                 reader : reader
10105             };
10106             var conn = this;
10107
10108             window[trans.cb] = function(o){
10109                 conn.handleResponse(o, trans);
10110             };
10111
10112             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10113
10114             if(this.autoAbort !== false){
10115                 this.abort();
10116             }
10117
10118             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10119
10120             var script = document.createElement("script");
10121             script.setAttribute("src", url);
10122             script.setAttribute("type", "text/javascript");
10123             script.setAttribute("id", trans.scriptId);
10124             this.head.appendChild(script);
10125
10126             this.trans = trans;
10127         }else{
10128             callback.call(scope||this, null, arg, false);
10129         }
10130     },
10131
10132     // private
10133     isLoading : function(){
10134         return this.trans ? true : false;
10135     },
10136
10137     /**
10138      * Abort the current server request.
10139      */
10140     abort : function(){
10141         if(this.isLoading()){
10142             this.destroyTrans(this.trans);
10143         }
10144     },
10145
10146     // private
10147     destroyTrans : function(trans, isLoaded){
10148         this.head.removeChild(document.getElementById(trans.scriptId));
10149         clearTimeout(trans.timeoutId);
10150         if(isLoaded){
10151             window[trans.cb] = undefined;
10152             try{
10153                 delete window[trans.cb];
10154             }catch(e){}
10155         }else{
10156             // if hasn't been loaded, wait for load to remove it to prevent script error
10157             window[trans.cb] = function(){
10158                 window[trans.cb] = undefined;
10159                 try{
10160                     delete window[trans.cb];
10161                 }catch(e){}
10162             };
10163         }
10164     },
10165
10166     // private
10167     handleResponse : function(o, trans){
10168         this.trans = false;
10169         this.destroyTrans(trans, true);
10170         var result;
10171         try {
10172             result = trans.reader.readRecords(o);
10173         }catch(e){
10174             this.fireEvent("loadexception", this, o, trans.arg, e);
10175             trans.callback.call(trans.scope||window, null, trans.arg, false);
10176             return;
10177         }
10178         this.fireEvent("load", this, o, trans.arg);
10179         trans.callback.call(trans.scope||window, result, trans.arg, true);
10180     },
10181
10182     // private
10183     handleFailure : function(trans){
10184         this.trans = false;
10185         this.destroyTrans(trans, false);
10186         this.fireEvent("loadexception", this, null, trans.arg);
10187         trans.callback.call(trans.scope||window, null, trans.arg, false);
10188     }
10189 });/*
10190  * Based on:
10191  * Ext JS Library 1.1.1
10192  * Copyright(c) 2006-2007, Ext JS, LLC.
10193  *
10194  * Originally Released Under LGPL - original licence link has changed is not relivant.
10195  *
10196  * Fork - LGPL
10197  * <script type="text/javascript">
10198  */
10199
10200 /**
10201  * @class Roo.data.JsonReader
10202  * @extends Roo.data.DataReader
10203  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10204  * based on mappings in a provided Roo.data.Record constructor.
10205  * 
10206  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10207  * in the reply previously. 
10208  * 
10209  * <p>
10210  * Example code:
10211  * <pre><code>
10212 var RecordDef = Roo.data.Record.create([
10213     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10214     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10215 ]);
10216 var myReader = new Roo.data.JsonReader({
10217     totalProperty: "results",    // The property which contains the total dataset size (optional)
10218     root: "rows",                // The property which contains an Array of row objects
10219     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10220 }, RecordDef);
10221 </code></pre>
10222  * <p>
10223  * This would consume a JSON file like this:
10224  * <pre><code>
10225 { 'results': 2, 'rows': [
10226     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10227     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10228 }
10229 </code></pre>
10230  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10231  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10232  * paged from the remote server.
10233  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10234  * @cfg {String} root name of the property which contains the Array of row objects.
10235  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10236  * @constructor
10237  * Create a new JsonReader
10238  * @param {Object} meta Metadata configuration options
10239  * @param {Object} recordType Either an Array of field definition objects,
10240  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10241  */
10242 Roo.data.JsonReader = function(meta, recordType){
10243     
10244     meta = meta || {};
10245     // set some defaults:
10246     Roo.applyIf(meta, {
10247         totalProperty: 'total',
10248         successProperty : 'success',
10249         root : 'data',
10250         id : 'id'
10251     });
10252     
10253     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10254 };
10255 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10256     
10257     /**
10258      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10259      * Used by Store query builder to append _requestMeta to params.
10260      * 
10261      */
10262     metaFromRemote : false,
10263     /**
10264      * This method is only used by a DataProxy which has retrieved data from a remote server.
10265      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10266      * @return {Object} data A data block which is used by an Roo.data.Store object as
10267      * a cache of Roo.data.Records.
10268      */
10269     read : function(response){
10270         var json = response.responseText;
10271        
10272         var o = /* eval:var:o */ eval("("+json+")");
10273         if(!o) {
10274             throw {message: "JsonReader.read: Json object not found"};
10275         }
10276         
10277         if(o.metaData){
10278             
10279             delete this.ef;
10280             this.metaFromRemote = true;
10281             this.meta = o.metaData;
10282             this.recordType = Roo.data.Record.create(o.metaData.fields);
10283             this.onMetaChange(this.meta, this.recordType, o);
10284         }
10285         return this.readRecords(o);
10286     },
10287
10288     // private function a store will implement
10289     onMetaChange : function(meta, recordType, o){
10290
10291     },
10292
10293     /**
10294          * @ignore
10295          */
10296     simpleAccess: function(obj, subsc) {
10297         return obj[subsc];
10298     },
10299
10300         /**
10301          * @ignore
10302          */
10303     getJsonAccessor: function(){
10304         var re = /[\[\.]/;
10305         return function(expr) {
10306             try {
10307                 return(re.test(expr))
10308                     ? new Function("obj", "return obj." + expr)
10309                     : function(obj){
10310                         return obj[expr];
10311                     };
10312             } catch(e){}
10313             return Roo.emptyFn;
10314         };
10315     }(),
10316
10317     /**
10318      * Create a data block containing Roo.data.Records from an XML document.
10319      * @param {Object} o An object which contains an Array of row objects in the property specified
10320      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10321      * which contains the total size of the dataset.
10322      * @return {Object} data A data block which is used by an Roo.data.Store object as
10323      * a cache of Roo.data.Records.
10324      */
10325     readRecords : function(o){
10326         /**
10327          * After any data loads, the raw JSON data is available for further custom processing.
10328          * @type Object
10329          */
10330         this.o = o;
10331         var s = this.meta, Record = this.recordType,
10332             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10333
10334 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10335         if (!this.ef) {
10336             if(s.totalProperty) {
10337                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10338                 }
10339                 if(s.successProperty) {
10340                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10341                 }
10342                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10343                 if (s.id) {
10344                         var g = this.getJsonAccessor(s.id);
10345                         this.getId = function(rec) {
10346                                 var r = g(rec);  
10347                                 return (r === undefined || r === "") ? null : r;
10348                         };
10349                 } else {
10350                         this.getId = function(){return null;};
10351                 }
10352             this.ef = [];
10353             for(var jj = 0; jj < fl; jj++){
10354                 f = fi[jj];
10355                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10356                 this.ef[jj] = this.getJsonAccessor(map);
10357             }
10358         }
10359
10360         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10361         if(s.totalProperty){
10362             var vt = parseInt(this.getTotal(o), 10);
10363             if(!isNaN(vt)){
10364                 totalRecords = vt;
10365             }
10366         }
10367         if(s.successProperty){
10368             var vs = this.getSuccess(o);
10369             if(vs === false || vs === 'false'){
10370                 success = false;
10371             }
10372         }
10373         var records = [];
10374         for(var i = 0; i < c; i++){
10375                 var n = root[i];
10376             var values = {};
10377             var id = this.getId(n);
10378             for(var j = 0; j < fl; j++){
10379                 f = fi[j];
10380             var v = this.ef[j](n);
10381             if (!f.convert) {
10382                 Roo.log('missing convert for ' + f.name);
10383                 Roo.log(f);
10384                 continue;
10385             }
10386             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10387             }
10388             var record = new Record(values, id);
10389             record.json = n;
10390             records[i] = record;
10391         }
10392         return {
10393             raw : o,
10394             success : success,
10395             records : records,
10396             totalRecords : totalRecords
10397         };
10398     }
10399 });/*
10400  * Based on:
10401  * Ext JS Library 1.1.1
10402  * Copyright(c) 2006-2007, Ext JS, LLC.
10403  *
10404  * Originally Released Under LGPL - original licence link has changed is not relivant.
10405  *
10406  * Fork - LGPL
10407  * <script type="text/javascript">
10408  */
10409
10410 /**
10411  * @class Roo.data.ArrayReader
10412  * @extends Roo.data.DataReader
10413  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10414  * Each element of that Array represents a row of data fields. The
10415  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10416  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10417  * <p>
10418  * Example code:.
10419  * <pre><code>
10420 var RecordDef = Roo.data.Record.create([
10421     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10422     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10423 ]);
10424 var myReader = new Roo.data.ArrayReader({
10425     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10426 }, RecordDef);
10427 </code></pre>
10428  * <p>
10429  * This would consume an Array like this:
10430  * <pre><code>
10431 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10432   </code></pre>
10433  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10434  * @constructor
10435  * Create a new JsonReader
10436  * @param {Object} meta Metadata configuration options.
10437  * @param {Object} recordType Either an Array of field definition objects
10438  * as specified to {@link Roo.data.Record#create},
10439  * or an {@link Roo.data.Record} object
10440  * created using {@link Roo.data.Record#create}.
10441  */
10442 Roo.data.ArrayReader = function(meta, recordType){
10443     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10444 };
10445
10446 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10447     /**
10448      * Create a data block containing Roo.data.Records from an XML document.
10449      * @param {Object} o An Array of row objects which represents the dataset.
10450      * @return {Object} data A data block which is used by an Roo.data.Store object as
10451      * a cache of Roo.data.Records.
10452      */
10453     readRecords : function(o){
10454         var sid = this.meta ? this.meta.id : null;
10455         var recordType = this.recordType, fields = recordType.prototype.fields;
10456         var records = [];
10457         var root = o;
10458             for(var i = 0; i < root.length; i++){
10459                     var n = root[i];
10460                 var values = {};
10461                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10462                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10463                 var f = fields.items[j];
10464                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10465                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10466                 v = f.convert(v);
10467                 values[f.name] = v;
10468             }
10469                 var record = new recordType(values, id);
10470                 record.json = n;
10471                 records[records.length] = record;
10472             }
10473             return {
10474                 records : records,
10475                 totalRecords : records.length
10476             };
10477     }
10478 });/*
10479  * - LGPL
10480  * * 
10481  */
10482
10483 /**
10484  * @class Roo.bootstrap.ComboBox
10485  * @extends Roo.bootstrap.TriggerField
10486  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10487  * @cfg {Boolean} append (true|false) default false
10488  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10489  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10490  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10491  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10492  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10493  * @constructor
10494  * Create a new ComboBox.
10495  * @param {Object} config Configuration options
10496  */
10497 Roo.bootstrap.ComboBox = function(config){
10498     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10499     this.addEvents({
10500         /**
10501          * @event expand
10502          * Fires when the dropdown list is expanded
10503              * @param {Roo.bootstrap.ComboBox} combo This combo box
10504              */
10505         'expand' : true,
10506         /**
10507          * @event collapse
10508          * Fires when the dropdown list is collapsed
10509              * @param {Roo.bootstrap.ComboBox} combo This combo box
10510              */
10511         'collapse' : true,
10512         /**
10513          * @event beforeselect
10514          * Fires before a list item is selected. Return false to cancel the selection.
10515              * @param {Roo.bootstrap.ComboBox} combo This combo box
10516              * @param {Roo.data.Record} record The data record returned from the underlying store
10517              * @param {Number} index The index of the selected item in the dropdown list
10518              */
10519         'beforeselect' : true,
10520         /**
10521          * @event select
10522          * Fires when a list item is selected
10523              * @param {Roo.bootstrap.ComboBox} combo This combo box
10524              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10525              * @param {Number} index The index of the selected item in the dropdown list
10526              */
10527         'select' : true,
10528         /**
10529          * @event beforequery
10530          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10531          * The event object passed has these properties:
10532              * @param {Roo.bootstrap.ComboBox} combo This combo box
10533              * @param {String} query The query
10534              * @param {Boolean} forceAll true to force "all" query
10535              * @param {Boolean} cancel true to cancel the query
10536              * @param {Object} e The query event object
10537              */
10538         'beforequery': true,
10539          /**
10540          * @event add
10541          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10542              * @param {Roo.bootstrap.ComboBox} combo This combo box
10543              */
10544         'add' : true,
10545         /**
10546          * @event edit
10547          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10548              * @param {Roo.bootstrap.ComboBox} combo This combo box
10549              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10550              */
10551         'edit' : true,
10552         /**
10553          * @event remove
10554          * Fires when the remove value from the combobox array
10555              * @param {Roo.bootstrap.ComboBox} combo This combo box
10556              */
10557         'remove' : true
10558         
10559     });
10560     
10561     this.item = [];
10562     this.tickItems = [];
10563     
10564     this.selectedIndex = -1;
10565     if(this.mode == 'local'){
10566         if(config.queryDelay === undefined){
10567             this.queryDelay = 10;
10568         }
10569         if(config.minChars === undefined){
10570             this.minChars = 0;
10571         }
10572     }
10573 };
10574
10575 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10576      
10577     /**
10578      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10579      * rendering into an Roo.Editor, defaults to false)
10580      */
10581     /**
10582      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10583      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10584      */
10585     /**
10586      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10587      */
10588     /**
10589      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10590      * the dropdown list (defaults to undefined, with no header element)
10591      */
10592
10593      /**
10594      * @cfg {String/Roo.Template} tpl The template to use to render the output
10595      */
10596      
10597      /**
10598      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10599      */
10600     listWidth: undefined,
10601     /**
10602      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10603      * mode = 'remote' or 'text' if mode = 'local')
10604      */
10605     displayField: undefined,
10606     /**
10607      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10608      * mode = 'remote' or 'value' if mode = 'local'). 
10609      * Note: use of a valueField requires the user make a selection
10610      * in order for a value to be mapped.
10611      */
10612     valueField: undefined,
10613     
10614     
10615     /**
10616      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10617      * field's data value (defaults to the underlying DOM element's name)
10618      */
10619     hiddenName: undefined,
10620     /**
10621      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10622      */
10623     listClass: '',
10624     /**
10625      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10626      */
10627     selectedClass: 'active',
10628     
10629     /**
10630      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10631      */
10632     shadow:'sides',
10633     /**
10634      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10635      * anchor positions (defaults to 'tl-bl')
10636      */
10637     listAlign: 'tl-bl?',
10638     /**
10639      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10640      */
10641     maxHeight: 300,
10642     /**
10643      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10644      * query specified by the allQuery config option (defaults to 'query')
10645      */
10646     triggerAction: 'query',
10647     /**
10648      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10649      * (defaults to 4, does not apply if editable = false)
10650      */
10651     minChars : 4,
10652     /**
10653      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10654      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10655      */
10656     typeAhead: false,
10657     /**
10658      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10659      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10660      */
10661     queryDelay: 500,
10662     /**
10663      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10664      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10665      */
10666     pageSize: 0,
10667     /**
10668      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10669      * when editable = true (defaults to false)
10670      */
10671     selectOnFocus:false,
10672     /**
10673      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10674      */
10675     queryParam: 'query',
10676     /**
10677      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10678      * when mode = 'remote' (defaults to 'Loading...')
10679      */
10680     loadingText: 'Loading...',
10681     /**
10682      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10683      */
10684     resizable: false,
10685     /**
10686      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10687      */
10688     handleHeight : 8,
10689     /**
10690      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10691      * traditional select (defaults to true)
10692      */
10693     editable: true,
10694     /**
10695      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10696      */
10697     allQuery: '',
10698     /**
10699      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10700      */
10701     mode: 'remote',
10702     /**
10703      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10704      * listWidth has a higher value)
10705      */
10706     minListWidth : 70,
10707     /**
10708      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10709      * allow the user to set arbitrary text into the field (defaults to false)
10710      */
10711     forceSelection:false,
10712     /**
10713      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10714      * if typeAhead = true (defaults to 250)
10715      */
10716     typeAheadDelay : 250,
10717     /**
10718      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10719      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10720      */
10721     valueNotFoundText : undefined,
10722     /**
10723      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10724      */
10725     blockFocus : false,
10726     
10727     /**
10728      * @cfg {Boolean} disableClear Disable showing of clear button.
10729      */
10730     disableClear : false,
10731     /**
10732      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10733      */
10734     alwaysQuery : false,
10735     
10736     /**
10737      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10738      */
10739     multiple : false,
10740     
10741     //private
10742     addicon : false,
10743     editicon: false,
10744     
10745     page: 0,
10746     hasQuery: false,
10747     append: false,
10748     loadNext: false,
10749     autoFocus : true,
10750     tickable : false,
10751     btnPosition : 'right',
10752     triggerList : true,
10753     showToggleBtn : true,
10754     // element that contains real text value.. (when hidden is used..)
10755     
10756     getAutoCreate : function()
10757     {
10758         var cfg = false;
10759         
10760         /*
10761          *  Normal ComboBox
10762          */
10763         if(!this.tickable){
10764             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10765             return cfg;
10766         }
10767         
10768         /*
10769          *  ComboBox with tickable selections
10770          */
10771              
10772         var align = this.labelAlign || this.parentLabelAlign();
10773         
10774         cfg = {
10775             cls : 'form-group roo-combobox-tickable' //input-group
10776         };
10777         
10778         
10779         var buttons = {
10780             tag : 'div',
10781             cls : 'tickable-buttons',
10782             cn : [
10783                 {
10784                     tag : 'button',
10785                     type : 'button',
10786                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10787                     html : 'Edit'
10788                 },
10789                 {
10790                     tag : 'button',
10791                     type : 'button',
10792                     name : 'ok',
10793                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10794                     html : 'Done'
10795                 },
10796                 {
10797                     tag : 'button',
10798                     type : 'button',
10799                     name : 'cancel',
10800                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10801                     html : 'Cancel'
10802                 }
10803             ]
10804         };
10805         
10806         var _this = this;
10807         Roo.each(buttons.cn, function(c){
10808             if (_this.size) {
10809                 c.cls += ' btn-' + _this.size;
10810             }
10811
10812             if (_this.disabled) {
10813                 c.disabled = true;
10814             }
10815         });
10816         
10817         var box = {
10818             tag: 'div',
10819             cn: [
10820                 {
10821                     tag: 'input',
10822                     type : 'hidden',
10823                     cls: 'form-hidden-field'
10824                 },
10825                 {
10826                     tag: 'ul',
10827                     cls: 'select2-choices',
10828                     cn:[
10829                         {
10830                             tag: 'li',
10831                             cls: 'select2-search-field',
10832                             cn: [
10833
10834                                 buttons
10835                             ]
10836                         }
10837                     ]
10838                 }
10839             ]
10840         }
10841         
10842         var combobox = {
10843             cls: 'select2-container input-group select2-container-multi',
10844             cn: [
10845                 box
10846 //                {
10847 //                    tag: 'ul',
10848 //                    cls: 'typeahead typeahead-long dropdown-menu',
10849 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10850 //                }
10851             ]
10852         };
10853         
10854         if (align ==='left' && this.fieldLabel.length) {
10855             
10856                 Roo.log("left and has label");
10857                 cfg.cn = [
10858                     
10859                     {
10860                         tag: 'label',
10861                         'for' :  id,
10862                         cls : 'control-label col-sm-' + this.labelWidth,
10863                         html : this.fieldLabel
10864                         
10865                     },
10866                     {
10867                         cls : "col-sm-" + (12 - this.labelWidth), 
10868                         cn: [
10869                             combobox
10870                         ]
10871                     }
10872                     
10873                 ];
10874         } else if ( this.fieldLabel.length) {
10875                 Roo.log(" label");
10876                  cfg.cn = [
10877                    
10878                     {
10879                         tag: 'label',
10880                         //cls : 'input-group-addon',
10881                         html : this.fieldLabel
10882                         
10883                     },
10884                     
10885                     combobox
10886                     
10887                 ];
10888
10889         } else {
10890             
10891                 Roo.log(" no label && no align");
10892                 cfg = combobox
10893                      
10894                 
10895         }
10896          
10897         var settings=this;
10898         ['xs','sm','md','lg'].map(function(size){
10899             if (settings[size]) {
10900                 cfg.cls += ' col-' + size + '-' + settings[size];
10901             }
10902         });
10903         
10904         return cfg;
10905         
10906     },
10907     
10908     // private
10909     initEvents: function()
10910     {
10911         
10912         if (!this.store) {
10913             throw "can not find store for combo";
10914         }
10915         this.store = Roo.factory(this.store, Roo.data);
10916         
10917         if(this.tickable){
10918             this.initTickableEvents();
10919             return;
10920         }
10921         
10922         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10923         
10924         if(this.hiddenName){
10925             
10926             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10927             
10928             this.hiddenField.dom.value =
10929                 this.hiddenValue !== undefined ? this.hiddenValue :
10930                 this.value !== undefined ? this.value : '';
10931
10932             // prevent input submission
10933             this.el.dom.removeAttribute('name');
10934             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10935              
10936              
10937         }
10938         //if(Roo.isGecko){
10939         //    this.el.dom.setAttribute('autocomplete', 'off');
10940         //}
10941         
10942         var cls = 'x-combo-list';
10943         
10944         //this.list = new Roo.Layer({
10945         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10946         //});
10947         
10948         var _this = this;
10949         
10950         (function(){
10951             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10952             _this.list.setWidth(lw);
10953         }).defer(100);
10954         
10955         this.list.on('mouseover', this.onViewOver, this);
10956         this.list.on('mousemove', this.onViewMove, this);
10957         
10958         this.list.on('scroll', this.onViewScroll, this);
10959         
10960         /*
10961         this.list.swallowEvent('mousewheel');
10962         this.assetHeight = 0;
10963
10964         if(this.title){
10965             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10966             this.assetHeight += this.header.getHeight();
10967         }
10968
10969         this.innerList = this.list.createChild({cls:cls+'-inner'});
10970         this.innerList.on('mouseover', this.onViewOver, this);
10971         this.innerList.on('mousemove', this.onViewMove, this);
10972         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10973         
10974         if(this.allowBlank && !this.pageSize && !this.disableClear){
10975             this.footer = this.list.createChild({cls:cls+'-ft'});
10976             this.pageTb = new Roo.Toolbar(this.footer);
10977            
10978         }
10979         if(this.pageSize){
10980             this.footer = this.list.createChild({cls:cls+'-ft'});
10981             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10982                     {pageSize: this.pageSize});
10983             
10984         }
10985         
10986         if (this.pageTb && this.allowBlank && !this.disableClear) {
10987             var _this = this;
10988             this.pageTb.add(new Roo.Toolbar.Fill(), {
10989                 cls: 'x-btn-icon x-btn-clear',
10990                 text: '&#160;',
10991                 handler: function()
10992                 {
10993                     _this.collapse();
10994                     _this.clearValue();
10995                     _this.onSelect(false, -1);
10996                 }
10997             });
10998         }
10999         if (this.footer) {
11000             this.assetHeight += this.footer.getHeight();
11001         }
11002         */
11003             
11004         if(!this.tpl){
11005             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11006         }
11007
11008         this.view = new Roo.View(this.list, this.tpl, {
11009             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11010         });
11011         //this.view.wrapEl.setDisplayed(false);
11012         this.view.on('click', this.onViewClick, this);
11013         
11014         
11015         
11016         this.store.on('beforeload', this.onBeforeLoad, this);
11017         this.store.on('load', this.onLoad, this);
11018         this.store.on('loadexception', this.onLoadException, this);
11019         /*
11020         if(this.resizable){
11021             this.resizer = new Roo.Resizable(this.list,  {
11022                pinned:true, handles:'se'
11023             });
11024             this.resizer.on('resize', function(r, w, h){
11025                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11026                 this.listWidth = w;
11027                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11028                 this.restrictHeight();
11029             }, this);
11030             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11031         }
11032         */
11033         if(!this.editable){
11034             this.editable = true;
11035             this.setEditable(false);
11036         }
11037         
11038         /*
11039         
11040         if (typeof(this.events.add.listeners) != 'undefined') {
11041             
11042             this.addicon = this.wrap.createChild(
11043                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11044        
11045             this.addicon.on('click', function(e) {
11046                 this.fireEvent('add', this);
11047             }, this);
11048         }
11049         if (typeof(this.events.edit.listeners) != 'undefined') {
11050             
11051             this.editicon = this.wrap.createChild(
11052                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11053             if (this.addicon) {
11054                 this.editicon.setStyle('margin-left', '40px');
11055             }
11056             this.editicon.on('click', function(e) {
11057                 
11058                 // we fire even  if inothing is selected..
11059                 this.fireEvent('edit', this, this.lastData );
11060                 
11061             }, this);
11062         }
11063         */
11064         
11065         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11066             "up" : function(e){
11067                 this.inKeyMode = true;
11068                 this.selectPrev();
11069             },
11070
11071             "down" : function(e){
11072                 if(!this.isExpanded()){
11073                     this.onTriggerClick();
11074                 }else{
11075                     this.inKeyMode = true;
11076                     this.selectNext();
11077                 }
11078             },
11079
11080             "enter" : function(e){
11081 //                this.onViewClick();
11082                 //return true;
11083                 this.collapse();
11084                 
11085                 if(this.fireEvent("specialkey", this, e)){
11086                     this.onViewClick(false);
11087                 }
11088                 
11089                 return true;
11090             },
11091
11092             "esc" : function(e){
11093                 this.collapse();
11094             },
11095
11096             "tab" : function(e){
11097                 this.collapse();
11098                 
11099                 if(this.fireEvent("specialkey", this, e)){
11100                     this.onViewClick(false);
11101                 }
11102                 
11103                 return true;
11104             },
11105
11106             scope : this,
11107
11108             doRelay : function(foo, bar, hname){
11109                 if(hname == 'down' || this.scope.isExpanded()){
11110                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11111                 }
11112                 return true;
11113             },
11114
11115             forceKeyDown: true
11116         });
11117         
11118         
11119         this.queryDelay = Math.max(this.queryDelay || 10,
11120                 this.mode == 'local' ? 10 : 250);
11121         
11122         
11123         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11124         
11125         if(this.typeAhead){
11126             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11127         }
11128         if(this.editable !== false){
11129             this.inputEl().on("keyup", this.onKeyUp, this);
11130         }
11131         if(this.forceSelection){
11132             this.inputEl().on('blur', this.doForce, this);
11133         }
11134         
11135         if(this.multiple){
11136             this.choices = this.el.select('ul.select2-choices', true).first();
11137             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11138         }
11139     },
11140     
11141     initTickableEvents: function()
11142     {   
11143         this.createList();
11144         
11145         if(this.hiddenName){
11146             
11147             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11148             
11149             this.hiddenField.dom.value =
11150                 this.hiddenValue !== undefined ? this.hiddenValue :
11151                 this.value !== undefined ? this.value : '';
11152
11153             // prevent input submission
11154             this.el.dom.removeAttribute('name');
11155             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11156              
11157              
11158         }
11159         
11160 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11161         
11162         this.choices = this.el.select('ul.select2-choices', true).first();
11163         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11164         if(this.triggerList){
11165             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11166         }
11167          
11168         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11169         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11170         
11171         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11172         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11173         
11174         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11175         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11176         
11177         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11178         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11179         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11180         
11181         this.okBtn.hide();
11182         this.cancelBtn.hide();
11183         
11184         var _this = this;
11185         
11186         (function(){
11187             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11188             _this.list.setWidth(lw);
11189         }).defer(100);
11190         
11191         this.list.on('mouseover', this.onViewOver, this);
11192         this.list.on('mousemove', this.onViewMove, this);
11193         
11194         this.list.on('scroll', this.onViewScroll, this);
11195         
11196         if(!this.tpl){
11197             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>';
11198         }
11199
11200         this.view = new Roo.View(this.list, this.tpl, {
11201             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11202         });
11203         
11204         //this.view.wrapEl.setDisplayed(false);
11205         this.view.on('click', this.onViewClick, this);
11206         
11207         
11208         
11209         this.store.on('beforeload', this.onBeforeLoad, this);
11210         this.store.on('load', this.onLoad, this);
11211         this.store.on('loadexception', this.onLoadException, this);
11212         
11213 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11214 //            "up" : function(e){
11215 //                this.inKeyMode = true;
11216 //                this.selectPrev();
11217 //            },
11218 //
11219 //            "down" : function(e){
11220 //                if(!this.isExpanded()){
11221 //                    this.onTriggerClick();
11222 //                }else{
11223 //                    this.inKeyMode = true;
11224 //                    this.selectNext();
11225 //                }
11226 //            },
11227 //
11228 //            "enter" : function(e){
11229 ////                this.onViewClick();
11230 //                //return true;
11231 //                this.collapse();
11232 //                
11233 //                if(this.fireEvent("specialkey", this, e)){
11234 //                    this.onViewClick(false);
11235 //                }
11236 //                
11237 //                return true;
11238 //            },
11239 //
11240 //            "esc" : function(e){
11241 //                this.collapse();
11242 //            },
11243 //
11244 //            "tab" : function(e){
11245 //                this.collapse();
11246 //                
11247 //                if(this.fireEvent("specialkey", this, e)){
11248 //                    this.onViewClick(false);
11249 //                }
11250 //                
11251 //                return true;
11252 //            },
11253 //
11254 //            scope : this,
11255 //
11256 //            doRelay : function(foo, bar, hname){
11257 //                if(hname == 'down' || this.scope.isExpanded()){
11258 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11259 //                }
11260 //                return true;
11261 //            },
11262 //
11263 //            forceKeyDown: true
11264 //        });
11265         
11266         
11267         this.queryDelay = Math.max(this.queryDelay || 10,
11268                 this.mode == 'local' ? 10 : 250);
11269         
11270         
11271         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11272         
11273         if(this.typeAhead){
11274             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11275         }
11276     },
11277
11278     onDestroy : function(){
11279         if(this.view){
11280             this.view.setStore(null);
11281             this.view.el.removeAllListeners();
11282             this.view.el.remove();
11283             this.view.purgeListeners();
11284         }
11285         if(this.list){
11286             this.list.dom.innerHTML  = '';
11287         }
11288         
11289         if(this.store){
11290             this.store.un('beforeload', this.onBeforeLoad, this);
11291             this.store.un('load', this.onLoad, this);
11292             this.store.un('loadexception', this.onLoadException, this);
11293         }
11294         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11295     },
11296
11297     // private
11298     fireKey : function(e){
11299         if(e.isNavKeyPress() && !this.list.isVisible()){
11300             this.fireEvent("specialkey", this, e);
11301         }
11302     },
11303
11304     // private
11305     onResize: function(w, h){
11306 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11307 //        
11308 //        if(typeof w != 'number'){
11309 //            // we do not handle it!?!?
11310 //            return;
11311 //        }
11312 //        var tw = this.trigger.getWidth();
11313 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11314 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11315 //        var x = w - tw;
11316 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11317 //            
11318 //        //this.trigger.setStyle('left', x+'px');
11319 //        
11320 //        if(this.list && this.listWidth === undefined){
11321 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11322 //            this.list.setWidth(lw);
11323 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11324 //        }
11325         
11326     
11327         
11328     },
11329
11330     /**
11331      * Allow or prevent the user from directly editing the field text.  If false is passed,
11332      * the user will only be able to select from the items defined in the dropdown list.  This method
11333      * is the runtime equivalent of setting the 'editable' config option at config time.
11334      * @param {Boolean} value True to allow the user to directly edit the field text
11335      */
11336     setEditable : function(value){
11337         if(value == this.editable){
11338             return;
11339         }
11340         this.editable = value;
11341         if(!value){
11342             this.inputEl().dom.setAttribute('readOnly', true);
11343             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11344             this.inputEl().addClass('x-combo-noedit');
11345         }else{
11346             this.inputEl().dom.setAttribute('readOnly', false);
11347             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11348             this.inputEl().removeClass('x-combo-noedit');
11349         }
11350     },
11351
11352     // private
11353     
11354     onBeforeLoad : function(combo,opts){
11355         if(!this.hasFocus){
11356             return;
11357         }
11358          if (!opts.add) {
11359             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11360          }
11361         this.restrictHeight();
11362         this.selectedIndex = -1;
11363     },
11364
11365     // private
11366     onLoad : function(){
11367         
11368         this.hasQuery = false;
11369         
11370         if(!this.hasFocus){
11371             return;
11372         }
11373         
11374         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11375             this.loading.hide();
11376         }
11377         
11378         if(this.store.getCount() > 0){
11379             this.expand();
11380 //            this.restrictHeight();
11381             if(this.lastQuery == this.allQuery){
11382                 if(this.editable && !this.tickable){
11383                     this.inputEl().dom.select();
11384                 }
11385                 
11386                 if(
11387                     !this.selectByValue(this.value, true) &&
11388                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11389                     this.store.lastOptions.add != true)
11390                 ){
11391                     this.select(0, true);
11392                 }
11393             }else{
11394                 if(this.autoFocus){
11395                     this.selectNext();
11396                 }
11397                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11398                     this.taTask.delay(this.typeAheadDelay);
11399                 }
11400             }
11401         }else{
11402             this.onEmptyResults();
11403         }
11404         
11405         //this.el.focus();
11406     },
11407     // private
11408     onLoadException : function()
11409     {
11410         this.hasQuery = false;
11411         
11412         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11413             this.loading.hide();
11414         }
11415         
11416         this.collapse();
11417         Roo.log(this.store.reader.jsonData);
11418         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11419             // fixme
11420             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11421         }
11422         
11423         
11424     },
11425     // private
11426     onTypeAhead : function(){
11427         if(this.store.getCount() > 0){
11428             var r = this.store.getAt(0);
11429             var newValue = r.data[this.displayField];
11430             var len = newValue.length;
11431             var selStart = this.getRawValue().length;
11432             
11433             if(selStart != len){
11434                 this.setRawValue(newValue);
11435                 this.selectText(selStart, newValue.length);
11436             }
11437         }
11438     },
11439
11440     // private
11441     onSelect : function(record, index){
11442         
11443         if(this.fireEvent('beforeselect', this, record, index) !== false){
11444         
11445             this.setFromData(index > -1 ? record.data : false);
11446             
11447             this.collapse();
11448             this.fireEvent('select', this, record, index);
11449         }
11450     },
11451
11452     /**
11453      * Returns the currently selected field value or empty string if no value is set.
11454      * @return {String} value The selected value
11455      */
11456     getValue : function(){
11457         
11458         if(this.multiple){
11459             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11460         }
11461         
11462         if(this.valueField){
11463             return typeof this.value != 'undefined' ? this.value : '';
11464         }else{
11465             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11466         }
11467     },
11468
11469     /**
11470      * Clears any text/value currently set in the field
11471      */
11472     clearValue : function(){
11473         if(this.hiddenField){
11474             this.hiddenField.dom.value = '';
11475         }
11476         this.value = '';
11477         this.setRawValue('');
11478         this.lastSelectionText = '';
11479         
11480     },
11481
11482     /**
11483      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11484      * will be displayed in the field.  If the value does not match the data value of an existing item,
11485      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11486      * Otherwise the field will be blank (although the value will still be set).
11487      * @param {String} value The value to match
11488      */
11489     setValue : function(v){
11490         if(this.multiple){
11491             this.syncValue();
11492             return;
11493         }
11494         
11495         var text = v;
11496         if(this.valueField){
11497             var r = this.findRecord(this.valueField, v);
11498             if(r){
11499                 text = r.data[this.displayField];
11500             }else if(this.valueNotFoundText !== undefined){
11501                 text = this.valueNotFoundText;
11502             }
11503         }
11504         this.lastSelectionText = text;
11505         if(this.hiddenField){
11506             this.hiddenField.dom.value = v;
11507         }
11508         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11509         this.value = v;
11510     },
11511     /**
11512      * @property {Object} the last set data for the element
11513      */
11514     
11515     lastData : false,
11516     /**
11517      * Sets the value of the field based on a object which is related to the record format for the store.
11518      * @param {Object} value the value to set as. or false on reset?
11519      */
11520     setFromData : function(o){
11521         
11522         if(this.multiple){
11523             this.addItem(o);
11524             return;
11525         }
11526             
11527         var dv = ''; // display value
11528         var vv = ''; // value value..
11529         this.lastData = o;
11530         if (this.displayField) {
11531             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11532         } else {
11533             // this is an error condition!!!
11534             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11535         }
11536         
11537         if(this.valueField){
11538             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11539         }
11540         
11541         if(this.hiddenField){
11542             this.hiddenField.dom.value = vv;
11543             
11544             this.lastSelectionText = dv;
11545             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11546             this.value = vv;
11547             return;
11548         }
11549         // no hidden field.. - we store the value in 'value', but still display
11550         // display field!!!!
11551         this.lastSelectionText = dv;
11552         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11553         this.value = vv;
11554         
11555         
11556     },
11557     // private
11558     reset : function(){
11559         // overridden so that last data is reset..
11560         this.setValue(this.originalValue);
11561         this.clearInvalid();
11562         this.lastData = false;
11563         if (this.view) {
11564             this.view.clearSelections();
11565         }
11566     },
11567     // private
11568     findRecord : function(prop, value){
11569         var record;
11570         if(this.store.getCount() > 0){
11571             this.store.each(function(r){
11572                 if(r.data[prop] == value){
11573                     record = r;
11574                     return false;
11575                 }
11576                 return true;
11577             });
11578         }
11579         return record;
11580     },
11581     
11582     getName: function()
11583     {
11584         // returns hidden if it's set..
11585         if (!this.rendered) {return ''};
11586         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11587         
11588     },
11589     // private
11590     onViewMove : function(e, t){
11591         this.inKeyMode = false;
11592     },
11593
11594     // private
11595     onViewOver : function(e, t){
11596         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11597             return;
11598         }
11599         var item = this.view.findItemFromChild(t);
11600         
11601         if(item){
11602             var index = this.view.indexOf(item);
11603             this.select(index, false);
11604         }
11605     },
11606
11607     // private
11608     onViewClick : function(view, doFocus, el, e)
11609     {
11610         var index = this.view.getSelectedIndexes()[0];
11611         
11612         var r = this.store.getAt(index);
11613         
11614         if(this.tickable){
11615             
11616             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11617                 return;
11618             }
11619             
11620             var rm = false;
11621             var _this = this;
11622             
11623             Roo.each(this.tickItems, function(v,k){
11624                 
11625                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11626                     _this.tickItems.splice(k, 1);
11627                     rm = true;
11628                     return;
11629                 }
11630             })
11631             
11632             if(rm){
11633                 return;
11634             }
11635             
11636             this.tickItems.push(r.data);
11637             return;
11638         }
11639         
11640         if(r){
11641             this.onSelect(r, index);
11642         }
11643         if(doFocus !== false && !this.blockFocus){
11644             this.inputEl().focus();
11645         }
11646     },
11647
11648     // private
11649     restrictHeight : function(){
11650         //this.innerList.dom.style.height = '';
11651         //var inner = this.innerList.dom;
11652         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11653         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11654         //this.list.beginUpdate();
11655         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11656         this.list.alignTo(this.inputEl(), this.listAlign);
11657         this.list.alignTo(this.inputEl(), this.listAlign);
11658         //this.list.endUpdate();
11659     },
11660
11661     // private
11662     onEmptyResults : function(){
11663         this.collapse();
11664     },
11665
11666     /**
11667      * Returns true if the dropdown list is expanded, else false.
11668      */
11669     isExpanded : function(){
11670         return this.list.isVisible();
11671     },
11672
11673     /**
11674      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11675      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11676      * @param {String} value The data value of the item to select
11677      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11678      * selected item if it is not currently in view (defaults to true)
11679      * @return {Boolean} True if the value matched an item in the list, else false
11680      */
11681     selectByValue : function(v, scrollIntoView){
11682         if(v !== undefined && v !== null){
11683             var r = this.findRecord(this.valueField || this.displayField, v);
11684             if(r){
11685                 this.select(this.store.indexOf(r), scrollIntoView);
11686                 return true;
11687             }
11688         }
11689         return false;
11690     },
11691
11692     /**
11693      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11694      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11695      * @param {Number} index The zero-based index of the list item to select
11696      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11697      * selected item if it is not currently in view (defaults to true)
11698      */
11699     select : function(index, scrollIntoView){
11700         this.selectedIndex = index;
11701         this.view.select(index);
11702         if(scrollIntoView !== false){
11703             var el = this.view.getNode(index);
11704             if(el && !this.multiple && !this.tickable){
11705                 this.list.scrollChildIntoView(el, false);
11706             }
11707         }
11708     },
11709
11710     // private
11711     selectNext : function(){
11712         var ct = this.store.getCount();
11713         if(ct > 0){
11714             if(this.selectedIndex == -1){
11715                 this.select(0);
11716             }else if(this.selectedIndex < ct-1){
11717                 this.select(this.selectedIndex+1);
11718             }
11719         }
11720     },
11721
11722     // private
11723     selectPrev : function(){
11724         var ct = this.store.getCount();
11725         if(ct > 0){
11726             if(this.selectedIndex == -1){
11727                 this.select(0);
11728             }else if(this.selectedIndex != 0){
11729                 this.select(this.selectedIndex-1);
11730             }
11731         }
11732     },
11733
11734     // private
11735     onKeyUp : function(e){
11736         if(this.editable !== false && !e.isSpecialKey()){
11737             this.lastKey = e.getKey();
11738             this.dqTask.delay(this.queryDelay);
11739         }
11740     },
11741
11742     // private
11743     validateBlur : function(){
11744         return !this.list || !this.list.isVisible();   
11745     },
11746
11747     // private
11748     initQuery : function(){
11749         this.doQuery(this.getRawValue());
11750     },
11751
11752     // private
11753     doForce : function(){
11754         if(this.inputEl().dom.value.length > 0){
11755             this.inputEl().dom.value =
11756                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11757              
11758         }
11759     },
11760
11761     /**
11762      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11763      * query allowing the query action to be canceled if needed.
11764      * @param {String} query The SQL query to execute
11765      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11766      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11767      * saved in the current store (defaults to false)
11768      */
11769     doQuery : function(q, forceAll){
11770         
11771         if(q === undefined || q === null){
11772             q = '';
11773         }
11774         var qe = {
11775             query: q,
11776             forceAll: forceAll,
11777             combo: this,
11778             cancel:false
11779         };
11780         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11781             return false;
11782         }
11783         q = qe.query;
11784         
11785         forceAll = qe.forceAll;
11786         if(forceAll === true || (q.length >= this.minChars)){
11787             
11788             this.hasQuery = true;
11789             
11790             if(this.lastQuery != q || this.alwaysQuery){
11791                 this.lastQuery = q;
11792                 if(this.mode == 'local'){
11793                     this.selectedIndex = -1;
11794                     if(forceAll){
11795                         this.store.clearFilter();
11796                     }else{
11797                         this.store.filter(this.displayField, q);
11798                     }
11799                     this.onLoad();
11800                 }else{
11801                     this.store.baseParams[this.queryParam] = q;
11802                     
11803                     var options = {params : this.getParams(q)};
11804                     
11805                     if(this.loadNext){
11806                         options.add = true;
11807                         options.params.start = this.page * this.pageSize;
11808                     }
11809                     
11810                     this.store.load(options);
11811                     /*
11812                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11813                      *  we should expand the list on onLoad
11814                      *  so command out it
11815                      */
11816 //                    this.expand();
11817                 }
11818             }else{
11819                 this.selectedIndex = -1;
11820                 this.onLoad();   
11821             }
11822         }
11823         
11824         this.loadNext = false;
11825     },
11826
11827     // private
11828     getParams : function(q){
11829         var p = {};
11830         //p[this.queryParam] = q;
11831         
11832         if(this.pageSize){
11833             p.start = 0;
11834             p.limit = this.pageSize;
11835         }
11836         return p;
11837     },
11838
11839     /**
11840      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11841      */
11842     collapse : function(){
11843         if(!this.isExpanded()){
11844             return;
11845         }
11846         
11847         this.list.hide();
11848         
11849         if(this.tickable){
11850             this.okBtn.hide();
11851             this.cancelBtn.hide();
11852             this.trigger.show();
11853         }
11854         
11855         Roo.get(document).un('mousedown', this.collapseIf, this);
11856         Roo.get(document).un('mousewheel', this.collapseIf, this);
11857         if (!this.editable) {
11858             Roo.get(document).un('keydown', this.listKeyPress, this);
11859         }
11860         this.fireEvent('collapse', this);
11861     },
11862
11863     // private
11864     collapseIf : function(e){
11865         var in_combo  = e.within(this.el);
11866         var in_list =  e.within(this.list);
11867         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11868         
11869         if (in_combo || in_list || is_list) {
11870             //e.stopPropagation();
11871             return;
11872         }
11873         
11874         if(this.tickable){
11875             this.onTickableFooterButtonClick(e, false, false);
11876         }
11877
11878         this.collapse();
11879         
11880     },
11881
11882     /**
11883      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11884      */
11885     expand : function(){
11886        
11887         if(this.isExpanded() || !this.hasFocus){
11888             return;
11889         }
11890         
11891         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11892         this.list.setWidth(lw);
11893         
11894         
11895          Roo.log('expand');
11896         
11897         this.list.show();
11898         
11899         this.restrictHeight();
11900         
11901         if(this.tickable){
11902             
11903             this.tickItems = Roo.apply([], this.item);
11904             
11905             this.okBtn.show();
11906             this.cancelBtn.show();
11907             this.trigger.hide();
11908             
11909         }
11910         
11911         Roo.get(document).on('mousedown', this.collapseIf, this);
11912         Roo.get(document).on('mousewheel', this.collapseIf, this);
11913         if (!this.editable) {
11914             Roo.get(document).on('keydown', this.listKeyPress, this);
11915         }
11916         
11917         this.fireEvent('expand', this);
11918     },
11919
11920     // private
11921     // Implements the default empty TriggerField.onTriggerClick function
11922     onTriggerClick : function(e)
11923     {
11924         Roo.log('trigger click');
11925         
11926         if(this.disabled || !this.triggerList){
11927             return;
11928         }
11929         
11930         this.page = 0;
11931         this.loadNext = false;
11932         
11933         if(this.isExpanded()){
11934             this.collapse();
11935             if (!this.blockFocus) {
11936                 this.inputEl().focus();
11937             }
11938             
11939         }else {
11940             this.hasFocus = true;
11941             if(this.triggerAction == 'all') {
11942                 this.doQuery(this.allQuery, true);
11943             } else {
11944                 this.doQuery(this.getRawValue());
11945             }
11946             if (!this.blockFocus) {
11947                 this.inputEl().focus();
11948             }
11949         }
11950     },
11951     
11952     onTickableTriggerClick : function(e)
11953     {
11954         if(this.disabled){
11955             return;
11956         }
11957         
11958         this.page = 0;
11959         this.loadNext = false;
11960         this.hasFocus = true;
11961         
11962         if(this.triggerAction == 'all') {
11963             this.doQuery(this.allQuery, true);
11964         } else {
11965             this.doQuery(this.getRawValue());
11966         }
11967     },
11968     
11969     onSearchFieldClick : function(e)
11970     {
11971         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11972             return;
11973         }
11974         
11975         this.page = 0;
11976         this.loadNext = false;
11977         this.hasFocus = true;
11978         
11979         if(this.triggerAction == 'all') {
11980             this.doQuery(this.allQuery, true);
11981         } else {
11982             this.doQuery(this.getRawValue());
11983         }
11984     },
11985     
11986     listKeyPress : function(e)
11987     {
11988         //Roo.log('listkeypress');
11989         // scroll to first matching element based on key pres..
11990         if (e.isSpecialKey()) {
11991             return false;
11992         }
11993         var k = String.fromCharCode(e.getKey()).toUpperCase();
11994         //Roo.log(k);
11995         var match  = false;
11996         var csel = this.view.getSelectedNodes();
11997         var cselitem = false;
11998         if (csel.length) {
11999             var ix = this.view.indexOf(csel[0]);
12000             cselitem  = this.store.getAt(ix);
12001             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12002                 cselitem = false;
12003             }
12004             
12005         }
12006         
12007         this.store.each(function(v) { 
12008             if (cselitem) {
12009                 // start at existing selection.
12010                 if (cselitem.id == v.id) {
12011                     cselitem = false;
12012                 }
12013                 return true;
12014             }
12015                 
12016             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12017                 match = this.store.indexOf(v);
12018                 return false;
12019             }
12020             return true;
12021         }, this);
12022         
12023         if (match === false) {
12024             return true; // no more action?
12025         }
12026         // scroll to?
12027         this.view.select(match);
12028         var sn = Roo.get(this.view.getSelectedNodes()[0])
12029         sn.scrollIntoView(sn.dom.parentNode, false);
12030     },
12031     
12032     onViewScroll : function(e, t){
12033         
12034         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){
12035             return;
12036         }
12037         
12038         this.hasQuery = true;
12039         
12040         this.loading = this.list.select('.loading', true).first();
12041         
12042         if(this.loading === null){
12043             this.list.createChild({
12044                 tag: 'div',
12045                 cls: 'loading select2-more-results select2-active',
12046                 html: 'Loading more results...'
12047             })
12048             
12049             this.loading = this.list.select('.loading', true).first();
12050             
12051             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12052             
12053             this.loading.hide();
12054         }
12055         
12056         this.loading.show();
12057         
12058         var _combo = this;
12059         
12060         this.page++;
12061         this.loadNext = true;
12062         
12063         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12064         
12065         return;
12066     },
12067     
12068     addItem : function(o)
12069     {   
12070         var dv = ''; // display value
12071         
12072         if (this.displayField) {
12073             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12074         } else {
12075             // this is an error condition!!!
12076             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12077         }
12078         
12079         if(!dv.length){
12080             return;
12081         }
12082         
12083         var choice = this.choices.createChild({
12084             tag: 'li',
12085             cls: 'select2-search-choice',
12086             cn: [
12087                 {
12088                     tag: 'div',
12089                     html: dv
12090                 },
12091                 {
12092                     tag: 'a',
12093                     href: '#',
12094                     cls: 'select2-search-choice-close',
12095                     tabindex: '-1'
12096                 }
12097             ]
12098             
12099         }, this.searchField);
12100         
12101         var close = choice.select('a.select2-search-choice-close', true).first()
12102         
12103         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12104         
12105         this.item.push(o);
12106         
12107         this.lastData = o;
12108         
12109         this.syncValue();
12110         
12111         this.inputEl().dom.value = '';
12112         
12113     },
12114     
12115     onRemoveItem : function(e, _self, o)
12116     {
12117         e.preventDefault();
12118         var index = this.item.indexOf(o.data) * 1;
12119         
12120         if( index < 0){
12121             Roo.log('not this item?!');
12122             return;
12123         }
12124         
12125         this.item.splice(index, 1);
12126         o.item.remove();
12127         
12128         this.syncValue();
12129         
12130         this.fireEvent('remove', this, e);
12131         
12132     },
12133     
12134     syncValue : function()
12135     {
12136         if(!this.item.length){
12137             this.clearValue();
12138             return;
12139         }
12140             
12141         var value = [];
12142         var _this = this;
12143         Roo.each(this.item, function(i){
12144             if(_this.valueField){
12145                 value.push(i[_this.valueField]);
12146                 return;
12147             }
12148
12149             value.push(i);
12150         });
12151
12152         this.value = value.join(',');
12153
12154         if(this.hiddenField){
12155             this.hiddenField.dom.value = this.value;
12156         }
12157     },
12158     
12159     clearItem : function()
12160     {
12161         if(!this.multiple){
12162             return;
12163         }
12164         
12165         this.item = [];
12166         
12167         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12168            c.remove();
12169         });
12170         
12171         this.syncValue();
12172     },
12173     
12174     inputEl: function ()
12175     {
12176         if(this.tickable){
12177             return this.searchField;
12178         }
12179         return this.el.select('input.form-control',true).first();
12180     },
12181     
12182     
12183     onTickableFooterButtonClick : function(e, btn, el)
12184     {
12185         e.preventDefault();
12186         
12187         if(btn && btn.name == 'cancel'){
12188             this.tickItems = Roo.apply([], this.item);
12189             this.collapse();
12190             return;
12191         }
12192         
12193         this.clearItem();
12194         
12195         var _this = this;
12196         
12197         Roo.each(this.tickItems, function(o){
12198             _this.addItem(o);
12199         });
12200         
12201         this.collapse();
12202         
12203     },
12204     
12205     validate : function()
12206     {
12207         var v = this.getRawValue();
12208         
12209         if(this.multiple){
12210             v = this.getValue();
12211         }
12212         
12213         if(this.disabled || this.validateValue(v)){
12214             this.clearInvalid();
12215             return true;
12216         }
12217         return false;
12218     }
12219     
12220     
12221
12222     /** 
12223     * @cfg {Boolean} grow 
12224     * @hide 
12225     */
12226     /** 
12227     * @cfg {Number} growMin 
12228     * @hide 
12229     */
12230     /** 
12231     * @cfg {Number} growMax 
12232     * @hide 
12233     */
12234     /**
12235      * @hide
12236      * @method autoSize
12237      */
12238 });
12239 /*
12240  * Based on:
12241  * Ext JS Library 1.1.1
12242  * Copyright(c) 2006-2007, Ext JS, LLC.
12243  *
12244  * Originally Released Under LGPL - original licence link has changed is not relivant.
12245  *
12246  * Fork - LGPL
12247  * <script type="text/javascript">
12248  */
12249
12250 /**
12251  * @class Roo.View
12252  * @extends Roo.util.Observable
12253  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12254  * This class also supports single and multi selection modes. <br>
12255  * Create a data model bound view:
12256  <pre><code>
12257  var store = new Roo.data.Store(...);
12258
12259  var view = new Roo.View({
12260     el : "my-element",
12261     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12262  
12263     singleSelect: true,
12264     selectedClass: "ydataview-selected",
12265     store: store
12266  });
12267
12268  // listen for node click?
12269  view.on("click", function(vw, index, node, e){
12270  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12271  });
12272
12273  // load XML data
12274  dataModel.load("foobar.xml");
12275  </code></pre>
12276  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12277  * <br><br>
12278  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12279  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12280  * 
12281  * Note: old style constructor is still suported (container, template, config)
12282  * 
12283  * @constructor
12284  * Create a new View
12285  * @param {Object} config The config object
12286  * 
12287  */
12288 Roo.View = function(config, depreciated_tpl, depreciated_config){
12289     
12290     this.parent = false;
12291     
12292     if (typeof(depreciated_tpl) == 'undefined') {
12293         // new way.. - universal constructor.
12294         Roo.apply(this, config);
12295         this.el  = Roo.get(this.el);
12296     } else {
12297         // old format..
12298         this.el  = Roo.get(config);
12299         this.tpl = depreciated_tpl;
12300         Roo.apply(this, depreciated_config);
12301     }
12302     this.wrapEl  = this.el.wrap().wrap();
12303     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12304     
12305     
12306     if(typeof(this.tpl) == "string"){
12307         this.tpl = new Roo.Template(this.tpl);
12308     } else {
12309         // support xtype ctors..
12310         this.tpl = new Roo.factory(this.tpl, Roo);
12311     }
12312     
12313     
12314     this.tpl.compile();
12315     
12316     /** @private */
12317     this.addEvents({
12318         /**
12319          * @event beforeclick
12320          * Fires before a click is processed. Returns false to cancel the default action.
12321          * @param {Roo.View} this
12322          * @param {Number} index The index of the target node
12323          * @param {HTMLElement} node The target node
12324          * @param {Roo.EventObject} e The raw event object
12325          */
12326             "beforeclick" : true,
12327         /**
12328          * @event click
12329          * Fires when a template node is clicked.
12330          * @param {Roo.View} this
12331          * @param {Number} index The index of the target node
12332          * @param {HTMLElement} node The target node
12333          * @param {Roo.EventObject} e The raw event object
12334          */
12335             "click" : true,
12336         /**
12337          * @event dblclick
12338          * Fires when a template node is double clicked.
12339          * @param {Roo.View} this
12340          * @param {Number} index The index of the target node
12341          * @param {HTMLElement} node The target node
12342          * @param {Roo.EventObject} e The raw event object
12343          */
12344             "dblclick" : true,
12345         /**
12346          * @event contextmenu
12347          * Fires when a template node is right clicked.
12348          * @param {Roo.View} this
12349          * @param {Number} index The index of the target node
12350          * @param {HTMLElement} node The target node
12351          * @param {Roo.EventObject} e The raw event object
12352          */
12353             "contextmenu" : true,
12354         /**
12355          * @event selectionchange
12356          * Fires when the selected nodes change.
12357          * @param {Roo.View} this
12358          * @param {Array} selections Array of the selected nodes
12359          */
12360             "selectionchange" : true,
12361     
12362         /**
12363          * @event beforeselect
12364          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12365          * @param {Roo.View} this
12366          * @param {HTMLElement} node The node to be selected
12367          * @param {Array} selections Array of currently selected nodes
12368          */
12369             "beforeselect" : true,
12370         /**
12371          * @event preparedata
12372          * Fires on every row to render, to allow you to change the data.
12373          * @param {Roo.View} this
12374          * @param {Object} data to be rendered (change this)
12375          */
12376           "preparedata" : true
12377           
12378           
12379         });
12380
12381
12382
12383     this.el.on({
12384         "click": this.onClick,
12385         "dblclick": this.onDblClick,
12386         "contextmenu": this.onContextMenu,
12387         scope:this
12388     });
12389
12390     this.selections = [];
12391     this.nodes = [];
12392     this.cmp = new Roo.CompositeElementLite([]);
12393     if(this.store){
12394         this.store = Roo.factory(this.store, Roo.data);
12395         this.setStore(this.store, true);
12396     }
12397     
12398     if ( this.footer && this.footer.xtype) {
12399            
12400          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12401         
12402         this.footer.dataSource = this.store
12403         this.footer.container = fctr;
12404         this.footer = Roo.factory(this.footer, Roo);
12405         fctr.insertFirst(this.el);
12406         
12407         // this is a bit insane - as the paging toolbar seems to detach the el..
12408 //        dom.parentNode.parentNode.parentNode
12409          // they get detached?
12410     }
12411     
12412     
12413     Roo.View.superclass.constructor.call(this);
12414     
12415     
12416 };
12417
12418 Roo.extend(Roo.View, Roo.util.Observable, {
12419     
12420      /**
12421      * @cfg {Roo.data.Store} store Data store to load data from.
12422      */
12423     store : false,
12424     
12425     /**
12426      * @cfg {String|Roo.Element} el The container element.
12427      */
12428     el : '',
12429     
12430     /**
12431      * @cfg {String|Roo.Template} tpl The template used by this View 
12432      */
12433     tpl : false,
12434     /**
12435      * @cfg {String} dataName the named area of the template to use as the data area
12436      *                          Works with domtemplates roo-name="name"
12437      */
12438     dataName: false,
12439     /**
12440      * @cfg {String} selectedClass The css class to add to selected nodes
12441      */
12442     selectedClass : "x-view-selected",
12443      /**
12444      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12445      */
12446     emptyText : "",
12447     
12448     /**
12449      * @cfg {String} text to display on mask (default Loading)
12450      */
12451     mask : false,
12452     /**
12453      * @cfg {Boolean} multiSelect Allow multiple selection
12454      */
12455     multiSelect : false,
12456     /**
12457      * @cfg {Boolean} singleSelect Allow single selection
12458      */
12459     singleSelect:  false,
12460     
12461     /**
12462      * @cfg {Boolean} toggleSelect - selecting 
12463      */
12464     toggleSelect : false,
12465     
12466     /**
12467      * @cfg {Boolean} tickable - selecting 
12468      */
12469     tickable : false,
12470     
12471     /**
12472      * Returns the element this view is bound to.
12473      * @return {Roo.Element}
12474      */
12475     getEl : function(){
12476         return this.wrapEl;
12477     },
12478     
12479     
12480
12481     /**
12482      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12483      */
12484     refresh : function(){
12485         //Roo.log('refresh');
12486         var t = this.tpl;
12487         
12488         // if we are using something like 'domtemplate', then
12489         // the what gets used is:
12490         // t.applySubtemplate(NAME, data, wrapping data..)
12491         // the outer template then get' applied with
12492         //     the store 'extra data'
12493         // and the body get's added to the
12494         //      roo-name="data" node?
12495         //      <span class='roo-tpl-{name}'></span> ?????
12496         
12497         
12498         
12499         this.clearSelections();
12500         this.el.update("");
12501         var html = [];
12502         var records = this.store.getRange();
12503         if(records.length < 1) {
12504             
12505             // is this valid??  = should it render a template??
12506             
12507             this.el.update(this.emptyText);
12508             return;
12509         }
12510         var el = this.el;
12511         if (this.dataName) {
12512             this.el.update(t.apply(this.store.meta)); //????
12513             el = this.el.child('.roo-tpl-' + this.dataName);
12514         }
12515         
12516         for(var i = 0, len = records.length; i < len; i++){
12517             var data = this.prepareData(records[i].data, i, records[i]);
12518             this.fireEvent("preparedata", this, data, i, records[i]);
12519             
12520             var d = Roo.apply({}, data);
12521             
12522             if(this.tickable){
12523                 Roo.apply(d, {'roo-id' : Roo.id()});
12524                 
12525                 var _this = this;
12526             
12527                 Roo.each(this.parent.item, function(item){
12528                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12529                         return;
12530                     }
12531                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12532                 });
12533             }
12534             
12535             html[html.length] = Roo.util.Format.trim(
12536                 this.dataName ?
12537                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12538                     t.apply(d)
12539             );
12540         }
12541         
12542         
12543         
12544         el.update(html.join(""));
12545         this.nodes = el.dom.childNodes;
12546         this.updateIndexes(0);
12547     },
12548     
12549
12550     /**
12551      * Function to override to reformat the data that is sent to
12552      * the template for each node.
12553      * DEPRICATED - use the preparedata event handler.
12554      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12555      * a JSON object for an UpdateManager bound view).
12556      */
12557     prepareData : function(data, index, record)
12558     {
12559         this.fireEvent("preparedata", this, data, index, record);
12560         return data;
12561     },
12562
12563     onUpdate : function(ds, record){
12564         // Roo.log('on update');   
12565         this.clearSelections();
12566         var index = this.store.indexOf(record);
12567         var n = this.nodes[index];
12568         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12569         n.parentNode.removeChild(n);
12570         this.updateIndexes(index, index);
12571     },
12572
12573     
12574     
12575 // --------- FIXME     
12576     onAdd : function(ds, records, index)
12577     {
12578         //Roo.log(['on Add', ds, records, index] );        
12579         this.clearSelections();
12580         if(this.nodes.length == 0){
12581             this.refresh();
12582             return;
12583         }
12584         var n = this.nodes[index];
12585         for(var i = 0, len = records.length; i < len; i++){
12586             var d = this.prepareData(records[i].data, i, records[i]);
12587             if(n){
12588                 this.tpl.insertBefore(n, d);
12589             }else{
12590                 
12591                 this.tpl.append(this.el, d);
12592             }
12593         }
12594         this.updateIndexes(index);
12595     },
12596
12597     onRemove : function(ds, record, index){
12598        // Roo.log('onRemove');
12599         this.clearSelections();
12600         var el = this.dataName  ?
12601             this.el.child('.roo-tpl-' + this.dataName) :
12602             this.el; 
12603         
12604         el.dom.removeChild(this.nodes[index]);
12605         this.updateIndexes(index);
12606     },
12607
12608     /**
12609      * Refresh an individual node.
12610      * @param {Number} index
12611      */
12612     refreshNode : function(index){
12613         this.onUpdate(this.store, this.store.getAt(index));
12614     },
12615
12616     updateIndexes : function(startIndex, endIndex){
12617         var ns = this.nodes;
12618         startIndex = startIndex || 0;
12619         endIndex = endIndex || ns.length - 1;
12620         for(var i = startIndex; i <= endIndex; i++){
12621             ns[i].nodeIndex = i;
12622         }
12623     },
12624
12625     /**
12626      * Changes the data store this view uses and refresh the view.
12627      * @param {Store} store
12628      */
12629     setStore : function(store, initial){
12630         if(!initial && this.store){
12631             this.store.un("datachanged", this.refresh);
12632             this.store.un("add", this.onAdd);
12633             this.store.un("remove", this.onRemove);
12634             this.store.un("update", this.onUpdate);
12635             this.store.un("clear", this.refresh);
12636             this.store.un("beforeload", this.onBeforeLoad);
12637             this.store.un("load", this.onLoad);
12638             this.store.un("loadexception", this.onLoad);
12639         }
12640         if(store){
12641           
12642             store.on("datachanged", this.refresh, this);
12643             store.on("add", this.onAdd, this);
12644             store.on("remove", this.onRemove, this);
12645             store.on("update", this.onUpdate, this);
12646             store.on("clear", this.refresh, this);
12647             store.on("beforeload", this.onBeforeLoad, this);
12648             store.on("load", this.onLoad, this);
12649             store.on("loadexception", this.onLoad, this);
12650         }
12651         
12652         if(store){
12653             this.refresh();
12654         }
12655     },
12656     /**
12657      * onbeforeLoad - masks the loading area.
12658      *
12659      */
12660     onBeforeLoad : function(store,opts)
12661     {
12662          //Roo.log('onBeforeLoad');   
12663         if (!opts.add) {
12664             this.el.update("");
12665         }
12666         this.el.mask(this.mask ? this.mask : "Loading" ); 
12667     },
12668     onLoad : function ()
12669     {
12670         this.el.unmask();
12671     },
12672     
12673
12674     /**
12675      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12676      * @param {HTMLElement} node
12677      * @return {HTMLElement} The template node
12678      */
12679     findItemFromChild : function(node){
12680         var el = this.dataName  ?
12681             this.el.child('.roo-tpl-' + this.dataName,true) :
12682             this.el.dom; 
12683         
12684         if(!node || node.parentNode == el){
12685                     return node;
12686             }
12687             var p = node.parentNode;
12688             while(p && p != el){
12689             if(p.parentNode == el){
12690                 return p;
12691             }
12692             p = p.parentNode;
12693         }
12694             return null;
12695     },
12696
12697     /** @ignore */
12698     onClick : function(e){
12699         var item = this.findItemFromChild(e.getTarget());
12700         if(item){
12701             var index = this.indexOf(item);
12702             if(this.onItemClick(item, index, e) !== false){
12703                 this.fireEvent("click", this, index, item, e);
12704             }
12705         }else{
12706             this.clearSelections();
12707         }
12708     },
12709
12710     /** @ignore */
12711     onContextMenu : function(e){
12712         var item = this.findItemFromChild(e.getTarget());
12713         if(item){
12714             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12715         }
12716     },
12717
12718     /** @ignore */
12719     onDblClick : function(e){
12720         var item = this.findItemFromChild(e.getTarget());
12721         if(item){
12722             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12723         }
12724     },
12725
12726     onItemClick : function(item, index, e)
12727     {
12728         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12729             return false;
12730         }
12731         if (this.toggleSelect) {
12732             var m = this.isSelected(item) ? 'unselect' : 'select';
12733             //Roo.log(m);
12734             var _t = this;
12735             _t[m](item, true, false);
12736             return true;
12737         }
12738         if(this.multiSelect || this.singleSelect){
12739             if(this.multiSelect && e.shiftKey && this.lastSelection){
12740                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12741             }else{
12742                 this.select(item, this.multiSelect && e.ctrlKey);
12743                 this.lastSelection = item;
12744             }
12745             
12746             if(!this.tickable){
12747                 e.preventDefault();
12748             }
12749             
12750         }
12751         return true;
12752     },
12753
12754     /**
12755      * Get the number of selected nodes.
12756      * @return {Number}
12757      */
12758     getSelectionCount : function(){
12759         return this.selections.length;
12760     },
12761
12762     /**
12763      * Get the currently selected nodes.
12764      * @return {Array} An array of HTMLElements
12765      */
12766     getSelectedNodes : function(){
12767         return this.selections;
12768     },
12769
12770     /**
12771      * Get the indexes of the selected nodes.
12772      * @return {Array}
12773      */
12774     getSelectedIndexes : function(){
12775         var indexes = [], s = this.selections;
12776         for(var i = 0, len = s.length; i < len; i++){
12777             indexes.push(s[i].nodeIndex);
12778         }
12779         return indexes;
12780     },
12781
12782     /**
12783      * Clear all selections
12784      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12785      */
12786     clearSelections : function(suppressEvent){
12787         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12788             this.cmp.elements = this.selections;
12789             this.cmp.removeClass(this.selectedClass);
12790             this.selections = [];
12791             if(!suppressEvent){
12792                 this.fireEvent("selectionchange", this, this.selections);
12793             }
12794         }
12795     },
12796
12797     /**
12798      * Returns true if the passed node is selected
12799      * @param {HTMLElement/Number} node The node or node index
12800      * @return {Boolean}
12801      */
12802     isSelected : function(node){
12803         var s = this.selections;
12804         if(s.length < 1){
12805             return false;
12806         }
12807         node = this.getNode(node);
12808         return s.indexOf(node) !== -1;
12809     },
12810
12811     /**
12812      * Selects nodes.
12813      * @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
12814      * @param {Boolean} keepExisting (optional) true to keep existing selections
12815      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12816      */
12817     select : function(nodeInfo, keepExisting, suppressEvent){
12818         if(nodeInfo instanceof Array){
12819             if(!keepExisting){
12820                 this.clearSelections(true);
12821             }
12822             for(var i = 0, len = nodeInfo.length; i < len; i++){
12823                 this.select(nodeInfo[i], true, true);
12824             }
12825             return;
12826         } 
12827         var node = this.getNode(nodeInfo);
12828         if(!node || this.isSelected(node)){
12829             return; // already selected.
12830         }
12831         if(!keepExisting){
12832             this.clearSelections(true);
12833         }
12834         
12835         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12836             Roo.fly(node).addClass(this.selectedClass);
12837             this.selections.push(node);
12838             if(!suppressEvent){
12839                 this.fireEvent("selectionchange", this, this.selections);
12840             }
12841         }
12842         
12843         
12844     },
12845       /**
12846      * Unselects nodes.
12847      * @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
12848      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12849      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12850      */
12851     unselect : function(nodeInfo, keepExisting, suppressEvent)
12852     {
12853         if(nodeInfo instanceof Array){
12854             Roo.each(this.selections, function(s) {
12855                 this.unselect(s, nodeInfo);
12856             }, this);
12857             return;
12858         }
12859         var node = this.getNode(nodeInfo);
12860         if(!node || !this.isSelected(node)){
12861             //Roo.log("not selected");
12862             return; // not selected.
12863         }
12864         // fireevent???
12865         var ns = [];
12866         Roo.each(this.selections, function(s) {
12867             if (s == node ) {
12868                 Roo.fly(node).removeClass(this.selectedClass);
12869
12870                 return;
12871             }
12872             ns.push(s);
12873         },this);
12874         
12875         this.selections= ns;
12876         this.fireEvent("selectionchange", this, this.selections);
12877     },
12878
12879     /**
12880      * Gets a template node.
12881      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12882      * @return {HTMLElement} The node or null if it wasn't found
12883      */
12884     getNode : function(nodeInfo){
12885         if(typeof nodeInfo == "string"){
12886             return document.getElementById(nodeInfo);
12887         }else if(typeof nodeInfo == "number"){
12888             return this.nodes[nodeInfo];
12889         }
12890         return nodeInfo;
12891     },
12892
12893     /**
12894      * Gets a range template nodes.
12895      * @param {Number} startIndex
12896      * @param {Number} endIndex
12897      * @return {Array} An array of nodes
12898      */
12899     getNodes : function(start, end){
12900         var ns = this.nodes;
12901         start = start || 0;
12902         end = typeof end == "undefined" ? ns.length - 1 : end;
12903         var nodes = [];
12904         if(start <= end){
12905             for(var i = start; i <= end; i++){
12906                 nodes.push(ns[i]);
12907             }
12908         } else{
12909             for(var i = start; i >= end; i--){
12910                 nodes.push(ns[i]);
12911             }
12912         }
12913         return nodes;
12914     },
12915
12916     /**
12917      * Finds the index of the passed node
12918      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12919      * @return {Number} The index of the node or -1
12920      */
12921     indexOf : function(node){
12922         node = this.getNode(node);
12923         if(typeof node.nodeIndex == "number"){
12924             return node.nodeIndex;
12925         }
12926         var ns = this.nodes;
12927         for(var i = 0, len = ns.length; i < len; i++){
12928             if(ns[i] == node){
12929                 return i;
12930             }
12931         }
12932         return -1;
12933     }
12934 });
12935 /*
12936  * - LGPL
12937  *
12938  * based on jquery fullcalendar
12939  * 
12940  */
12941
12942 Roo.bootstrap = Roo.bootstrap || {};
12943 /**
12944  * @class Roo.bootstrap.Calendar
12945  * @extends Roo.bootstrap.Component
12946  * Bootstrap Calendar class
12947  * @cfg {Boolean} loadMask (true|false) default false
12948  * @cfg {Object} header generate the user specific header of the calendar, default false
12949
12950  * @constructor
12951  * Create a new Container
12952  * @param {Object} config The config object
12953  */
12954
12955
12956
12957 Roo.bootstrap.Calendar = function(config){
12958     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12959      this.addEvents({
12960         /**
12961              * @event select
12962              * Fires when a date is selected
12963              * @param {DatePicker} this
12964              * @param {Date} date The selected date
12965              */
12966         'select': true,
12967         /**
12968              * @event monthchange
12969              * Fires when the displayed month changes 
12970              * @param {DatePicker} this
12971              * @param {Date} date The selected month
12972              */
12973         'monthchange': true,
12974         /**
12975              * @event evententer
12976              * Fires when mouse over an event
12977              * @param {Calendar} this
12978              * @param {event} Event
12979              */
12980         'evententer': true,
12981         /**
12982              * @event eventleave
12983              * Fires when the mouse leaves an
12984              * @param {Calendar} this
12985              * @param {event}
12986              */
12987         'eventleave': true,
12988         /**
12989              * @event eventclick
12990              * Fires when the mouse click an
12991              * @param {Calendar} this
12992              * @param {event}
12993              */
12994         'eventclick': true
12995         
12996     });
12997
12998 };
12999
13000 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13001     
13002      /**
13003      * @cfg {Number} startDay
13004      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13005      */
13006     startDay : 0,
13007     
13008     loadMask : false,
13009     
13010     header : false,
13011       
13012     getAutoCreate : function(){
13013         
13014         
13015         var fc_button = function(name, corner, style, content ) {
13016             return Roo.apply({},{
13017                 tag : 'span',
13018                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13019                          (corner.length ?
13020                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13021                             ''
13022                         ),
13023                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13024                 unselectable: 'on'
13025             });
13026         };
13027         
13028         var header = {};
13029         
13030         if(!this.header){
13031             header = {
13032                 tag : 'table',
13033                 cls : 'fc-header',
13034                 style : 'width:100%',
13035                 cn : [
13036                     {
13037                         tag: 'tr',
13038                         cn : [
13039                             {
13040                                 tag : 'td',
13041                                 cls : 'fc-header-left',
13042                                 cn : [
13043                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13044                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13045                                     { tag: 'span', cls: 'fc-header-space' },
13046                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13047
13048
13049                                 ]
13050                             },
13051
13052                             {
13053                                 tag : 'td',
13054                                 cls : 'fc-header-center',
13055                                 cn : [
13056                                     {
13057                                         tag: 'span',
13058                                         cls: 'fc-header-title',
13059                                         cn : {
13060                                             tag: 'H2',
13061                                             html : 'month / year'
13062                                         }
13063                                     }
13064
13065                                 ]
13066                             },
13067                             {
13068                                 tag : 'td',
13069                                 cls : 'fc-header-right',
13070                                 cn : [
13071                               /*      fc_button('month', 'left', '', 'month' ),
13072                                     fc_button('week', '', '', 'week' ),
13073                                     fc_button('day', 'right', '', 'day' )
13074                                 */    
13075
13076                                 ]
13077                             }
13078
13079                         ]
13080                     }
13081                 ]
13082             };
13083         }
13084         
13085         header = this.header;
13086         
13087        
13088         var cal_heads = function() {
13089             var ret = [];
13090             // fixme - handle this.
13091             
13092             for (var i =0; i < Date.dayNames.length; i++) {
13093                 var d = Date.dayNames[i];
13094                 ret.push({
13095                     tag: 'th',
13096                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13097                     html : d.substring(0,3)
13098                 });
13099                 
13100             }
13101             ret[0].cls += ' fc-first';
13102             ret[6].cls += ' fc-last';
13103             return ret;
13104         };
13105         var cal_cell = function(n) {
13106             return  {
13107                 tag: 'td',
13108                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13109                 cn : [
13110                     {
13111                         cn : [
13112                             {
13113                                 cls: 'fc-day-number',
13114                                 html: 'D'
13115                             },
13116                             {
13117                                 cls: 'fc-day-content',
13118                              
13119                                 cn : [
13120                                      {
13121                                         style: 'position: relative;' // height: 17px;
13122                                     }
13123                                 ]
13124                             }
13125                             
13126                             
13127                         ]
13128                     }
13129                 ]
13130                 
13131             }
13132         };
13133         var cal_rows = function() {
13134             
13135             var ret = []
13136             for (var r = 0; r < 6; r++) {
13137                 var row= {
13138                     tag : 'tr',
13139                     cls : 'fc-week',
13140                     cn : []
13141                 };
13142                 
13143                 for (var i =0; i < Date.dayNames.length; i++) {
13144                     var d = Date.dayNames[i];
13145                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13146
13147                 }
13148                 row.cn[0].cls+=' fc-first';
13149                 row.cn[0].cn[0].style = 'min-height:90px';
13150                 row.cn[6].cls+=' fc-last';
13151                 ret.push(row);
13152                 
13153             }
13154             ret[0].cls += ' fc-first';
13155             ret[4].cls += ' fc-prev-last';
13156             ret[5].cls += ' fc-last';
13157             return ret;
13158             
13159         };
13160         
13161         var cal_table = {
13162             tag: 'table',
13163             cls: 'fc-border-separate',
13164             style : 'width:100%',
13165             cellspacing  : 0,
13166             cn : [
13167                 { 
13168                     tag: 'thead',
13169                     cn : [
13170                         { 
13171                             tag: 'tr',
13172                             cls : 'fc-first fc-last',
13173                             cn : cal_heads()
13174                         }
13175                     ]
13176                 },
13177                 { 
13178                     tag: 'tbody',
13179                     cn : cal_rows()
13180                 }
13181                   
13182             ]
13183         };
13184          
13185          var cfg = {
13186             cls : 'fc fc-ltr',
13187             cn : [
13188                 header,
13189                 {
13190                     cls : 'fc-content',
13191                     style : "position: relative;",
13192                     cn : [
13193                         {
13194                             cls : 'fc-view fc-view-month fc-grid',
13195                             style : 'position: relative',
13196                             unselectable : 'on',
13197                             cn : [
13198                                 {
13199                                     cls : 'fc-event-container',
13200                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13201                                 },
13202                                 cal_table
13203                             ]
13204                         }
13205                     ]
13206     
13207                 }
13208            ] 
13209             
13210         };
13211         
13212          
13213         
13214         return cfg;
13215     },
13216     
13217     
13218     initEvents : function()
13219     {
13220         if(!this.store){
13221             throw "can not find store for calendar";
13222         }
13223         
13224         var mark = {
13225             tag: "div",
13226             cls:"x-dlg-mask",
13227             style: "text-align:center",
13228             cn: [
13229                 {
13230                     tag: "div",
13231                     style: "background-color:white;width:50%;margin:250 auto",
13232                     cn: [
13233                         {
13234                             tag: "img",
13235                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13236                         },
13237                         {
13238                             tag: "span",
13239                             html: "Loading"
13240                         }
13241                         
13242                     ]
13243                 }
13244             ]
13245         }
13246         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13247         
13248         var size = this.el.select('.fc-content', true).first().getSize();
13249         this.maskEl.setSize(size.width, size.height);
13250         this.maskEl.enableDisplayMode("block");
13251         if(!this.loadMask){
13252             this.maskEl.hide();
13253         }
13254         
13255         this.store = Roo.factory(this.store, Roo.data);
13256         this.store.on('load', this.onLoad, this);
13257         this.store.on('beforeload', this.onBeforeLoad, this);
13258         
13259         this.resize();
13260         
13261         this.cells = this.el.select('.fc-day',true);
13262         //Roo.log(this.cells);
13263         this.textNodes = this.el.query('.fc-day-number');
13264         this.cells.addClassOnOver('fc-state-hover');
13265         
13266         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13267         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13268         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13269         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13270         
13271         this.on('monthchange', this.onMonthChange, this);
13272         
13273         this.update(new Date().clearTime());
13274     },
13275     
13276     resize : function() {
13277         var sz  = this.el.getSize();
13278         
13279         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13280         this.el.select('.fc-day-content div',true).setHeight(34);
13281     },
13282     
13283     
13284     // private
13285     showPrevMonth : function(e){
13286         this.update(this.activeDate.add("mo", -1));
13287     },
13288     showToday : function(e){
13289         this.update(new Date().clearTime());
13290     },
13291     // private
13292     showNextMonth : function(e){
13293         this.update(this.activeDate.add("mo", 1));
13294     },
13295
13296     // private
13297     showPrevYear : function(){
13298         this.update(this.activeDate.add("y", -1));
13299     },
13300
13301     // private
13302     showNextYear : function(){
13303         this.update(this.activeDate.add("y", 1));
13304     },
13305
13306     
13307    // private
13308     update : function(date)
13309     {
13310         var vd = this.activeDate;
13311         this.activeDate = date;
13312 //        if(vd && this.el){
13313 //            var t = date.getTime();
13314 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13315 //                Roo.log('using add remove');
13316 //                
13317 //                this.fireEvent('monthchange', this, date);
13318 //                
13319 //                this.cells.removeClass("fc-state-highlight");
13320 //                this.cells.each(function(c){
13321 //                   if(c.dateValue == t){
13322 //                       c.addClass("fc-state-highlight");
13323 //                       setTimeout(function(){
13324 //                            try{c.dom.firstChild.focus();}catch(e){}
13325 //                       }, 50);
13326 //                       return false;
13327 //                   }
13328 //                   return true;
13329 //                });
13330 //                return;
13331 //            }
13332 //        }
13333         
13334         var days = date.getDaysInMonth();
13335         
13336         var firstOfMonth = date.getFirstDateOfMonth();
13337         var startingPos = firstOfMonth.getDay()-this.startDay;
13338         
13339         if(startingPos < this.startDay){
13340             startingPos += 7;
13341         }
13342         
13343         var pm = date.add(Date.MONTH, -1);
13344         var prevStart = pm.getDaysInMonth()-startingPos;
13345 //        
13346         this.cells = this.el.select('.fc-day',true);
13347         this.textNodes = this.el.query('.fc-day-number');
13348         this.cells.addClassOnOver('fc-state-hover');
13349         
13350         var cells = this.cells.elements;
13351         var textEls = this.textNodes;
13352         
13353         Roo.each(cells, function(cell){
13354             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13355         });
13356         
13357         days += startingPos;
13358
13359         // convert everything to numbers so it's fast
13360         var day = 86400000;
13361         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13362         //Roo.log(d);
13363         //Roo.log(pm);
13364         //Roo.log(prevStart);
13365         
13366         var today = new Date().clearTime().getTime();
13367         var sel = date.clearTime().getTime();
13368         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13369         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13370         var ddMatch = this.disabledDatesRE;
13371         var ddText = this.disabledDatesText;
13372         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13373         var ddaysText = this.disabledDaysText;
13374         var format = this.format;
13375         
13376         var setCellClass = function(cal, cell){
13377             cell.row = 0;
13378             cell.events = [];
13379             cell.more = [];
13380             //Roo.log('set Cell Class');
13381             cell.title = "";
13382             var t = d.getTime();
13383             
13384             //Roo.log(d);
13385             
13386             cell.dateValue = t;
13387             if(t == today){
13388                 cell.className += " fc-today";
13389                 cell.className += " fc-state-highlight";
13390                 cell.title = cal.todayText;
13391             }
13392             if(t == sel){
13393                 // disable highlight in other month..
13394                 //cell.className += " fc-state-highlight";
13395                 
13396             }
13397             // disabling
13398             if(t < min) {
13399                 cell.className = " fc-state-disabled";
13400                 cell.title = cal.minText;
13401                 return;
13402             }
13403             if(t > max) {
13404                 cell.className = " fc-state-disabled";
13405                 cell.title = cal.maxText;
13406                 return;
13407             }
13408             if(ddays){
13409                 if(ddays.indexOf(d.getDay()) != -1){
13410                     cell.title = ddaysText;
13411                     cell.className = " fc-state-disabled";
13412                 }
13413             }
13414             if(ddMatch && format){
13415                 var fvalue = d.dateFormat(format);
13416                 if(ddMatch.test(fvalue)){
13417                     cell.title = ddText.replace("%0", fvalue);
13418                     cell.className = " fc-state-disabled";
13419                 }
13420             }
13421             
13422             if (!cell.initialClassName) {
13423                 cell.initialClassName = cell.dom.className;
13424             }
13425             
13426             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13427         };
13428
13429         var i = 0;
13430         
13431         for(; i < startingPos; i++) {
13432             textEls[i].innerHTML = (++prevStart);
13433             d.setDate(d.getDate()+1);
13434             
13435             cells[i].className = "fc-past fc-other-month";
13436             setCellClass(this, cells[i]);
13437         }
13438         
13439         var intDay = 0;
13440         
13441         for(; i < days; i++){
13442             intDay = i - startingPos + 1;
13443             textEls[i].innerHTML = (intDay);
13444             d.setDate(d.getDate()+1);
13445             
13446             cells[i].className = ''; // "x-date-active";
13447             setCellClass(this, cells[i]);
13448         }
13449         var extraDays = 0;
13450         
13451         for(; i < 42; i++) {
13452             textEls[i].innerHTML = (++extraDays);
13453             d.setDate(d.getDate()+1);
13454             
13455             cells[i].className = "fc-future fc-other-month";
13456             setCellClass(this, cells[i]);
13457         }
13458         
13459         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13460         
13461         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13462         
13463         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13464         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13465         
13466         if(totalRows != 6){
13467             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13468             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13469         }
13470         
13471         this.fireEvent('monthchange', this, date);
13472         
13473         
13474         /*
13475         if(!this.internalRender){
13476             var main = this.el.dom.firstChild;
13477             var w = main.offsetWidth;
13478             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13479             Roo.fly(main).setWidth(w);
13480             this.internalRender = true;
13481             // opera does not respect the auto grow header center column
13482             // then, after it gets a width opera refuses to recalculate
13483             // without a second pass
13484             if(Roo.isOpera && !this.secondPass){
13485                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13486                 this.secondPass = true;
13487                 this.update.defer(10, this, [date]);
13488             }
13489         }
13490         */
13491         
13492     },
13493     
13494     findCell : function(dt) {
13495         dt = dt.clearTime().getTime();
13496         var ret = false;
13497         this.cells.each(function(c){
13498             //Roo.log("check " +c.dateValue + '?=' + dt);
13499             if(c.dateValue == dt){
13500                 ret = c;
13501                 return false;
13502             }
13503             return true;
13504         });
13505         
13506         return ret;
13507     },
13508     
13509     findCells : function(ev) {
13510         var s = ev.start.clone().clearTime().getTime();
13511        // Roo.log(s);
13512         var e= ev.end.clone().clearTime().getTime();
13513        // Roo.log(e);
13514         var ret = [];
13515         this.cells.each(function(c){
13516              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13517             
13518             if(c.dateValue > e){
13519                 return ;
13520             }
13521             if(c.dateValue < s){
13522                 return ;
13523             }
13524             ret.push(c);
13525         });
13526         
13527         return ret;    
13528     },
13529     
13530 //    findBestRow: function(cells)
13531 //    {
13532 //        var ret = 0;
13533 //        
13534 //        for (var i =0 ; i < cells.length;i++) {
13535 //            ret  = Math.max(cells[i].rows || 0,ret);
13536 //        }
13537 //        return ret;
13538 //        
13539 //    },
13540     
13541     
13542     addItem : function(ev)
13543     {
13544         // look for vertical location slot in
13545         var cells = this.findCells(ev);
13546         
13547 //        ev.row = this.findBestRow(cells);
13548         
13549         // work out the location.
13550         
13551         var crow = false;
13552         var rows = [];
13553         for(var i =0; i < cells.length; i++) {
13554             
13555             cells[i].row = cells[0].row;
13556             
13557             if(i == 0){
13558                 cells[i].row = cells[i].row + 1;
13559             }
13560             
13561             if (!crow) {
13562                 crow = {
13563                     start : cells[i],
13564                     end :  cells[i]
13565                 };
13566                 continue;
13567             }
13568             if (crow.start.getY() == cells[i].getY()) {
13569                 // on same row.
13570                 crow.end = cells[i];
13571                 continue;
13572             }
13573             // different row.
13574             rows.push(crow);
13575             crow = {
13576                 start: cells[i],
13577                 end : cells[i]
13578             };
13579             
13580         }
13581         
13582         rows.push(crow);
13583         ev.els = [];
13584         ev.rows = rows;
13585         ev.cells = cells;
13586         
13587         cells[0].events.push(ev);
13588         
13589         this.calevents.push(ev);
13590     },
13591     
13592     clearEvents: function() {
13593         
13594         if(!this.calevents){
13595             return;
13596         }
13597         
13598         Roo.each(this.cells.elements, function(c){
13599             c.row = 0;
13600             c.events = [];
13601             c.more = [];
13602         });
13603         
13604         Roo.each(this.calevents, function(e) {
13605             Roo.each(e.els, function(el) {
13606                 el.un('mouseenter' ,this.onEventEnter, this);
13607                 el.un('mouseleave' ,this.onEventLeave, this);
13608                 el.remove();
13609             },this);
13610         },this);
13611         
13612         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13613             e.remove();
13614         });
13615         
13616     },
13617     
13618     renderEvents: function()
13619     {   
13620         var _this = this;
13621         
13622         this.cells.each(function(c) {
13623             
13624             if(c.row < 5){
13625                 return;
13626             }
13627             
13628             var ev = c.events;
13629             
13630             var r = 4;
13631             if(c.row != c.events.length){
13632                 r = 4 - (4 - (c.row - c.events.length));
13633             }
13634             
13635             c.events = ev.slice(0, r);
13636             c.more = ev.slice(r);
13637             
13638             if(c.more.length && c.more.length == 1){
13639                 c.events.push(c.more.pop());
13640             }
13641             
13642             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13643             
13644         });
13645             
13646         this.cells.each(function(c) {
13647             
13648             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13649             
13650             
13651             for (var e = 0; e < c.events.length; e++){
13652                 var ev = c.events[e];
13653                 var rows = ev.rows;
13654                 
13655                 for(var i = 0; i < rows.length; i++) {
13656                 
13657                     // how many rows should it span..
13658
13659                     var  cfg = {
13660                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13661                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13662
13663                         unselectable : "on",
13664                         cn : [
13665                             {
13666                                 cls: 'fc-event-inner',
13667                                 cn : [
13668     //                                {
13669     //                                  tag:'span',
13670     //                                  cls: 'fc-event-time',
13671     //                                  html : cells.length > 1 ? '' : ev.time
13672     //                                },
13673                                     {
13674                                       tag:'span',
13675                                       cls: 'fc-event-title',
13676                                       html : String.format('{0}', ev.title)
13677                                     }
13678
13679
13680                                 ]
13681                             },
13682                             {
13683                                 cls: 'ui-resizable-handle ui-resizable-e',
13684                                 html : '&nbsp;&nbsp;&nbsp'
13685                             }
13686
13687                         ]
13688                     };
13689
13690                     if (i == 0) {
13691                         cfg.cls += ' fc-event-start';
13692                     }
13693                     if ((i+1) == rows.length) {
13694                         cfg.cls += ' fc-event-end';
13695                     }
13696
13697                     var ctr = _this.el.select('.fc-event-container',true).first();
13698                     var cg = ctr.createChild(cfg);
13699
13700                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13701                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13702
13703                     var r = (c.more.length) ? 1 : 0;
13704                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13705                     cg.setWidth(ebox.right - sbox.x -2);
13706
13707                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13708                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13709                     cg.on('click', _this.onEventClick, _this, ev);
13710
13711                     ev.els.push(cg);
13712                     
13713                 }
13714                 
13715             }
13716             
13717             
13718             if(c.more.length){
13719                 var  cfg = {
13720                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13721                     style : 'position: absolute',
13722                     unselectable : "on",
13723                     cn : [
13724                         {
13725                             cls: 'fc-event-inner',
13726                             cn : [
13727                                 {
13728                                   tag:'span',
13729                                   cls: 'fc-event-title',
13730                                   html : 'More'
13731                                 }
13732
13733
13734                             ]
13735                         },
13736                         {
13737                             cls: 'ui-resizable-handle ui-resizable-e',
13738                             html : '&nbsp;&nbsp;&nbsp'
13739                         }
13740
13741                     ]
13742                 };
13743
13744                 var ctr = _this.el.select('.fc-event-container',true).first();
13745                 var cg = ctr.createChild(cfg);
13746
13747                 var sbox = c.select('.fc-day-content',true).first().getBox();
13748                 var ebox = c.select('.fc-day-content',true).first().getBox();
13749                 //Roo.log(cg);
13750                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13751                 cg.setWidth(ebox.right - sbox.x -2);
13752
13753                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13754                 
13755             }
13756             
13757         });
13758         
13759         
13760         
13761     },
13762     
13763     onEventEnter: function (e, el,event,d) {
13764         this.fireEvent('evententer', this, el, event);
13765     },
13766     
13767     onEventLeave: function (e, el,event,d) {
13768         this.fireEvent('eventleave', this, el, event);
13769     },
13770     
13771     onEventClick: function (e, el,event,d) {
13772         this.fireEvent('eventclick', this, el, event);
13773     },
13774     
13775     onMonthChange: function () {
13776         this.store.load();
13777     },
13778     
13779     onMoreEventClick: function(e, el, more)
13780     {
13781         var _this = this;
13782         
13783         this.calpopover.placement = 'right';
13784         this.calpopover.setTitle('More');
13785         
13786         this.calpopover.setContent('');
13787         
13788         var ctr = this.calpopover.el.select('.popover-content', true).first();
13789         
13790         Roo.each(more, function(m){
13791             var cfg = {
13792                 cls : 'fc-event-hori fc-event-draggable',
13793                 html : m.title
13794             }
13795             var cg = ctr.createChild(cfg);
13796             
13797             cg.on('click', _this.onEventClick, _this, m);
13798         });
13799         
13800         this.calpopover.show(el);
13801         
13802         
13803     },
13804     
13805     onLoad: function () 
13806     {   
13807         this.calevents = [];
13808         var cal = this;
13809         
13810         if(this.store.getCount() > 0){
13811             this.store.data.each(function(d){
13812                cal.addItem({
13813                     id : d.data.id,
13814                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13815                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13816                     time : d.data.start_time,
13817                     title : d.data.title,
13818                     description : d.data.description,
13819                     venue : d.data.venue
13820                 });
13821             });
13822         }
13823         
13824         this.renderEvents();
13825         
13826         if(this.calevents.length && this.loadMask){
13827             this.maskEl.hide();
13828         }
13829     },
13830     
13831     onBeforeLoad: function()
13832     {
13833         this.clearEvents();
13834         if(this.loadMask){
13835             this.maskEl.show();
13836         }
13837     }
13838 });
13839
13840  
13841  /*
13842  * - LGPL
13843  *
13844  * element
13845  * 
13846  */
13847
13848 /**
13849  * @class Roo.bootstrap.Popover
13850  * @extends Roo.bootstrap.Component
13851  * Bootstrap Popover class
13852  * @cfg {String} html contents of the popover   (or false to use children..)
13853  * @cfg {String} title of popover (or false to hide)
13854  * @cfg {String} placement how it is placed
13855  * @cfg {String} trigger click || hover (or false to trigger manually)
13856  * @cfg {String} over what (parent or false to trigger manually.)
13857  * @cfg {Number} delay - delay before showing
13858  
13859  * @constructor
13860  * Create a new Popover
13861  * @param {Object} config The config object
13862  */
13863
13864 Roo.bootstrap.Popover = function(config){
13865     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13866 };
13867
13868 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13869     
13870     title: 'Fill in a title',
13871     html: false,
13872     
13873     placement : 'right',
13874     trigger : 'hover', // hover
13875     
13876     delay : 0,
13877     
13878     over: 'parent',
13879     
13880     can_build_overlaid : false,
13881     
13882     getChildContainer : function()
13883     {
13884         return this.el.select('.popover-content',true).first();
13885     },
13886     
13887     getAutoCreate : function(){
13888          Roo.log('make popover?');
13889         var cfg = {
13890            cls : 'popover roo-dynamic',
13891            style: 'display:block',
13892            cn : [
13893                 {
13894                     cls : 'arrow'
13895                 },
13896                 {
13897                     cls : 'popover-inner',
13898                     cn : [
13899                         {
13900                             tag: 'h3',
13901                             cls: 'popover-title',
13902                             html : this.title
13903                         },
13904                         {
13905                             cls : 'popover-content',
13906                             html : this.html
13907                         }
13908                     ]
13909                     
13910                 }
13911            ]
13912         };
13913         
13914         return cfg;
13915     },
13916     setTitle: function(str)
13917     {
13918         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13919     },
13920     setContent: function(str)
13921     {
13922         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13923     },
13924     // as it get's added to the bottom of the page.
13925     onRender : function(ct, position)
13926     {
13927         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13928         if(!this.el){
13929             var cfg = Roo.apply({},  this.getAutoCreate());
13930             cfg.id = Roo.id();
13931             
13932             if (this.cls) {
13933                 cfg.cls += ' ' + this.cls;
13934             }
13935             if (this.style) {
13936                 cfg.style = this.style;
13937             }
13938             Roo.log("adding to ")
13939             this.el = Roo.get(document.body).createChild(cfg, position);
13940             Roo.log(this.el);
13941         }
13942         this.initEvents();
13943     },
13944     
13945     initEvents : function()
13946     {
13947         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13948         this.el.enableDisplayMode('block');
13949         this.el.hide();
13950         if (this.over === false) {
13951             return; 
13952         }
13953         if (this.triggers === false) {
13954             return;
13955         }
13956         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13957         var triggers = this.trigger ? this.trigger.split(' ') : [];
13958         Roo.each(triggers, function(trigger) {
13959         
13960             if (trigger == 'click') {
13961                 on_el.on('click', this.toggle, this);
13962             } else if (trigger != 'manual') {
13963                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13964                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13965       
13966                 on_el.on(eventIn  ,this.enter, this);
13967                 on_el.on(eventOut, this.leave, this);
13968             }
13969         }, this);
13970         
13971     },
13972     
13973     
13974     // private
13975     timeout : null,
13976     hoverState : null,
13977     
13978     toggle : function () {
13979         this.hoverState == 'in' ? this.leave() : this.enter();
13980     },
13981     
13982     enter : function () {
13983        
13984     
13985         clearTimeout(this.timeout);
13986     
13987         this.hoverState = 'in'
13988     
13989         if (!this.delay || !this.delay.show) {
13990             this.show();
13991             return 
13992         }
13993         var _t = this;
13994         this.timeout = setTimeout(function () {
13995             if (_t.hoverState == 'in') {
13996                 _t.show();
13997             }
13998         }, this.delay.show)
13999     },
14000     leave : function() {
14001         clearTimeout(this.timeout);
14002     
14003         this.hoverState = 'out'
14004     
14005         if (!this.delay || !this.delay.hide) {
14006             this.hide();
14007             return 
14008         }
14009         var _t = this;
14010         this.timeout = setTimeout(function () {
14011             if (_t.hoverState == 'out') {
14012                 _t.hide();
14013             }
14014         }, this.delay.hide)
14015     },
14016     
14017     show : function (on_el)
14018     {
14019         if (!on_el) {
14020             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14021         }
14022         // set content.
14023         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14024         if (this.html !== false) {
14025             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14026         }
14027         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14028         if (!this.title.length) {
14029             this.el.select('.popover-title',true).hide();
14030         }
14031         
14032         var placement = typeof this.placement == 'function' ?
14033             this.placement.call(this, this.el, on_el) :
14034             this.placement;
14035             
14036         var autoToken = /\s?auto?\s?/i;
14037         var autoPlace = autoToken.test(placement);
14038         if (autoPlace) {
14039             placement = placement.replace(autoToken, '') || 'top';
14040         }
14041         
14042         //this.el.detach()
14043         //this.el.setXY([0,0]);
14044         this.el.show();
14045         this.el.dom.style.display='block';
14046         this.el.addClass(placement);
14047         
14048         //this.el.appendTo(on_el);
14049         
14050         var p = this.getPosition();
14051         var box = this.el.getBox();
14052         
14053         if (autoPlace) {
14054             // fixme..
14055         }
14056         var align = Roo.bootstrap.Popover.alignment[placement]
14057         this.el.alignTo(on_el, align[0],align[1]);
14058         //var arrow = this.el.select('.arrow',true).first();
14059         //arrow.set(align[2], 
14060         
14061         this.el.addClass('in');
14062         this.hoverState = null;
14063         
14064         if (this.el.hasClass('fade')) {
14065             // fade it?
14066         }
14067         
14068     },
14069     hide : function()
14070     {
14071         this.el.setXY([0,0]);
14072         this.el.removeClass('in');
14073         this.el.hide();
14074         
14075     }
14076     
14077 });
14078
14079 Roo.bootstrap.Popover.alignment = {
14080     'left' : ['r-l', [-10,0], 'right'],
14081     'right' : ['l-r', [10,0], 'left'],
14082     'bottom' : ['t-b', [0,10], 'top'],
14083     'top' : [ 'b-t', [0,-10], 'bottom']
14084 };
14085
14086  /*
14087  * - LGPL
14088  *
14089  * Progress
14090  * 
14091  */
14092
14093 /**
14094  * @class Roo.bootstrap.Progress
14095  * @extends Roo.bootstrap.Component
14096  * Bootstrap Progress class
14097  * @cfg {Boolean} striped striped of the progress bar
14098  * @cfg {Boolean} active animated of the progress bar
14099  * 
14100  * 
14101  * @constructor
14102  * Create a new Progress
14103  * @param {Object} config The config object
14104  */
14105
14106 Roo.bootstrap.Progress = function(config){
14107     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14108 };
14109
14110 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14111     
14112     striped : false,
14113     active: false,
14114     
14115     getAutoCreate : function(){
14116         var cfg = {
14117             tag: 'div',
14118             cls: 'progress'
14119         };
14120         
14121         
14122         if(this.striped){
14123             cfg.cls += ' progress-striped';
14124         }
14125       
14126         if(this.active){
14127             cfg.cls += ' active';
14128         }
14129         
14130         
14131         return cfg;
14132     }
14133    
14134 });
14135
14136  
14137
14138  /*
14139  * - LGPL
14140  *
14141  * ProgressBar
14142  * 
14143  */
14144
14145 /**
14146  * @class Roo.bootstrap.ProgressBar
14147  * @extends Roo.bootstrap.Component
14148  * Bootstrap ProgressBar class
14149  * @cfg {Number} aria_valuenow aria-value now
14150  * @cfg {Number} aria_valuemin aria-value min
14151  * @cfg {Number} aria_valuemax aria-value max
14152  * @cfg {String} label label for the progress bar
14153  * @cfg {String} panel (success | info | warning | danger )
14154  * @cfg {String} role role of the progress bar
14155  * @cfg {String} sr_only text
14156  * 
14157  * 
14158  * @constructor
14159  * Create a new ProgressBar
14160  * @param {Object} config The config object
14161  */
14162
14163 Roo.bootstrap.ProgressBar = function(config){
14164     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14165 };
14166
14167 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14168     
14169     aria_valuenow : 0,
14170     aria_valuemin : 0,
14171     aria_valuemax : 100,
14172     label : false,
14173     panel : false,
14174     role : false,
14175     sr_only: false,
14176     
14177     getAutoCreate : function()
14178     {
14179         
14180         var cfg = {
14181             tag: 'div',
14182             cls: 'progress-bar',
14183             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14184         };
14185         
14186         if(this.sr_only){
14187             cfg.cn = {
14188                 tag: 'span',
14189                 cls: 'sr-only',
14190                 html: this.sr_only
14191             }
14192         }
14193         
14194         if(this.role){
14195             cfg.role = this.role;
14196         }
14197         
14198         if(this.aria_valuenow){
14199             cfg['aria-valuenow'] = this.aria_valuenow;
14200         }
14201         
14202         if(this.aria_valuemin){
14203             cfg['aria-valuemin'] = this.aria_valuemin;
14204         }
14205         
14206         if(this.aria_valuemax){
14207             cfg['aria-valuemax'] = this.aria_valuemax;
14208         }
14209         
14210         if(this.label && !this.sr_only){
14211             cfg.html = this.label;
14212         }
14213         
14214         if(this.panel){
14215             cfg.cls += ' progress-bar-' + this.panel;
14216         }
14217         
14218         return cfg;
14219     },
14220     
14221     update : function(aria_valuenow)
14222     {
14223         this.aria_valuenow = aria_valuenow;
14224         
14225         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14226     }
14227    
14228 });
14229
14230  
14231
14232  /*
14233  * - LGPL
14234  *
14235  * column
14236  * 
14237  */
14238
14239 /**
14240  * @class Roo.bootstrap.TabGroup
14241  * @extends Roo.bootstrap.Column
14242  * Bootstrap Column class
14243  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14244  * @cfg {Boolean} carousel true to make the group behave like a carousel
14245  * 
14246  * @constructor
14247  * Create a new TabGroup
14248  * @param {Object} config The config object
14249  */
14250
14251 Roo.bootstrap.TabGroup = function(config){
14252     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14253     if (!this.navId) {
14254         this.navId = Roo.id();
14255     }
14256     this.tabs = [];
14257     Roo.bootstrap.TabGroup.register(this);
14258     
14259 };
14260
14261 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14262     
14263     carousel : false,
14264     transition : false,
14265      
14266     getAutoCreate : function()
14267     {
14268         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14269         
14270         cfg.cls += ' tab-content';
14271         
14272         if (this.carousel) {
14273             cfg.cls += ' carousel slide';
14274             cfg.cn = [{
14275                cls : 'carousel-inner'
14276             }]
14277         }
14278         
14279         
14280         return cfg;
14281     },
14282     getChildContainer : function()
14283     {
14284         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14285     },
14286     
14287     /**
14288     * register a Navigation item
14289     * @param {Roo.bootstrap.NavItem} the navitem to add
14290     */
14291     register : function(item)
14292     {
14293         this.tabs.push( item);
14294         item.navId = this.navId; // not really needed..
14295     
14296     },
14297     
14298     getActivePanel : function()
14299     {
14300         var r = false;
14301         Roo.each(this.tabs, function(t) {
14302             if (t.active) {
14303                 r = t;
14304                 return false;
14305             }
14306             return null;
14307         });
14308         return r;
14309         
14310     },
14311     getPanelByName : function(n)
14312     {
14313         var r = false;
14314         Roo.each(this.tabs, function(t) {
14315             if (t.tabId == n) {
14316                 r = t;
14317                 return false;
14318             }
14319             return null;
14320         });
14321         return r;
14322     },
14323     indexOfPanel : function(p)
14324     {
14325         var r = false;
14326         Roo.each(this.tabs, function(t,i) {
14327             if (t.tabId == p.tabId) {
14328                 r = i;
14329                 return false;
14330             }
14331             return null;
14332         });
14333         return r;
14334     },
14335     /**
14336      * show a specific panel
14337      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14338      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14339      */
14340     showPanel : function (pan)
14341     {
14342         
14343         if (typeof(pan) == 'number') {
14344             pan = this.tabs[pan];
14345         }
14346         if (typeof(pan) == 'string') {
14347             pan = this.getPanelByName(pan);
14348         }
14349         if (pan.tabId == this.getActivePanel().tabId) {
14350             return true;
14351         }
14352         var cur = this.getActivePanel();
14353         
14354         if (false === cur.fireEvent('beforedeactivate')) {
14355             return false;
14356         }
14357         
14358         if (this.carousel) {
14359             this.transition = true;
14360             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14361             var lr = dir == 'next' ? 'left' : 'right';
14362             pan.el.addClass(dir); // or prev
14363             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14364             cur.el.addClass(lr); // or right
14365             pan.el.addClass(lr);
14366             
14367             var _this = this;
14368             cur.el.on('transitionend', function() {
14369                 Roo.log("trans end?");
14370                 
14371                 pan.el.removeClass([lr,dir]);
14372                 pan.setActive(true);
14373                 
14374                 cur.el.removeClass([lr]);
14375                 cur.setActive(false);
14376                 
14377                 _this.transition = false;
14378                 
14379             }, this, { single:  true } );
14380             return true;
14381         }
14382         
14383         cur.setActive(false);
14384         pan.setActive(true);
14385         return true;
14386         
14387     },
14388     showPanelNext : function()
14389     {
14390         var i = this.indexOfPanel(this.getActivePanel());
14391         if (i > this.tabs.length) {
14392             return;
14393         }
14394         this.showPanel(this.tabs[i+1]);
14395     },
14396     showPanelPrev : function()
14397     {
14398         var i = this.indexOfPanel(this.getActivePanel());
14399         if (i  < 1) {
14400             return;
14401         }
14402         this.showPanel(this.tabs[i-1]);
14403     }
14404     
14405     
14406   
14407 });
14408
14409  
14410
14411  
14412  
14413 Roo.apply(Roo.bootstrap.TabGroup, {
14414     
14415     groups: {},
14416      /**
14417     * register a Navigation Group
14418     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14419     */
14420     register : function(navgrp)
14421     {
14422         this.groups[navgrp.navId] = navgrp;
14423         
14424     },
14425     /**
14426     * fetch a Navigation Group based on the navigation ID
14427     * if one does not exist , it will get created.
14428     * @param {string} the navgroup to add
14429     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14430     */
14431     get: function(navId) {
14432         if (typeof(this.groups[navId]) == 'undefined') {
14433             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14434         }
14435         return this.groups[navId] ;
14436     }
14437     
14438     
14439     
14440 });
14441
14442  /*
14443  * - LGPL
14444  *
14445  * TabPanel
14446  * 
14447  */
14448
14449 /**
14450  * @class Roo.bootstrap.TabPanel
14451  * @extends Roo.bootstrap.Component
14452  * Bootstrap TabPanel class
14453  * @cfg {Boolean} active panel active
14454  * @cfg {String} html panel content
14455  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14456  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14457  * 
14458  * 
14459  * @constructor
14460  * Create a new TabPanel
14461  * @param {Object} config The config object
14462  */
14463
14464 Roo.bootstrap.TabPanel = function(config){
14465     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14466     this.addEvents({
14467         /**
14468              * @event changed
14469              * Fires when the active status changes
14470              * @param {Roo.bootstrap.TabPanel} this
14471              * @param {Boolean} state the new state
14472             
14473          */
14474         'changed': true,
14475         /**
14476              * @event beforedeactivate
14477              * Fires before a tab is de-activated - can be used to do validation on a form.
14478              * @param {Roo.bootstrap.TabPanel} this
14479              * @return {Boolean} false if there is an error
14480             
14481          */
14482         'beforedeactivate': true
14483      });
14484     
14485     this.tabId = this.tabId || Roo.id();
14486   
14487 };
14488
14489 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14490     
14491     active: false,
14492     html: false,
14493     tabId: false,
14494     navId : false,
14495     
14496     getAutoCreate : function(){
14497         var cfg = {
14498             tag: 'div',
14499             // item is needed for carousel - not sure if it has any effect otherwise
14500             cls: 'tab-pane item',
14501             html: this.html || ''
14502         };
14503         
14504         if(this.active){
14505             cfg.cls += ' active';
14506         }
14507         
14508         if(this.tabId){
14509             cfg.tabId = this.tabId;
14510         }
14511         
14512         
14513         return cfg;
14514     },
14515     
14516     initEvents:  function()
14517     {
14518         Roo.log('-------- init events on tab panel ---------');
14519         
14520         var p = this.parent();
14521         this.navId = this.navId || p.navId;
14522         
14523         if (typeof(this.navId) != 'undefined') {
14524             // not really needed.. but just in case.. parent should be a NavGroup.
14525             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14526             Roo.log(['register', tg, this]);
14527             tg.register(this);
14528         }
14529     },
14530     
14531     
14532     onRender : function(ct, position)
14533     {
14534        // Roo.log("Call onRender: " + this.xtype);
14535         
14536         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14537         
14538         
14539         
14540         
14541         
14542     },
14543     
14544     setActive: function(state)
14545     {
14546         Roo.log("panel - set active " + this.tabId + "=" + state);
14547         
14548         this.active = state;
14549         if (!state) {
14550             this.el.removeClass('active');
14551             
14552         } else  if (!this.el.hasClass('active')) {
14553             this.el.addClass('active');
14554         }
14555         this.fireEvent('changed', this, state);
14556     }
14557     
14558     
14559 });
14560  
14561
14562  
14563
14564  /*
14565  * - LGPL
14566  *
14567  * DateField
14568  * 
14569  */
14570
14571 /**
14572  * @class Roo.bootstrap.DateField
14573  * @extends Roo.bootstrap.Input
14574  * Bootstrap DateField class
14575  * @cfg {Number} weekStart default 0
14576  * @cfg {String} viewMode default empty, (months|years)
14577  * @cfg {String} minViewMode default empty, (months|years)
14578  * @cfg {Number} startDate default -Infinity
14579  * @cfg {Number} endDate default Infinity
14580  * @cfg {Boolean} todayHighlight default false
14581  * @cfg {Boolean} todayBtn default false
14582  * @cfg {Boolean} calendarWeeks default false
14583  * @cfg {Object} daysOfWeekDisabled default empty
14584  * @cfg {Boolean} singleMode default false (true | false)
14585  * 
14586  * @cfg {Boolean} keyboardNavigation default true
14587  * @cfg {String} language default en
14588  * 
14589  * @constructor
14590  * Create a new DateField
14591  * @param {Object} config The config object
14592  */
14593
14594 Roo.bootstrap.DateField = function(config){
14595     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14596      this.addEvents({
14597             /**
14598              * @event show
14599              * Fires when this field show.
14600              * @param {Roo.bootstrap.DateField} this
14601              * @param {Mixed} date The date value
14602              */
14603             show : true,
14604             /**
14605              * @event show
14606              * Fires when this field hide.
14607              * @param {Roo.bootstrap.DateField} this
14608              * @param {Mixed} date The date value
14609              */
14610             hide : true,
14611             /**
14612              * @event select
14613              * Fires when select a date.
14614              * @param {Roo.bootstrap.DateField} this
14615              * @param {Mixed} date The date value
14616              */
14617             select : true
14618         });
14619 };
14620
14621 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14622     
14623     /**
14624      * @cfg {String} format
14625      * The default date format string which can be overriden for localization support.  The format must be
14626      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14627      */
14628     format : "m/d/y",
14629     /**
14630      * @cfg {String} altFormats
14631      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14632      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14633      */
14634     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14635     
14636     weekStart : 0,
14637     
14638     viewMode : '',
14639     
14640     minViewMode : '',
14641     
14642     todayHighlight : false,
14643     
14644     todayBtn: false,
14645     
14646     language: 'en',
14647     
14648     keyboardNavigation: true,
14649     
14650     calendarWeeks: false,
14651     
14652     startDate: -Infinity,
14653     
14654     endDate: Infinity,
14655     
14656     daysOfWeekDisabled: [],
14657     
14658     _events: [],
14659     
14660     singleMode : false,
14661     
14662     UTCDate: function()
14663     {
14664         return new Date(Date.UTC.apply(Date, arguments));
14665     },
14666     
14667     UTCToday: function()
14668     {
14669         var today = new Date();
14670         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14671     },
14672     
14673     getDate: function() {
14674             var d = this.getUTCDate();
14675             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14676     },
14677     
14678     getUTCDate: function() {
14679             return this.date;
14680     },
14681     
14682     setDate: function(d) {
14683             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14684     },
14685     
14686     setUTCDate: function(d) {
14687             this.date = d;
14688             this.setValue(this.formatDate(this.date));
14689     },
14690         
14691     onRender: function(ct, position)
14692     {
14693         
14694         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14695         
14696         this.language = this.language || 'en';
14697         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14698         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14699         
14700         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14701         this.format = this.format || 'm/d/y';
14702         this.isInline = false;
14703         this.isInput = true;
14704         this.component = this.el.select('.add-on', true).first() || false;
14705         this.component = (this.component && this.component.length === 0) ? false : this.component;
14706         this.hasInput = this.component && this.inputEL().length;
14707         
14708         if (typeof(this.minViewMode === 'string')) {
14709             switch (this.minViewMode) {
14710                 case 'months':
14711                     this.minViewMode = 1;
14712                     break;
14713                 case 'years':
14714                     this.minViewMode = 2;
14715                     break;
14716                 default:
14717                     this.minViewMode = 0;
14718                     break;
14719             }
14720         }
14721         
14722         if (typeof(this.viewMode === 'string')) {
14723             switch (this.viewMode) {
14724                 case 'months':
14725                     this.viewMode = 1;
14726                     break;
14727                 case 'years':
14728                     this.viewMode = 2;
14729                     break;
14730                 default:
14731                     this.viewMode = 0;
14732                     break;
14733             }
14734         }
14735                 
14736         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14737         
14738 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14739         
14740         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14741         
14742         this.picker().on('mousedown', this.onMousedown, this);
14743         this.picker().on('click', this.onClick, this);
14744         
14745         this.picker().addClass('datepicker-dropdown');
14746         
14747         this.startViewMode = this.viewMode;
14748         
14749         if(this.singleMode){
14750             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
14751                 v.setVisibilityMode(Roo.Element.DISPLAY)
14752                 v.hide();
14753             })
14754             
14755             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
14756                 v.setStyle('width', '189px');
14757             });
14758         }
14759         
14760         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14761             if(!this.calendarWeeks){
14762                 v.remove();
14763                 return;
14764             };
14765             
14766             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14767             v.attr('colspan', function(i, val){
14768                 return parseInt(val) + 1;
14769             });
14770         })
14771                         
14772         
14773         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14774         
14775         this.setStartDate(this.startDate);
14776         this.setEndDate(this.endDate);
14777         
14778         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14779         
14780         this.fillDow();
14781         this.fillMonths();
14782         this.update();
14783         this.showMode();
14784         
14785         if(this.isInline) {
14786             this.show();
14787         }
14788     },
14789     
14790     picker : function()
14791     {
14792         return this.pickerEl;
14793 //        return this.el.select('.datepicker', true).first();
14794     },
14795     
14796     fillDow: function()
14797     {
14798         var dowCnt = this.weekStart;
14799         
14800         var dow = {
14801             tag: 'tr',
14802             cn: [
14803                 
14804             ]
14805         };
14806         
14807         if(this.calendarWeeks){
14808             dow.cn.push({
14809                 tag: 'th',
14810                 cls: 'cw',
14811                 html: '&nbsp;'
14812             })
14813         }
14814         
14815         while (dowCnt < this.weekStart + 7) {
14816             dow.cn.push({
14817                 tag: 'th',
14818                 cls: 'dow',
14819                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14820             });
14821         }
14822         
14823         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14824     },
14825     
14826     fillMonths: function()
14827     {    
14828         var i = 0
14829         var months = this.picker().select('>.datepicker-months td', true).first();
14830         
14831         months.dom.innerHTML = '';
14832         
14833         while (i < 12) {
14834             var month = {
14835                 tag: 'span',
14836                 cls: 'month',
14837                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14838             }
14839             
14840             months.createChild(month);
14841         }
14842         
14843     },
14844     
14845     update: function()
14846     {
14847         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;
14848         
14849         if (this.date < this.startDate) {
14850             this.viewDate = new Date(this.startDate);
14851         } else if (this.date > this.endDate) {
14852             this.viewDate = new Date(this.endDate);
14853         } else {
14854             this.viewDate = new Date(this.date);
14855         }
14856         
14857         this.fill();
14858     },
14859     
14860     fill: function() 
14861     {
14862         var d = new Date(this.viewDate),
14863                 year = d.getUTCFullYear(),
14864                 month = d.getUTCMonth(),
14865                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14866                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14867                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14868                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14869                 currentDate = this.date && this.date.valueOf(),
14870                 today = this.UTCToday();
14871         
14872         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14873         
14874 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14875         
14876 //        this.picker.select('>tfoot th.today').
14877 //                                              .text(dates[this.language].today)
14878 //                                              .toggle(this.todayBtn !== false);
14879     
14880         this.updateNavArrows();
14881         this.fillMonths();
14882                                                 
14883         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14884         
14885         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14886          
14887         prevMonth.setUTCDate(day);
14888         
14889         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14890         
14891         var nextMonth = new Date(prevMonth);
14892         
14893         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14894         
14895         nextMonth = nextMonth.valueOf();
14896         
14897         var fillMonths = false;
14898         
14899         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14900         
14901         while(prevMonth.valueOf() < nextMonth) {
14902             var clsName = '';
14903             
14904             if (prevMonth.getUTCDay() === this.weekStart) {
14905                 if(fillMonths){
14906                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14907                 }
14908                     
14909                 fillMonths = {
14910                     tag: 'tr',
14911                     cn: []
14912                 };
14913                 
14914                 if(this.calendarWeeks){
14915                     // ISO 8601: First week contains first thursday.
14916                     // ISO also states week starts on Monday, but we can be more abstract here.
14917                     var
14918                     // Start of current week: based on weekstart/current date
14919                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14920                     // Thursday of this week
14921                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14922                     // First Thursday of year, year from thursday
14923                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14924                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14925                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14926                     
14927                     fillMonths.cn.push({
14928                         tag: 'td',
14929                         cls: 'cw',
14930                         html: calWeek
14931                     });
14932                 }
14933             }
14934             
14935             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14936                 clsName += ' old';
14937             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14938                 clsName += ' new';
14939             }
14940             if (this.todayHighlight &&
14941                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14942                 prevMonth.getUTCMonth() == today.getMonth() &&
14943                 prevMonth.getUTCDate() == today.getDate()) {
14944                 clsName += ' today';
14945             }
14946             
14947             if (currentDate && prevMonth.valueOf() === currentDate) {
14948                 clsName += ' active';
14949             }
14950             
14951             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14952                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14953                     clsName += ' disabled';
14954             }
14955             
14956             fillMonths.cn.push({
14957                 tag: 'td',
14958                 cls: 'day ' + clsName,
14959                 html: prevMonth.getDate()
14960             })
14961             
14962             prevMonth.setDate(prevMonth.getDate()+1);
14963         }
14964           
14965         var currentYear = this.date && this.date.getUTCFullYear();
14966         var currentMonth = this.date && this.date.getUTCMonth();
14967         
14968         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14969         
14970         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14971             v.removeClass('active');
14972             
14973             if(currentYear === year && k === currentMonth){
14974                 v.addClass('active');
14975             }
14976             
14977             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14978                 v.addClass('disabled');
14979             }
14980             
14981         });
14982         
14983         
14984         year = parseInt(year/10, 10) * 10;
14985         
14986         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14987         
14988         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14989         
14990         year -= 1;
14991         for (var i = -1; i < 11; i++) {
14992             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14993                 tag: 'span',
14994                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14995                 html: year
14996             })
14997             
14998             year += 1;
14999         }
15000     },
15001     
15002     showMode: function(dir) 
15003     {
15004         if (dir) {
15005             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15006         }
15007         
15008         Roo.each(this.picker().select('>div',true).elements, function(v){
15009             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15010             v.hide();
15011         });
15012         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15013     },
15014     
15015     place: function()
15016     {
15017         if(this.isInline) return;
15018         
15019         this.picker().removeClass(['bottom', 'top']);
15020         
15021         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15022             /*
15023              * place to the top of element!
15024              *
15025              */
15026             
15027             this.picker().addClass('top');
15028             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15029             
15030             return;
15031         }
15032         
15033         this.picker().addClass('bottom');
15034         
15035         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15036     },
15037     
15038     parseDate : function(value)
15039     {
15040         if(!value || value instanceof Date){
15041             return value;
15042         }
15043         var v = Date.parseDate(value, this.format);
15044         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15045             v = Date.parseDate(value, 'Y-m-d');
15046         }
15047         if(!v && this.altFormats){
15048             if(!this.altFormatsArray){
15049                 this.altFormatsArray = this.altFormats.split("|");
15050             }
15051             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15052                 v = Date.parseDate(value, this.altFormatsArray[i]);
15053             }
15054         }
15055         return v;
15056     },
15057     
15058     formatDate : function(date, fmt)
15059     {   
15060         return (!date || !(date instanceof Date)) ?
15061         date : date.dateFormat(fmt || this.format);
15062     },
15063     
15064     onFocus : function()
15065     {
15066         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15067         this.show();
15068     },
15069     
15070     onBlur : function()
15071     {
15072         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15073         
15074         var d = this.inputEl().getValue();
15075         
15076         this.setValue(d);
15077                 
15078         this.hide();
15079     },
15080     
15081     show : function()
15082     {
15083         this.picker().show();
15084         this.update();
15085         this.place();
15086         
15087         this.fireEvent('show', this, this.date);
15088     },
15089     
15090     hide : function()
15091     {
15092         if(this.isInline) return;
15093         this.picker().hide();
15094         this.viewMode = this.startViewMode;
15095         this.showMode();
15096         
15097         this.fireEvent('hide', this, this.date);
15098         
15099     },
15100     
15101     onMousedown: function(e)
15102     {
15103         e.stopPropagation();
15104         e.preventDefault();
15105     },
15106     
15107     keyup: function(e)
15108     {
15109         Roo.bootstrap.DateField.superclass.keyup.call(this);
15110         this.update();
15111     },
15112
15113     setValue: function(v)
15114     {
15115         
15116         // v can be a string or a date..
15117         
15118         
15119         var d = new Date(this.parseDate(v) ).clearTime();
15120         
15121         if(isNaN(d.getTime())){
15122             this.date = this.viewDate = '';
15123             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15124             return;
15125         }
15126         
15127         v = this.formatDate(d);
15128         
15129         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15130         
15131         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15132      
15133         this.update();
15134
15135         this.fireEvent('select', this, this.date);
15136         
15137     },
15138     
15139     getValue: function()
15140     {
15141         return this.formatDate(this.date);
15142     },
15143     
15144     fireKey: function(e)
15145     {
15146         if (!this.picker().isVisible()){
15147             if (e.keyCode == 27) // allow escape to hide and re-show picker
15148                 this.show();
15149             return;
15150         }
15151         
15152         var dateChanged = false,
15153         dir, day, month,
15154         newDate, newViewDate;
15155         
15156         switch(e.keyCode){
15157             case 27: // escape
15158                 this.hide();
15159                 e.preventDefault();
15160                 break;
15161             case 37: // left
15162             case 39: // right
15163                 if (!this.keyboardNavigation) break;
15164                 dir = e.keyCode == 37 ? -1 : 1;
15165                 
15166                 if (e.ctrlKey){
15167                     newDate = this.moveYear(this.date, dir);
15168                     newViewDate = this.moveYear(this.viewDate, dir);
15169                 } else if (e.shiftKey){
15170                     newDate = this.moveMonth(this.date, dir);
15171                     newViewDate = this.moveMonth(this.viewDate, dir);
15172                 } else {
15173                     newDate = new Date(this.date);
15174                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15175                     newViewDate = new Date(this.viewDate);
15176                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15177                 }
15178                 if (this.dateWithinRange(newDate)){
15179                     this.date = newDate;
15180                     this.viewDate = newViewDate;
15181                     this.setValue(this.formatDate(this.date));
15182 //                    this.update();
15183                     e.preventDefault();
15184                     dateChanged = true;
15185                 }
15186                 break;
15187             case 38: // up
15188             case 40: // down
15189                 if (!this.keyboardNavigation) break;
15190                 dir = e.keyCode == 38 ? -1 : 1;
15191                 if (e.ctrlKey){
15192                     newDate = this.moveYear(this.date, dir);
15193                     newViewDate = this.moveYear(this.viewDate, dir);
15194                 } else if (e.shiftKey){
15195                     newDate = this.moveMonth(this.date, dir);
15196                     newViewDate = this.moveMonth(this.viewDate, dir);
15197                 } else {
15198                     newDate = new Date(this.date);
15199                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15200                     newViewDate = new Date(this.viewDate);
15201                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15202                 }
15203                 if (this.dateWithinRange(newDate)){
15204                     this.date = newDate;
15205                     this.viewDate = newViewDate;
15206                     this.setValue(this.formatDate(this.date));
15207 //                    this.update();
15208                     e.preventDefault();
15209                     dateChanged = true;
15210                 }
15211                 break;
15212             case 13: // enter
15213                 this.setValue(this.formatDate(this.date));
15214                 this.hide();
15215                 e.preventDefault();
15216                 break;
15217             case 9: // tab
15218                 this.setValue(this.formatDate(this.date));
15219                 this.hide();
15220                 break;
15221             case 16: // shift
15222             case 17: // ctrl
15223             case 18: // alt
15224                 break;
15225             default :
15226                 this.hide();
15227                 
15228         }
15229     },
15230     
15231     
15232     onClick: function(e) 
15233     {
15234         e.stopPropagation();
15235         e.preventDefault();
15236         
15237         var target = e.getTarget();
15238         
15239         if(target.nodeName.toLowerCase() === 'i'){
15240             target = Roo.get(target).dom.parentNode;
15241         }
15242         
15243         var nodeName = target.nodeName;
15244         var className = target.className;
15245         var html = target.innerHTML;
15246         //Roo.log(nodeName);
15247         
15248         switch(nodeName.toLowerCase()) {
15249             case 'th':
15250                 switch(className) {
15251                     case 'switch':
15252                         this.showMode(1);
15253                         break;
15254                     case 'prev':
15255                     case 'next':
15256                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15257                         switch(this.viewMode){
15258                                 case 0:
15259                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15260                                         break;
15261                                 case 1:
15262                                 case 2:
15263                                         this.viewDate = this.moveYear(this.viewDate, dir);
15264                                         break;
15265                         }
15266                         this.fill();
15267                         break;
15268                     case 'today':
15269                         var date = new Date();
15270                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15271 //                        this.fill()
15272                         this.setValue(this.formatDate(this.date));
15273                         
15274                         this.hide();
15275                         break;
15276                 }
15277                 break;
15278             case 'span':
15279                 if (className.indexOf('disabled') < 0) {
15280                     this.viewDate.setUTCDate(1);
15281                     if (className.indexOf('month') > -1) {
15282                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15283                     } else {
15284                         var year = parseInt(html, 10) || 0;
15285                         this.viewDate.setUTCFullYear(year);
15286                         
15287                     }
15288                     
15289                     if(this.singleMode){
15290                         this.setValue(this.formatDate(this.viewDate));
15291                         this.hide();
15292                         return;
15293                     }
15294                     
15295                     this.showMode(-1);
15296                     this.fill();
15297                 }
15298                 break;
15299                 
15300             case 'td':
15301                 //Roo.log(className);
15302                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15303                     var day = parseInt(html, 10) || 1;
15304                     var year = this.viewDate.getUTCFullYear(),
15305                         month = this.viewDate.getUTCMonth();
15306
15307                     if (className.indexOf('old') > -1) {
15308                         if(month === 0 ){
15309                             month = 11;
15310                             year -= 1;
15311                         }else{
15312                             month -= 1;
15313                         }
15314                     } else if (className.indexOf('new') > -1) {
15315                         if (month == 11) {
15316                             month = 0;
15317                             year += 1;
15318                         } else {
15319                             month += 1;
15320                         }
15321                     }
15322                     //Roo.log([year,month,day]);
15323                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15324                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15325 //                    this.fill();
15326                     //Roo.log(this.formatDate(this.date));
15327                     this.setValue(this.formatDate(this.date));
15328                     this.hide();
15329                 }
15330                 break;
15331         }
15332     },
15333     
15334     setStartDate: function(startDate)
15335     {
15336         this.startDate = startDate || -Infinity;
15337         if (this.startDate !== -Infinity) {
15338             this.startDate = this.parseDate(this.startDate);
15339         }
15340         this.update();
15341         this.updateNavArrows();
15342     },
15343
15344     setEndDate: function(endDate)
15345     {
15346         this.endDate = endDate || Infinity;
15347         if (this.endDate !== Infinity) {
15348             this.endDate = this.parseDate(this.endDate);
15349         }
15350         this.update();
15351         this.updateNavArrows();
15352     },
15353     
15354     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15355     {
15356         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15357         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15358             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15359         }
15360         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15361             return parseInt(d, 10);
15362         });
15363         this.update();
15364         this.updateNavArrows();
15365     },
15366     
15367     updateNavArrows: function() 
15368     {
15369         if(this.singleMode){
15370             return;
15371         }
15372         
15373         var d = new Date(this.viewDate),
15374         year = d.getUTCFullYear(),
15375         month = d.getUTCMonth();
15376         
15377         Roo.each(this.picker().select('.prev', true).elements, function(v){
15378             v.show();
15379             switch (this.viewMode) {
15380                 case 0:
15381
15382                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15383                         v.hide();
15384                     }
15385                     break;
15386                 case 1:
15387                 case 2:
15388                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15389                         v.hide();
15390                     }
15391                     break;
15392             }
15393         });
15394         
15395         Roo.each(this.picker().select('.next', true).elements, function(v){
15396             v.show();
15397             switch (this.viewMode) {
15398                 case 0:
15399
15400                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15401                         v.hide();
15402                     }
15403                     break;
15404                 case 1:
15405                 case 2:
15406                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15407                         v.hide();
15408                     }
15409                     break;
15410             }
15411         })
15412     },
15413     
15414     moveMonth: function(date, dir)
15415     {
15416         if (!dir) return date;
15417         var new_date = new Date(date.valueOf()),
15418         day = new_date.getUTCDate(),
15419         month = new_date.getUTCMonth(),
15420         mag = Math.abs(dir),
15421         new_month, test;
15422         dir = dir > 0 ? 1 : -1;
15423         if (mag == 1){
15424             test = dir == -1
15425             // If going back one month, make sure month is not current month
15426             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15427             ? function(){
15428                 return new_date.getUTCMonth() == month;
15429             }
15430             // If going forward one month, make sure month is as expected
15431             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15432             : function(){
15433                 return new_date.getUTCMonth() != new_month;
15434             };
15435             new_month = month + dir;
15436             new_date.setUTCMonth(new_month);
15437             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15438             if (new_month < 0 || new_month > 11)
15439                 new_month = (new_month + 12) % 12;
15440         } else {
15441             // For magnitudes >1, move one month at a time...
15442             for (var i=0; i<mag; i++)
15443                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15444                 new_date = this.moveMonth(new_date, dir);
15445             // ...then reset the day, keeping it in the new month
15446             new_month = new_date.getUTCMonth();
15447             new_date.setUTCDate(day);
15448             test = function(){
15449                 return new_month != new_date.getUTCMonth();
15450             };
15451         }
15452         // Common date-resetting loop -- if date is beyond end of month, make it
15453         // end of month
15454         while (test()){
15455             new_date.setUTCDate(--day);
15456             new_date.setUTCMonth(new_month);
15457         }
15458         return new_date;
15459     },
15460
15461     moveYear: function(date, dir)
15462     {
15463         return this.moveMonth(date, dir*12);
15464     },
15465
15466     dateWithinRange: function(date)
15467     {
15468         return date >= this.startDate && date <= this.endDate;
15469     },
15470
15471     
15472     remove: function() 
15473     {
15474         this.picker().remove();
15475     }
15476    
15477 });
15478
15479 Roo.apply(Roo.bootstrap.DateField,  {
15480     
15481     head : {
15482         tag: 'thead',
15483         cn: [
15484         {
15485             tag: 'tr',
15486             cn: [
15487             {
15488                 tag: 'th',
15489                 cls: 'prev',
15490                 html: '<i class="fa fa-arrow-left"/>'
15491             },
15492             {
15493                 tag: 'th',
15494                 cls: 'switch',
15495                 colspan: '5'
15496             },
15497             {
15498                 tag: 'th',
15499                 cls: 'next',
15500                 html: '<i class="fa fa-arrow-right"/>'
15501             }
15502
15503             ]
15504         }
15505         ]
15506     },
15507     
15508     content : {
15509         tag: 'tbody',
15510         cn: [
15511         {
15512             tag: 'tr',
15513             cn: [
15514             {
15515                 tag: 'td',
15516                 colspan: '7'
15517             }
15518             ]
15519         }
15520         ]
15521     },
15522     
15523     footer : {
15524         tag: 'tfoot',
15525         cn: [
15526         {
15527             tag: 'tr',
15528             cn: [
15529             {
15530                 tag: 'th',
15531                 colspan: '7',
15532                 cls: 'today'
15533             }
15534                     
15535             ]
15536         }
15537         ]
15538     },
15539     
15540     dates:{
15541         en: {
15542             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15543             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15544             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15545             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15546             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15547             today: "Today"
15548         }
15549     },
15550     
15551     modes: [
15552     {
15553         clsName: 'days',
15554         navFnc: 'Month',
15555         navStep: 1
15556     },
15557     {
15558         clsName: 'months',
15559         navFnc: 'FullYear',
15560         navStep: 1
15561     },
15562     {
15563         clsName: 'years',
15564         navFnc: 'FullYear',
15565         navStep: 10
15566     }]
15567 });
15568
15569 Roo.apply(Roo.bootstrap.DateField,  {
15570   
15571     template : {
15572         tag: 'div',
15573         cls: 'datepicker dropdown-menu roo-dynamic',
15574         cn: [
15575         {
15576             tag: 'div',
15577             cls: 'datepicker-days',
15578             cn: [
15579             {
15580                 tag: 'table',
15581                 cls: 'table-condensed',
15582                 cn:[
15583                 Roo.bootstrap.DateField.head,
15584                 {
15585                     tag: 'tbody'
15586                 },
15587                 Roo.bootstrap.DateField.footer
15588                 ]
15589             }
15590             ]
15591         },
15592         {
15593             tag: 'div',
15594             cls: 'datepicker-months',
15595             cn: [
15596             {
15597                 tag: 'table',
15598                 cls: 'table-condensed',
15599                 cn:[
15600                 Roo.bootstrap.DateField.head,
15601                 Roo.bootstrap.DateField.content,
15602                 Roo.bootstrap.DateField.footer
15603                 ]
15604             }
15605             ]
15606         },
15607         {
15608             tag: 'div',
15609             cls: 'datepicker-years',
15610             cn: [
15611             {
15612                 tag: 'table',
15613                 cls: 'table-condensed',
15614                 cn:[
15615                 Roo.bootstrap.DateField.head,
15616                 Roo.bootstrap.DateField.content,
15617                 Roo.bootstrap.DateField.footer
15618                 ]
15619             }
15620             ]
15621         }
15622         ]
15623     }
15624 });
15625
15626  
15627
15628  /*
15629  * - LGPL
15630  *
15631  * TimeField
15632  * 
15633  */
15634
15635 /**
15636  * @class Roo.bootstrap.TimeField
15637  * @extends Roo.bootstrap.Input
15638  * Bootstrap DateField class
15639  * 
15640  * 
15641  * @constructor
15642  * Create a new TimeField
15643  * @param {Object} config The config object
15644  */
15645
15646 Roo.bootstrap.TimeField = function(config){
15647     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15648     this.addEvents({
15649             /**
15650              * @event show
15651              * Fires when this field show.
15652              * @param {Roo.bootstrap.DateField} this
15653              * @param {Mixed} date The date value
15654              */
15655             show : true,
15656             /**
15657              * @event show
15658              * Fires when this field hide.
15659              * @param {Roo.bootstrap.DateField} this
15660              * @param {Mixed} date The date value
15661              */
15662             hide : true,
15663             /**
15664              * @event select
15665              * Fires when select a date.
15666              * @param {Roo.bootstrap.DateField} this
15667              * @param {Mixed} date The date value
15668              */
15669             select : true
15670         });
15671 };
15672
15673 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15674     
15675     /**
15676      * @cfg {String} format
15677      * The default time format string which can be overriden for localization support.  The format must be
15678      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15679      */
15680     format : "H:i",
15681        
15682     onRender: function(ct, position)
15683     {
15684         
15685         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15686                 
15687         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15688         
15689         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15690         
15691         this.pop = this.picker().select('>.datepicker-time',true).first();
15692         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15693         
15694         this.picker().on('mousedown', this.onMousedown, this);
15695         this.picker().on('click', this.onClick, this);
15696         
15697         this.picker().addClass('datepicker-dropdown');
15698     
15699         this.fillTime();
15700         this.update();
15701             
15702         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15703         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15704         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15705         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15706         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15707         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15708
15709     },
15710     
15711     fireKey: function(e){
15712         if (!this.picker().isVisible()){
15713             if (e.keyCode == 27) // allow escape to hide and re-show picker
15714                 this.show();
15715             return;
15716         }
15717
15718         e.preventDefault();
15719         
15720         switch(e.keyCode){
15721             case 27: // escape
15722                 this.hide();
15723                 break;
15724             case 37: // left
15725             case 39: // right
15726                 this.onTogglePeriod();
15727                 break;
15728             case 38: // up
15729                 this.onIncrementMinutes();
15730                 break;
15731             case 40: // down
15732                 this.onDecrementMinutes();
15733                 break;
15734             case 13: // enter
15735             case 9: // tab
15736                 this.setTime();
15737                 break;
15738         }
15739     },
15740     
15741     onClick: function(e) {
15742         e.stopPropagation();
15743         e.preventDefault();
15744     },
15745     
15746     picker : function()
15747     {
15748         return this.el.select('.datepicker', true).first();
15749     },
15750     
15751     fillTime: function()
15752     {    
15753         var time = this.pop.select('tbody', true).first();
15754         
15755         time.dom.innerHTML = '';
15756         
15757         time.createChild({
15758             tag: 'tr',
15759             cn: [
15760                 {
15761                     tag: 'td',
15762                     cn: [
15763                         {
15764                             tag: 'a',
15765                             href: '#',
15766                             cls: 'btn',
15767                             cn: [
15768                                 {
15769                                     tag: 'span',
15770                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15771                                 }
15772                             ]
15773                         } 
15774                     ]
15775                 },
15776                 {
15777                     tag: 'td',
15778                     cls: 'separator'
15779                 },
15780                 {
15781                     tag: 'td',
15782                     cn: [
15783                         {
15784                             tag: 'a',
15785                             href: '#',
15786                             cls: 'btn',
15787                             cn: [
15788                                 {
15789                                     tag: 'span',
15790                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15791                                 }
15792                             ]
15793                         }
15794                     ]
15795                 },
15796                 {
15797                     tag: 'td',
15798                     cls: 'separator'
15799                 }
15800             ]
15801         });
15802         
15803         time.createChild({
15804             tag: 'tr',
15805             cn: [
15806                 {
15807                     tag: 'td',
15808                     cn: [
15809                         {
15810                             tag: 'span',
15811                             cls: 'timepicker-hour',
15812                             html: '00'
15813                         }  
15814                     ]
15815                 },
15816                 {
15817                     tag: 'td',
15818                     cls: 'separator',
15819                     html: ':'
15820                 },
15821                 {
15822                     tag: 'td',
15823                     cn: [
15824                         {
15825                             tag: 'span',
15826                             cls: 'timepicker-minute',
15827                             html: '00'
15828                         }  
15829                     ]
15830                 },
15831                 {
15832                     tag: 'td',
15833                     cls: 'separator'
15834                 },
15835                 {
15836                     tag: 'td',
15837                     cn: [
15838                         {
15839                             tag: 'button',
15840                             type: 'button',
15841                             cls: 'btn btn-primary period',
15842                             html: 'AM'
15843                             
15844                         }
15845                     ]
15846                 }
15847             ]
15848         });
15849         
15850         time.createChild({
15851             tag: 'tr',
15852             cn: [
15853                 {
15854                     tag: 'td',
15855                     cn: [
15856                         {
15857                             tag: 'a',
15858                             href: '#',
15859                             cls: 'btn',
15860                             cn: [
15861                                 {
15862                                     tag: 'span',
15863                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15864                                 }
15865                             ]
15866                         }
15867                     ]
15868                 },
15869                 {
15870                     tag: 'td',
15871                     cls: 'separator'
15872                 },
15873                 {
15874                     tag: 'td',
15875                     cn: [
15876                         {
15877                             tag: 'a',
15878                             href: '#',
15879                             cls: 'btn',
15880                             cn: [
15881                                 {
15882                                     tag: 'span',
15883                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15884                                 }
15885                             ]
15886                         }
15887                     ]
15888                 },
15889                 {
15890                     tag: 'td',
15891                     cls: 'separator'
15892                 }
15893             ]
15894         });
15895         
15896     },
15897     
15898     update: function()
15899     {
15900         
15901         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15902         
15903         this.fill();
15904     },
15905     
15906     fill: function() 
15907     {
15908         var hours = this.time.getHours();
15909         var minutes = this.time.getMinutes();
15910         var period = 'AM';
15911         
15912         if(hours > 11){
15913             period = 'PM';
15914         }
15915         
15916         if(hours == 0){
15917             hours = 12;
15918         }
15919         
15920         
15921         if(hours > 12){
15922             hours = hours - 12;
15923         }
15924         
15925         if(hours < 10){
15926             hours = '0' + hours;
15927         }
15928         
15929         if(minutes < 10){
15930             minutes = '0' + minutes;
15931         }
15932         
15933         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15934         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15935         this.pop.select('button', true).first().dom.innerHTML = period;
15936         
15937     },
15938     
15939     place: function()
15940     {   
15941         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15942         
15943         var cls = ['bottom'];
15944         
15945         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15946             cls.pop();
15947             cls.push('top');
15948         }
15949         
15950         cls.push('right');
15951         
15952         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15953             cls.pop();
15954             cls.push('left');
15955         }
15956         
15957         this.picker().addClass(cls.join('-'));
15958         
15959         var _this = this;
15960         
15961         Roo.each(cls, function(c){
15962             if(c == 'bottom'){
15963                 _this.picker().setTop(_this.inputEl().getHeight());
15964                 return;
15965             }
15966             if(c == 'top'){
15967                 _this.picker().setTop(0 - _this.picker().getHeight());
15968                 return;
15969             }
15970             
15971             if(c == 'left'){
15972                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15973                 return;
15974             }
15975             if(c == 'right'){
15976                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15977                 return;
15978             }
15979         });
15980         
15981     },
15982   
15983     onFocus : function()
15984     {
15985         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15986         this.show();
15987     },
15988     
15989     onBlur : function()
15990     {
15991         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15992         this.hide();
15993     },
15994     
15995     show : function()
15996     {
15997         this.picker().show();
15998         this.pop.show();
15999         this.update();
16000         this.place();
16001         
16002         this.fireEvent('show', this, this.date);
16003     },
16004     
16005     hide : function()
16006     {
16007         this.picker().hide();
16008         this.pop.hide();
16009         
16010         this.fireEvent('hide', this, this.date);
16011     },
16012     
16013     setTime : function()
16014     {
16015         this.hide();
16016         this.setValue(this.time.format(this.format));
16017         
16018         this.fireEvent('select', this, this.date);
16019         
16020         
16021     },
16022     
16023     onMousedown: function(e){
16024         e.stopPropagation();
16025         e.preventDefault();
16026     },
16027     
16028     onIncrementHours: function()
16029     {
16030         Roo.log('onIncrementHours');
16031         this.time = this.time.add(Date.HOUR, 1);
16032         this.update();
16033         
16034     },
16035     
16036     onDecrementHours: function()
16037     {
16038         Roo.log('onDecrementHours');
16039         this.time = this.time.add(Date.HOUR, -1);
16040         this.update();
16041     },
16042     
16043     onIncrementMinutes: function()
16044     {
16045         Roo.log('onIncrementMinutes');
16046         this.time = this.time.add(Date.MINUTE, 1);
16047         this.update();
16048     },
16049     
16050     onDecrementMinutes: function()
16051     {
16052         Roo.log('onDecrementMinutes');
16053         this.time = this.time.add(Date.MINUTE, -1);
16054         this.update();
16055     },
16056     
16057     onTogglePeriod: function()
16058     {
16059         Roo.log('onTogglePeriod');
16060         this.time = this.time.add(Date.HOUR, 12);
16061         this.update();
16062     }
16063     
16064    
16065 });
16066
16067 Roo.apply(Roo.bootstrap.TimeField,  {
16068     
16069     content : {
16070         tag: 'tbody',
16071         cn: [
16072             {
16073                 tag: 'tr',
16074                 cn: [
16075                 {
16076                     tag: 'td',
16077                     colspan: '7'
16078                 }
16079                 ]
16080             }
16081         ]
16082     },
16083     
16084     footer : {
16085         tag: 'tfoot',
16086         cn: [
16087             {
16088                 tag: 'tr',
16089                 cn: [
16090                 {
16091                     tag: 'th',
16092                     colspan: '7',
16093                     cls: '',
16094                     cn: [
16095                         {
16096                             tag: 'button',
16097                             cls: 'btn btn-info ok',
16098                             html: 'OK'
16099                         }
16100                     ]
16101                 }
16102
16103                 ]
16104             }
16105         ]
16106     }
16107 });
16108
16109 Roo.apply(Roo.bootstrap.TimeField,  {
16110   
16111     template : {
16112         tag: 'div',
16113         cls: 'datepicker dropdown-menu',
16114         cn: [
16115             {
16116                 tag: 'div',
16117                 cls: 'datepicker-time',
16118                 cn: [
16119                 {
16120                     tag: 'table',
16121                     cls: 'table-condensed',
16122                     cn:[
16123                     Roo.bootstrap.TimeField.content,
16124                     Roo.bootstrap.TimeField.footer
16125                     ]
16126                 }
16127                 ]
16128             }
16129         ]
16130     }
16131 });
16132
16133  
16134
16135  /*
16136  * - LGPL
16137  *
16138  * CheckBox
16139  * 
16140  */
16141
16142 /**
16143  * @class Roo.bootstrap.CheckBox
16144  * @extends Roo.bootstrap.Input
16145  * Bootstrap CheckBox class
16146  * 
16147  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
16148  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
16149  * @cfg {String} boxLabel The text that appears beside the checkbox
16150  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
16151  * @cfg {Boolean} checked initnal the element
16152  * @cfg {Boolean} inline inline the element (default false)
16153  * 
16154  * @constructor
16155  * Create a new CheckBox
16156  * @param {Object} config The config object
16157  */
16158
16159 Roo.bootstrap.CheckBox = function(config){
16160     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
16161    
16162         this.addEvents({
16163             /**
16164             * @event check
16165             * Fires when the element is checked or unchecked.
16166             * @param {Roo.bootstrap.CheckBox} this This input
16167             * @param {Boolean} checked The new checked value
16168             */
16169            check : true
16170         });
16171 };
16172
16173 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
16174     
16175     inputType: 'checkbox',
16176     inputValue: 1,
16177     valueOff: 0,
16178     boxLabel: false,
16179     checked: false,
16180     weight : false,
16181     inline: false,
16182     
16183     getAutoCreate : function()
16184     {
16185         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16186         
16187         var id = Roo.id();
16188         
16189         var cfg = {};
16190         
16191         cfg.cls = 'form-group ' + this.inputType //input-group
16192         
16193         if(this.inline){
16194             cfg.cls += ' ' + this.inputType + '-inline';
16195         }
16196         
16197         var input =  {
16198             tag: 'input',
16199             id : id,
16200             type : this.inputType,
16201             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
16202             cls : 'roo-' + this.inputType, //'form-box',
16203             placeholder : this.placeholder || ''
16204             
16205         };
16206         
16207         if (this.weight) { // Validity check?
16208             cfg.cls += " " + this.inputType + "-" + this.weight;
16209         }
16210         
16211         if (this.disabled) {
16212             input.disabled=true;
16213         }
16214         
16215         if(this.checked){
16216             input.checked = this.checked;
16217         }
16218         
16219         if (this.name) {
16220             input.name = this.name;
16221         }
16222         
16223         if (this.size) {
16224             input.cls += ' input-' + this.size;
16225         }
16226         
16227         var settings=this;
16228         ['xs','sm','md','lg'].map(function(size){
16229             if (settings[size]) {
16230                 cfg.cls += ' col-' + size + '-' + settings[size];
16231             }
16232         });
16233         
16234        
16235         
16236         var inputblock = input;
16237         
16238         
16239         
16240         
16241         if (this.before || this.after) {
16242             
16243             inputblock = {
16244                 cls : 'input-group',
16245                 cn :  [] 
16246             };
16247             if (this.before) {
16248                 inputblock.cn.push({
16249                     tag :'span',
16250                     cls : 'input-group-addon',
16251                     html : this.before
16252                 });
16253             }
16254             inputblock.cn.push(input);
16255             if (this.after) {
16256                 inputblock.cn.push({
16257                     tag :'span',
16258                     cls : 'input-group-addon',
16259                     html : this.after
16260                 });
16261             }
16262             
16263         };
16264         
16265         if (align ==='left' && this.fieldLabel.length) {
16266                 Roo.log("left and has label");
16267                 cfg.cn = [
16268                     
16269                     {
16270                         tag: 'label',
16271                         'for' :  id,
16272                         cls : 'control-label col-md-' + this.labelWidth,
16273                         html : this.fieldLabel
16274                         
16275                     },
16276                     {
16277                         cls : "col-md-" + (12 - this.labelWidth), 
16278                         cn: [
16279                             inputblock
16280                         ]
16281                     }
16282                     
16283                 ];
16284         } else if ( this.fieldLabel.length) {
16285                 Roo.log(" label");
16286                 cfg.cn = [
16287                    
16288                     {
16289                         tag: this.boxLabel ? 'span' : 'label',
16290                         'for': id,
16291                         cls: 'control-label box-input-label',
16292                         //cls : 'input-group-addon',
16293                         html : this.fieldLabel
16294                         
16295                     },
16296                     
16297                     inputblock
16298                     
16299                 ];
16300
16301         } else {
16302             
16303                 Roo.log(" no label && no align");
16304                 cfg.cn = [  inputblock ] ;
16305                 
16306                 
16307         };
16308          if(this.boxLabel){
16309              var boxLabelCfg = {
16310                 tag: 'label',
16311                 //'for': id, // box label is handled by onclick - so no for...
16312                 cls: 'box-label',
16313                 html: this.boxLabel
16314             }
16315             
16316             if(this.tooltip){
16317                 boxLabelCfg.tooltip = this.tooltip;
16318             }
16319              
16320             cfg.cn.push(boxLabelCfg);
16321         }
16322         
16323         
16324        
16325         return cfg;
16326         
16327     },
16328     
16329     /**
16330      * return the real input element.
16331      */
16332     inputEl: function ()
16333     {
16334         return this.el.select('input.roo-' + this.inputType,true).first();
16335     },
16336     
16337     labelEl: function()
16338     {
16339         return this.el.select('label.control-label',true).first();
16340     },
16341     /* depricated... */
16342     
16343     label: function()
16344     {
16345         return this.labelEl();
16346     },
16347     
16348     initEvents : function()
16349     {
16350 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16351         
16352         this.inputEl().on('click', this.onClick,  this);
16353         if (this.boxLabel) { 
16354             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
16355         }
16356         
16357     },
16358     
16359     onClick : function()
16360     {   
16361         this.setChecked(!this.checked);
16362     },
16363     
16364     setChecked : function(state,suppressEvent)
16365     {
16366         if(this.inputType == 'radio'){
16367             
16368             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
16369                 e.dom.checked = false;
16370             });
16371             
16372             this.inputEl().dom.checked = true;
16373             
16374             if(suppressEvent !== true){
16375                 this.fireEvent('check', this, true);
16376             }
16377             
16378             this.inputEl().dom.value = this.inputValue;
16379             
16380             return;
16381         }
16382         
16383         this.checked = state;
16384         
16385         if(suppressEvent !== true){
16386             this.fireEvent('check', this, state);
16387         }
16388         
16389         this.inputEl().dom.checked = state;
16390         
16391         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16392         
16393     },
16394     
16395     getValue : function()
16396     {
16397         if(this.inputType == 'radio'){
16398             return this.getGroupValue();
16399         }
16400         
16401         return this.inputEl().getValue();
16402         
16403     },
16404     
16405     getGroupValue : function()
16406     {
16407         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
16408             return '';
16409         }
16410         
16411         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
16412     },
16413     
16414     setValue : function(v,suppressEvent)
16415     {
16416         if(this.inputType == 'radio'){
16417             this.setGroupValue(v, suppressEvent);
16418             return;
16419         }
16420         
16421         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16422     },
16423     
16424     setGroupValue : function(v, suppressEvent)
16425     {
16426         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
16427             e.dom.checked = false;
16428             
16429             if(e.dom.value == v){
16430                 e.dom.checked = true;
16431             }
16432         });
16433         
16434         if(suppressEvent !== true){
16435             this.fireEvent('check', this, true);
16436         }
16437
16438         return;
16439     }
16440     
16441 });
16442
16443  
16444 /*
16445  * - LGPL
16446  *
16447  * Radio
16448  *
16449  *
16450  * not inline
16451  *<div class="radio">
16452   <label>
16453     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
16454     Option one is this and that&mdash;be sure to include why it's great
16455   </label>
16456 </div>
16457  *
16458  *
16459  *inline
16460  *<span>
16461  *<label class="radio-inline">fieldLabel</label>
16462  *<label class="radio-inline">
16463   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
16464 </label>
16465 <span>
16466  * 
16467  * 
16468  */
16469
16470 /**
16471  * @class Roo.bootstrap.Radio
16472  * @extends Roo.bootstrap.CheckBox
16473  * Bootstrap Radio class
16474
16475  * @constructor
16476  * Create a new Radio
16477  * @param {Object} config The config object
16478  */
16479
16480 Roo.bootstrap.Radio = function(config){
16481     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16482    
16483 };
16484
16485 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16486     
16487     inputType: 'radio',
16488     inputValue: '',
16489     valueOff: '',
16490     
16491     getAutoCreate : function()
16492     {
16493         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16494         align = align || 'left'; // default...
16495         
16496         
16497         
16498         var id = Roo.id();
16499         
16500         var cfg = {
16501                 tag : this.inline ? 'span' : 'div',
16502                 cls : '',
16503                 cn : []
16504         };
16505         
16506         var inline = this.inline ? ' radio-inline' : '';
16507         
16508         var lbl = {
16509                 tag: 'label' ,
16510                 // does not need for, as we wrap the input with it..
16511                 'for' : id,
16512                 cls : 'control-label box-label' + inline,
16513                 cn : []
16514         };
16515         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
16516         
16517         var fieldLabel = {
16518             tag: 'label' ,
16519             //cls : 'control-label' + inline,
16520             html : this.fieldLabel,
16521             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
16522         };
16523         
16524  
16525         
16526         
16527         var input =  {
16528             tag: 'input',
16529             id : id,
16530             type : this.inputType,
16531             //value : (!this.checked) ? this.valueOff : this.inputValue,
16532             value : this.inputValue,
16533             cls : 'roo-radio',
16534             placeholder : this.placeholder || '' // ?? needed????
16535             
16536         };
16537         if (this.weight) { // Validity check?
16538             input.cls += " radio-" + this.weight;
16539         }
16540         if (this.disabled) {
16541             input.disabled=true;
16542         }
16543         
16544         if(this.checked){
16545             input.checked = this.checked;
16546         }
16547         
16548         if (this.name) {
16549             input.name = this.name;
16550         }
16551         
16552         if (this.size) {
16553             input.cls += ' input-' + this.size;
16554         }
16555         
16556         //?? can span's inline have a width??
16557         
16558         var settings=this;
16559         ['xs','sm','md','lg'].map(function(size){
16560             if (settings[size]) {
16561                 cfg.cls += ' col-' + size + '-' + settings[size];
16562             }
16563         });
16564         
16565         var inputblock = input;
16566         
16567         if (this.before || this.after) {
16568             
16569             inputblock = {
16570                 cls : 'input-group',
16571                 tag : 'span',
16572                 cn :  [] 
16573             };
16574             if (this.before) {
16575                 inputblock.cn.push({
16576                     tag :'span',
16577                     cls : 'input-group-addon',
16578                     html : this.before
16579                 });
16580             }
16581             inputblock.cn.push(input);
16582             if (this.after) {
16583                 inputblock.cn.push({
16584                     tag :'span',
16585                     cls : 'input-group-addon',
16586                     html : this.after
16587                 });
16588             }
16589             
16590         };
16591         
16592         
16593         if (this.fieldLabel && this.fieldLabel.length) {
16594             cfg.cn.push(fieldLabel);
16595         }
16596        
16597         // normal bootstrap puts the input inside the label.
16598         // however with our styled version - it has to go after the input.
16599        
16600         //lbl.cn.push(inputblock);
16601         
16602         var lblwrap =  {
16603             tag: 'span',
16604             cls: 'radio' + inline,
16605             cn: [
16606                 inputblock,
16607                 lbl
16608             ]
16609         };
16610         
16611         cfg.cn.push( lblwrap);
16612         
16613         if(this.boxLabel){
16614             lbl.cn.push({
16615                 tag: 'span',
16616                 html: this.boxLabel
16617             })
16618         }
16619          
16620         
16621         return cfg;
16622         
16623     },
16624     
16625     initEvents : function()
16626     {
16627 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16628         
16629         this.inputEl().on('click', this.onClick,  this);
16630         if (this.boxLabel) {
16631             Roo.log('find label')
16632             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
16633         }
16634         
16635     },
16636     
16637     inputEl: function ()
16638     {
16639         return this.el.select('input.roo-radio',true).first();
16640     },
16641     onClick : function()
16642     {   
16643         Roo.log("click");
16644         this.setChecked(true);
16645     },
16646     
16647     setChecked : function(state,suppressEvent)
16648     {
16649         if(state){
16650             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16651                 v.dom.checked = false;
16652             });
16653         }
16654         Roo.log(this.inputEl().dom);
16655         this.checked = state;
16656         this.inputEl().dom.checked = state;
16657         
16658         if(suppressEvent !== true){
16659             this.fireEvent('check', this, state);
16660         }
16661         
16662         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16663         
16664     },
16665     
16666     getGroupValue : function()
16667     {
16668         var value = ''
16669         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16670             if(v.dom.checked == true){
16671                 value = v.dom.value;
16672             }
16673         });
16674         
16675         return value;
16676     },
16677     
16678     /**
16679      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16680      * @return {Mixed} value The field value
16681      */
16682     getValue : function(){
16683         return this.getGroupValue();
16684     }
16685     
16686 });
16687
16688  
16689 //<script type="text/javascript">
16690
16691 /*
16692  * Based  Ext JS Library 1.1.1
16693  * Copyright(c) 2006-2007, Ext JS, LLC.
16694  * LGPL
16695  *
16696  */
16697  
16698 /**
16699  * @class Roo.HtmlEditorCore
16700  * @extends Roo.Component
16701  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16702  *
16703  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16704  */
16705
16706 Roo.HtmlEditorCore = function(config){
16707     
16708     
16709     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16710     
16711     
16712     this.addEvents({
16713         /**
16714          * @event initialize
16715          * Fires when the editor is fully initialized (including the iframe)
16716          * @param {Roo.HtmlEditorCore} this
16717          */
16718         initialize: true,
16719         /**
16720          * @event activate
16721          * Fires when the editor is first receives the focus. Any insertion must wait
16722          * until after this event.
16723          * @param {Roo.HtmlEditorCore} this
16724          */
16725         activate: true,
16726          /**
16727          * @event beforesync
16728          * Fires before the textarea is updated with content from the editor iframe. Return false
16729          * to cancel the sync.
16730          * @param {Roo.HtmlEditorCore} this
16731          * @param {String} html
16732          */
16733         beforesync: true,
16734          /**
16735          * @event beforepush
16736          * Fires before the iframe editor is updated with content from the textarea. Return false
16737          * to cancel the push.
16738          * @param {Roo.HtmlEditorCore} this
16739          * @param {String} html
16740          */
16741         beforepush: true,
16742          /**
16743          * @event sync
16744          * Fires when the textarea is updated with content from the editor iframe.
16745          * @param {Roo.HtmlEditorCore} this
16746          * @param {String} html
16747          */
16748         sync: true,
16749          /**
16750          * @event push
16751          * Fires when the iframe editor is updated with content from the textarea.
16752          * @param {Roo.HtmlEditorCore} this
16753          * @param {String} html
16754          */
16755         push: true,
16756         
16757         /**
16758          * @event editorevent
16759          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16760          * @param {Roo.HtmlEditorCore} this
16761          */
16762         editorevent: true
16763         
16764     });
16765     
16766     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16767     
16768     // defaults : white / black...
16769     this.applyBlacklists();
16770     
16771     
16772     
16773 };
16774
16775
16776 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16777
16778
16779      /**
16780      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16781      */
16782     
16783     owner : false,
16784     
16785      /**
16786      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16787      *                        Roo.resizable.
16788      */
16789     resizable : false,
16790      /**
16791      * @cfg {Number} height (in pixels)
16792      */   
16793     height: 300,
16794    /**
16795      * @cfg {Number} width (in pixels)
16796      */   
16797     width: 500,
16798     
16799     /**
16800      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16801      * 
16802      */
16803     stylesheets: false,
16804     
16805     // id of frame..
16806     frameId: false,
16807     
16808     // private properties
16809     validationEvent : false,
16810     deferHeight: true,
16811     initialized : false,
16812     activated : false,
16813     sourceEditMode : false,
16814     onFocus : Roo.emptyFn,
16815     iframePad:3,
16816     hideMode:'offsets',
16817     
16818     clearUp: true,
16819     
16820     // blacklist + whitelisted elements..
16821     black: false,
16822     white: false,
16823      
16824     
16825
16826     /**
16827      * Protected method that will not generally be called directly. It
16828      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16829      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16830      */
16831     getDocMarkup : function(){
16832         // body styles..
16833         var st = '';
16834         
16835         // inherit styels from page...?? 
16836         if (this.stylesheets === false) {
16837             
16838             Roo.get(document.head).select('style').each(function(node) {
16839                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16840             });
16841             
16842             Roo.get(document.head).select('link').each(function(node) { 
16843                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16844             });
16845             
16846         } else if (!this.stylesheets.length) {
16847                 // simple..
16848                 st = '<style type="text/css">' +
16849                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16850                    '</style>';
16851         } else { 
16852             
16853         }
16854         
16855         st +=  '<style type="text/css">' +
16856             'IMG { cursor: pointer } ' +
16857         '</style>';
16858
16859         
16860         return '<html><head>' + st  +
16861             //<style type="text/css">' +
16862             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16863             //'</style>' +
16864             ' </head><body class="roo-htmleditor-body"></body></html>';
16865     },
16866
16867     // private
16868     onRender : function(ct, position)
16869     {
16870         var _t = this;
16871         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16872         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16873         
16874         
16875         this.el.dom.style.border = '0 none';
16876         this.el.dom.setAttribute('tabIndex', -1);
16877         this.el.addClass('x-hidden hide');
16878         
16879         
16880         
16881         if(Roo.isIE){ // fix IE 1px bogus margin
16882             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16883         }
16884        
16885         
16886         this.frameId = Roo.id();
16887         
16888          
16889         
16890         var iframe = this.owner.wrap.createChild({
16891             tag: 'iframe',
16892             cls: 'form-control', // bootstrap..
16893             id: this.frameId,
16894             name: this.frameId,
16895             frameBorder : 'no',
16896             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16897         }, this.el
16898         );
16899         
16900         
16901         this.iframe = iframe.dom;
16902
16903          this.assignDocWin();
16904         
16905         this.doc.designMode = 'on';
16906        
16907         this.doc.open();
16908         this.doc.write(this.getDocMarkup());
16909         this.doc.close();
16910
16911         
16912         var task = { // must defer to wait for browser to be ready
16913             run : function(){
16914                 //console.log("run task?" + this.doc.readyState);
16915                 this.assignDocWin();
16916                 if(this.doc.body || this.doc.readyState == 'complete'){
16917                     try {
16918                         this.doc.designMode="on";
16919                     } catch (e) {
16920                         return;
16921                     }
16922                     Roo.TaskMgr.stop(task);
16923                     this.initEditor.defer(10, this);
16924                 }
16925             },
16926             interval : 10,
16927             duration: 10000,
16928             scope: this
16929         };
16930         Roo.TaskMgr.start(task);
16931
16932     },
16933
16934     // private
16935     onResize : function(w, h)
16936     {
16937          Roo.log('resize: ' +w + ',' + h );
16938         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16939         if(!this.iframe){
16940             return;
16941         }
16942         if(typeof w == 'number'){
16943             
16944             this.iframe.style.width = w + 'px';
16945         }
16946         if(typeof h == 'number'){
16947             
16948             this.iframe.style.height = h + 'px';
16949             if(this.doc){
16950                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16951             }
16952         }
16953         
16954     },
16955
16956     /**
16957      * Toggles the editor between standard and source edit mode.
16958      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16959      */
16960     toggleSourceEdit : function(sourceEditMode){
16961         
16962         this.sourceEditMode = sourceEditMode === true;
16963         
16964         if(this.sourceEditMode){
16965  
16966             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16967             
16968         }else{
16969             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16970             //this.iframe.className = '';
16971             this.deferFocus();
16972         }
16973         //this.setSize(this.owner.wrap.getSize());
16974         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16975     },
16976
16977     
16978   
16979
16980     /**
16981      * Protected method that will not generally be called directly. If you need/want
16982      * custom HTML cleanup, this is the method you should override.
16983      * @param {String} html The HTML to be cleaned
16984      * return {String} The cleaned HTML
16985      */
16986     cleanHtml : function(html){
16987         html = String(html);
16988         if(html.length > 5){
16989             if(Roo.isSafari){ // strip safari nonsense
16990                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16991             }
16992         }
16993         if(html == '&nbsp;'){
16994             html = '';
16995         }
16996         return html;
16997     },
16998
16999     /**
17000      * HTML Editor -> Textarea
17001      * Protected method that will not generally be called directly. Syncs the contents
17002      * of the editor iframe with the textarea.
17003      */
17004     syncValue : function(){
17005         if(this.initialized){
17006             var bd = (this.doc.body || this.doc.documentElement);
17007             //this.cleanUpPaste(); -- this is done else where and causes havoc..
17008             var html = bd.innerHTML;
17009             if(Roo.isSafari){
17010                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
17011                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
17012                 if(m && m[1]){
17013                     html = '<div style="'+m[0]+'">' + html + '</div>';
17014                 }
17015             }
17016             html = this.cleanHtml(html);
17017             // fix up the special chars.. normaly like back quotes in word...
17018             // however we do not want to do this with chinese..
17019             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
17020                 var cc = b.charCodeAt();
17021                 if (
17022                     (cc >= 0x4E00 && cc < 0xA000 ) ||
17023                     (cc >= 0x3400 && cc < 0x4E00 ) ||
17024                     (cc >= 0xf900 && cc < 0xfb00 )
17025                 ) {
17026                         return b;
17027                 }
17028                 return "&#"+cc+";" 
17029             });
17030             if(this.owner.fireEvent('beforesync', this, html) !== false){
17031                 this.el.dom.value = html;
17032                 this.owner.fireEvent('sync', this, html);
17033             }
17034         }
17035     },
17036
17037     /**
17038      * Protected method that will not generally be called directly. Pushes the value of the textarea
17039      * into the iframe editor.
17040      */
17041     pushValue : function(){
17042         if(this.initialized){
17043             var v = this.el.dom.value.trim();
17044             
17045 //            if(v.length < 1){
17046 //                v = '&#160;';
17047 //            }
17048             
17049             if(this.owner.fireEvent('beforepush', this, v) !== false){
17050                 var d = (this.doc.body || this.doc.documentElement);
17051                 d.innerHTML = v;
17052                 this.cleanUpPaste();
17053                 this.el.dom.value = d.innerHTML;
17054                 this.owner.fireEvent('push', this, v);
17055             }
17056         }
17057     },
17058
17059     // private
17060     deferFocus : function(){
17061         this.focus.defer(10, this);
17062     },
17063
17064     // doc'ed in Field
17065     focus : function(){
17066         if(this.win && !this.sourceEditMode){
17067             this.win.focus();
17068         }else{
17069             this.el.focus();
17070         }
17071     },
17072     
17073     assignDocWin: function()
17074     {
17075         var iframe = this.iframe;
17076         
17077          if(Roo.isIE){
17078             this.doc = iframe.contentWindow.document;
17079             this.win = iframe.contentWindow;
17080         } else {
17081 //            if (!Roo.get(this.frameId)) {
17082 //                return;
17083 //            }
17084 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
17085 //            this.win = Roo.get(this.frameId).dom.contentWindow;
17086             
17087             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
17088                 return;
17089             }
17090             
17091             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
17092             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
17093         }
17094     },
17095     
17096     // private
17097     initEditor : function(){
17098         //console.log("INIT EDITOR");
17099         this.assignDocWin();
17100         
17101         
17102         
17103         this.doc.designMode="on";
17104         this.doc.open();
17105         this.doc.write(this.getDocMarkup());
17106         this.doc.close();
17107         
17108         var dbody = (this.doc.body || this.doc.documentElement);
17109         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
17110         // this copies styles from the containing element into thsi one..
17111         // not sure why we need all of this..
17112         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
17113         
17114         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
17115         //ss['background-attachment'] = 'fixed'; // w3c
17116         dbody.bgProperties = 'fixed'; // ie
17117         //Roo.DomHelper.applyStyles(dbody, ss);
17118         Roo.EventManager.on(this.doc, {
17119             //'mousedown': this.onEditorEvent,
17120             'mouseup': this.onEditorEvent,
17121             'dblclick': this.onEditorEvent,
17122             'click': this.onEditorEvent,
17123             'keyup': this.onEditorEvent,
17124             buffer:100,
17125             scope: this
17126         });
17127         if(Roo.isGecko){
17128             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
17129         }
17130         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
17131             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
17132         }
17133         this.initialized = true;
17134
17135         this.owner.fireEvent('initialize', this);
17136         this.pushValue();
17137     },
17138
17139     // private
17140     onDestroy : function(){
17141         
17142         
17143         
17144         if(this.rendered){
17145             
17146             //for (var i =0; i < this.toolbars.length;i++) {
17147             //    // fixme - ask toolbars for heights?
17148             //    this.toolbars[i].onDestroy();
17149            // }
17150             
17151             //this.wrap.dom.innerHTML = '';
17152             //this.wrap.remove();
17153         }
17154     },
17155
17156     // private
17157     onFirstFocus : function(){
17158         
17159         this.assignDocWin();
17160         
17161         
17162         this.activated = true;
17163          
17164     
17165         if(Roo.isGecko){ // prevent silly gecko errors
17166             this.win.focus();
17167             var s = this.win.getSelection();
17168             if(!s.focusNode || s.focusNode.nodeType != 3){
17169                 var r = s.getRangeAt(0);
17170                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
17171                 r.collapse(true);
17172                 this.deferFocus();
17173             }
17174             try{
17175                 this.execCmd('useCSS', true);
17176                 this.execCmd('styleWithCSS', false);
17177             }catch(e){}
17178         }
17179         this.owner.fireEvent('activate', this);
17180     },
17181
17182     // private
17183     adjustFont: function(btn){
17184         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
17185         //if(Roo.isSafari){ // safari
17186         //    adjust *= 2;
17187        // }
17188         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
17189         if(Roo.isSafari){ // safari
17190             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
17191             v =  (v < 10) ? 10 : v;
17192             v =  (v > 48) ? 48 : v;
17193             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
17194             
17195         }
17196         
17197         
17198         v = Math.max(1, v+adjust);
17199         
17200         this.execCmd('FontSize', v  );
17201     },
17202
17203     onEditorEvent : function(e){
17204         this.owner.fireEvent('editorevent', this, e);
17205       //  this.updateToolbar();
17206         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
17207     },
17208
17209     insertTag : function(tg)
17210     {
17211         // could be a bit smarter... -> wrap the current selected tRoo..
17212         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
17213             
17214             range = this.createRange(this.getSelection());
17215             var wrappingNode = this.doc.createElement(tg.toLowerCase());
17216             wrappingNode.appendChild(range.extractContents());
17217             range.insertNode(wrappingNode);
17218
17219             return;
17220             
17221             
17222             
17223         }
17224         this.execCmd("formatblock",   tg);
17225         
17226     },
17227     
17228     insertText : function(txt)
17229     {
17230         
17231         
17232         var range = this.createRange();
17233         range.deleteContents();
17234                //alert(Sender.getAttribute('label'));
17235                
17236         range.insertNode(this.doc.createTextNode(txt));
17237     } ,
17238     
17239      
17240
17241     /**
17242      * Executes a Midas editor command on the editor document and performs necessary focus and
17243      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
17244      * @param {String} cmd The Midas command
17245      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17246      */
17247     relayCmd : function(cmd, value){
17248         this.win.focus();
17249         this.execCmd(cmd, value);
17250         this.owner.fireEvent('editorevent', this);
17251         //this.updateToolbar();
17252         this.owner.deferFocus();
17253     },
17254
17255     /**
17256      * Executes a Midas editor command directly on the editor document.
17257      * For visual commands, you should use {@link #relayCmd} instead.
17258      * <b>This should only be called after the editor is initialized.</b>
17259      * @param {String} cmd The Midas command
17260      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17261      */
17262     execCmd : function(cmd, value){
17263         this.doc.execCommand(cmd, false, value === undefined ? null : value);
17264         this.syncValue();
17265     },
17266  
17267  
17268    
17269     /**
17270      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
17271      * to insert tRoo.
17272      * @param {String} text | dom node.. 
17273      */
17274     insertAtCursor : function(text)
17275     {
17276         
17277         
17278         
17279         if(!this.activated){
17280             return;
17281         }
17282         /*
17283         if(Roo.isIE){
17284             this.win.focus();
17285             var r = this.doc.selection.createRange();
17286             if(r){
17287                 r.collapse(true);
17288                 r.pasteHTML(text);
17289                 this.syncValue();
17290                 this.deferFocus();
17291             
17292             }
17293             return;
17294         }
17295         */
17296         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
17297             this.win.focus();
17298             
17299             
17300             // from jquery ui (MIT licenced)
17301             var range, node;
17302             var win = this.win;
17303             
17304             if (win.getSelection && win.getSelection().getRangeAt) {
17305                 range = win.getSelection().getRangeAt(0);
17306                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
17307                 range.insertNode(node);
17308             } else if (win.document.selection && win.document.selection.createRange) {
17309                 // no firefox support
17310                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17311                 win.document.selection.createRange().pasteHTML(txt);
17312             } else {
17313                 // no firefox support
17314                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17315                 this.execCmd('InsertHTML', txt);
17316             } 
17317             
17318             this.syncValue();
17319             
17320             this.deferFocus();
17321         }
17322     },
17323  // private
17324     mozKeyPress : function(e){
17325         if(e.ctrlKey){
17326             var c = e.getCharCode(), cmd;
17327           
17328             if(c > 0){
17329                 c = String.fromCharCode(c).toLowerCase();
17330                 switch(c){
17331                     case 'b':
17332                         cmd = 'bold';
17333                         break;
17334                     case 'i':
17335                         cmd = 'italic';
17336                         break;
17337                     
17338                     case 'u':
17339                         cmd = 'underline';
17340                         break;
17341                     
17342                     case 'v':
17343                         this.cleanUpPaste.defer(100, this);
17344                         return;
17345                         
17346                 }
17347                 if(cmd){
17348                     this.win.focus();
17349                     this.execCmd(cmd);
17350                     this.deferFocus();
17351                     e.preventDefault();
17352                 }
17353                 
17354             }
17355         }
17356     },
17357
17358     // private
17359     fixKeys : function(){ // load time branching for fastest keydown performance
17360         if(Roo.isIE){
17361             return function(e){
17362                 var k = e.getKey(), r;
17363                 if(k == e.TAB){
17364                     e.stopEvent();
17365                     r = this.doc.selection.createRange();
17366                     if(r){
17367                         r.collapse(true);
17368                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17369                         this.deferFocus();
17370                     }
17371                     return;
17372                 }
17373                 
17374                 if(k == e.ENTER){
17375                     r = this.doc.selection.createRange();
17376                     if(r){
17377                         var target = r.parentElement();
17378                         if(!target || target.tagName.toLowerCase() != 'li'){
17379                             e.stopEvent();
17380                             r.pasteHTML('<br />');
17381                             r.collapse(false);
17382                             r.select();
17383                         }
17384                     }
17385                 }
17386                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17387                     this.cleanUpPaste.defer(100, this);
17388                     return;
17389                 }
17390                 
17391                 
17392             };
17393         }else if(Roo.isOpera){
17394             return function(e){
17395                 var k = e.getKey();
17396                 if(k == e.TAB){
17397                     e.stopEvent();
17398                     this.win.focus();
17399                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17400                     this.deferFocus();
17401                 }
17402                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17403                     this.cleanUpPaste.defer(100, this);
17404                     return;
17405                 }
17406                 
17407             };
17408         }else if(Roo.isSafari){
17409             return function(e){
17410                 var k = e.getKey();
17411                 
17412                 if(k == e.TAB){
17413                     e.stopEvent();
17414                     this.execCmd('InsertText','\t');
17415                     this.deferFocus();
17416                     return;
17417                 }
17418                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17419                     this.cleanUpPaste.defer(100, this);
17420                     return;
17421                 }
17422                 
17423              };
17424         }
17425     }(),
17426     
17427     getAllAncestors: function()
17428     {
17429         var p = this.getSelectedNode();
17430         var a = [];
17431         if (!p) {
17432             a.push(p); // push blank onto stack..
17433             p = this.getParentElement();
17434         }
17435         
17436         
17437         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17438             a.push(p);
17439             p = p.parentNode;
17440         }
17441         a.push(this.doc.body);
17442         return a;
17443     },
17444     lastSel : false,
17445     lastSelNode : false,
17446     
17447     
17448     getSelection : function() 
17449     {
17450         this.assignDocWin();
17451         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17452     },
17453     
17454     getSelectedNode: function() 
17455     {
17456         // this may only work on Gecko!!!
17457         
17458         // should we cache this!!!!
17459         
17460         
17461         
17462          
17463         var range = this.createRange(this.getSelection()).cloneRange();
17464         
17465         if (Roo.isIE) {
17466             var parent = range.parentElement();
17467             while (true) {
17468                 var testRange = range.duplicate();
17469                 testRange.moveToElementText(parent);
17470                 if (testRange.inRange(range)) {
17471                     break;
17472                 }
17473                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17474                     break;
17475                 }
17476                 parent = parent.parentElement;
17477             }
17478             return parent;
17479         }
17480         
17481         // is ancestor a text element.
17482         var ac =  range.commonAncestorContainer;
17483         if (ac.nodeType == 3) {
17484             ac = ac.parentNode;
17485         }
17486         
17487         var ar = ac.childNodes;
17488          
17489         var nodes = [];
17490         var other_nodes = [];
17491         var has_other_nodes = false;
17492         for (var i=0;i<ar.length;i++) {
17493             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17494                 continue;
17495             }
17496             // fullly contained node.
17497             
17498             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17499                 nodes.push(ar[i]);
17500                 continue;
17501             }
17502             
17503             // probably selected..
17504             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17505                 other_nodes.push(ar[i]);
17506                 continue;
17507             }
17508             // outer..
17509             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17510                 continue;
17511             }
17512             
17513             
17514             has_other_nodes = true;
17515         }
17516         if (!nodes.length && other_nodes.length) {
17517             nodes= other_nodes;
17518         }
17519         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17520             return false;
17521         }
17522         
17523         return nodes[0];
17524     },
17525     createRange: function(sel)
17526     {
17527         // this has strange effects when using with 
17528         // top toolbar - not sure if it's a great idea.
17529         //this.editor.contentWindow.focus();
17530         if (typeof sel != "undefined") {
17531             try {
17532                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17533             } catch(e) {
17534                 return this.doc.createRange();
17535             }
17536         } else {
17537             return this.doc.createRange();
17538         }
17539     },
17540     getParentElement: function()
17541     {
17542         
17543         this.assignDocWin();
17544         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17545         
17546         var range = this.createRange(sel);
17547          
17548         try {
17549             var p = range.commonAncestorContainer;
17550             while (p.nodeType == 3) { // text node
17551                 p = p.parentNode;
17552             }
17553             return p;
17554         } catch (e) {
17555             return null;
17556         }
17557     
17558     },
17559     /***
17560      *
17561      * Range intersection.. the hard stuff...
17562      *  '-1' = before
17563      *  '0' = hits..
17564      *  '1' = after.
17565      *         [ -- selected range --- ]
17566      *   [fail]                        [fail]
17567      *
17568      *    basically..
17569      *      if end is before start or  hits it. fail.
17570      *      if start is after end or hits it fail.
17571      *
17572      *   if either hits (but other is outside. - then it's not 
17573      *   
17574      *    
17575      **/
17576     
17577     
17578     // @see http://www.thismuchiknow.co.uk/?p=64.
17579     rangeIntersectsNode : function(range, node)
17580     {
17581         var nodeRange = node.ownerDocument.createRange();
17582         try {
17583             nodeRange.selectNode(node);
17584         } catch (e) {
17585             nodeRange.selectNodeContents(node);
17586         }
17587     
17588         var rangeStartRange = range.cloneRange();
17589         rangeStartRange.collapse(true);
17590     
17591         var rangeEndRange = range.cloneRange();
17592         rangeEndRange.collapse(false);
17593     
17594         var nodeStartRange = nodeRange.cloneRange();
17595         nodeStartRange.collapse(true);
17596     
17597         var nodeEndRange = nodeRange.cloneRange();
17598         nodeEndRange.collapse(false);
17599     
17600         return rangeStartRange.compareBoundaryPoints(
17601                  Range.START_TO_START, nodeEndRange) == -1 &&
17602                rangeEndRange.compareBoundaryPoints(
17603                  Range.START_TO_START, nodeStartRange) == 1;
17604         
17605          
17606     },
17607     rangeCompareNode : function(range, node)
17608     {
17609         var nodeRange = node.ownerDocument.createRange();
17610         try {
17611             nodeRange.selectNode(node);
17612         } catch (e) {
17613             nodeRange.selectNodeContents(node);
17614         }
17615         
17616         
17617         range.collapse(true);
17618     
17619         nodeRange.collapse(true);
17620      
17621         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17622         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17623          
17624         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17625         
17626         var nodeIsBefore   =  ss == 1;
17627         var nodeIsAfter    = ee == -1;
17628         
17629         if (nodeIsBefore && nodeIsAfter)
17630             return 0; // outer
17631         if (!nodeIsBefore && nodeIsAfter)
17632             return 1; //right trailed.
17633         
17634         if (nodeIsBefore && !nodeIsAfter)
17635             return 2;  // left trailed.
17636         // fully contined.
17637         return 3;
17638     },
17639
17640     // private? - in a new class?
17641     cleanUpPaste :  function()
17642     {
17643         // cleans up the whole document..
17644         Roo.log('cleanuppaste');
17645         
17646         this.cleanUpChildren(this.doc.body);
17647         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17648         if (clean != this.doc.body.innerHTML) {
17649             this.doc.body.innerHTML = clean;
17650         }
17651         
17652     },
17653     
17654     cleanWordChars : function(input) {// change the chars to hex code
17655         var he = Roo.HtmlEditorCore;
17656         
17657         var output = input;
17658         Roo.each(he.swapCodes, function(sw) { 
17659             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17660             
17661             output = output.replace(swapper, sw[1]);
17662         });
17663         
17664         return output;
17665     },
17666     
17667     
17668     cleanUpChildren : function (n)
17669     {
17670         if (!n.childNodes.length) {
17671             return;
17672         }
17673         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17674            this.cleanUpChild(n.childNodes[i]);
17675         }
17676     },
17677     
17678     
17679         
17680     
17681     cleanUpChild : function (node)
17682     {
17683         var ed = this;
17684         //console.log(node);
17685         if (node.nodeName == "#text") {
17686             // clean up silly Windows -- stuff?
17687             return; 
17688         }
17689         if (node.nodeName == "#comment") {
17690             node.parentNode.removeChild(node);
17691             // clean up silly Windows -- stuff?
17692             return; 
17693         }
17694         var lcname = node.tagName.toLowerCase();
17695         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17696         // whitelist of tags..
17697         
17698         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17699             // remove node.
17700             node.parentNode.removeChild(node);
17701             return;
17702             
17703         }
17704         
17705         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17706         
17707         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17708         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17709         
17710         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17711         //    remove_keep_children = true;
17712         //}
17713         
17714         if (remove_keep_children) {
17715             this.cleanUpChildren(node);
17716             // inserts everything just before this node...
17717             while (node.childNodes.length) {
17718                 var cn = node.childNodes[0];
17719                 node.removeChild(cn);
17720                 node.parentNode.insertBefore(cn, node);
17721             }
17722             node.parentNode.removeChild(node);
17723             return;
17724         }
17725         
17726         if (!node.attributes || !node.attributes.length) {
17727             this.cleanUpChildren(node);
17728             return;
17729         }
17730         
17731         function cleanAttr(n,v)
17732         {
17733             
17734             if (v.match(/^\./) || v.match(/^\//)) {
17735                 return;
17736             }
17737             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17738                 return;
17739             }
17740             if (v.match(/^#/)) {
17741                 return;
17742             }
17743 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17744             node.removeAttribute(n);
17745             
17746         }
17747         
17748         var cwhite = this.cwhite;
17749         var cblack = this.cblack;
17750             
17751         function cleanStyle(n,v)
17752         {
17753             if (v.match(/expression/)) { //XSS?? should we even bother..
17754                 node.removeAttribute(n);
17755                 return;
17756             }
17757             
17758             var parts = v.split(/;/);
17759             var clean = [];
17760             
17761             Roo.each(parts, function(p) {
17762                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17763                 if (!p.length) {
17764                     return true;
17765                 }
17766                 var l = p.split(':').shift().replace(/\s+/g,'');
17767                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17768                 
17769                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17770 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17771                     //node.removeAttribute(n);
17772                     return true;
17773                 }
17774                 //Roo.log()
17775                 // only allow 'c whitelisted system attributes'
17776                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17777 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17778                     //node.removeAttribute(n);
17779                     return true;
17780                 }
17781                 
17782                 
17783                  
17784                 
17785                 clean.push(p);
17786                 return true;
17787             });
17788             if (clean.length) { 
17789                 node.setAttribute(n, clean.join(';'));
17790             } else {
17791                 node.removeAttribute(n);
17792             }
17793             
17794         }
17795         
17796         
17797         for (var i = node.attributes.length-1; i > -1 ; i--) {
17798             var a = node.attributes[i];
17799             //console.log(a);
17800             
17801             if (a.name.toLowerCase().substr(0,2)=='on')  {
17802                 node.removeAttribute(a.name);
17803                 continue;
17804             }
17805             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17806                 node.removeAttribute(a.name);
17807                 continue;
17808             }
17809             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17810                 cleanAttr(a.name,a.value); // fixme..
17811                 continue;
17812             }
17813             if (a.name == 'style') {
17814                 cleanStyle(a.name,a.value);
17815                 continue;
17816             }
17817             /// clean up MS crap..
17818             // tecnically this should be a list of valid class'es..
17819             
17820             
17821             if (a.name == 'class') {
17822                 if (a.value.match(/^Mso/)) {
17823                     node.className = '';
17824                 }
17825                 
17826                 if (a.value.match(/body/)) {
17827                     node.className = '';
17828                 }
17829                 continue;
17830             }
17831             
17832             // style cleanup!?
17833             // class cleanup?
17834             
17835         }
17836         
17837         
17838         this.cleanUpChildren(node);
17839         
17840         
17841     },
17842     /**
17843      * Clean up MS wordisms...
17844      */
17845     cleanWord : function(node)
17846     {
17847         var _t = this;
17848         var cleanWordChildren = function()
17849         {
17850             if (!node.childNodes.length) {
17851                 return;
17852             }
17853             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17854                _t.cleanWord(node.childNodes[i]);
17855             }
17856         }
17857         
17858         
17859         if (!node) {
17860             this.cleanWord(this.doc.body);
17861             return;
17862         }
17863         if (node.nodeName == "#text") {
17864             // clean up silly Windows -- stuff?
17865             return; 
17866         }
17867         if (node.nodeName == "#comment") {
17868             node.parentNode.removeChild(node);
17869             // clean up silly Windows -- stuff?
17870             return; 
17871         }
17872         
17873         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17874             node.parentNode.removeChild(node);
17875             return;
17876         }
17877         
17878         // remove - but keep children..
17879         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17880             while (node.childNodes.length) {
17881                 var cn = node.childNodes[0];
17882                 node.removeChild(cn);
17883                 node.parentNode.insertBefore(cn, node);
17884             }
17885             node.parentNode.removeChild(node);
17886             cleanWordChildren();
17887             return;
17888         }
17889         // clean styles
17890         if (node.className.length) {
17891             
17892             var cn = node.className.split(/\W+/);
17893             var cna = [];
17894             Roo.each(cn, function(cls) {
17895                 if (cls.match(/Mso[a-zA-Z]+/)) {
17896                     return;
17897                 }
17898                 cna.push(cls);
17899             });
17900             node.className = cna.length ? cna.join(' ') : '';
17901             if (!cna.length) {
17902                 node.removeAttribute("class");
17903             }
17904         }
17905         
17906         if (node.hasAttribute("lang")) {
17907             node.removeAttribute("lang");
17908         }
17909         
17910         if (node.hasAttribute("style")) {
17911             
17912             var styles = node.getAttribute("style").split(";");
17913             var nstyle = [];
17914             Roo.each(styles, function(s) {
17915                 if (!s.match(/:/)) {
17916                     return;
17917                 }
17918                 var kv = s.split(":");
17919                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17920                     return;
17921                 }
17922                 // what ever is left... we allow.
17923                 nstyle.push(s);
17924             });
17925             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17926             if (!nstyle.length) {
17927                 node.removeAttribute('style');
17928             }
17929         }
17930         
17931         cleanWordChildren();
17932         
17933         
17934     },
17935     domToHTML : function(currentElement, depth, nopadtext) {
17936         
17937         depth = depth || 0;
17938         nopadtext = nopadtext || false;
17939     
17940         if (!currentElement) {
17941             return this.domToHTML(this.doc.body);
17942         }
17943         
17944         //Roo.log(currentElement);
17945         var j;
17946         var allText = false;
17947         var nodeName = currentElement.nodeName;
17948         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17949         
17950         if  (nodeName == '#text') {
17951             
17952             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
17953         }
17954         
17955         
17956         var ret = '';
17957         if (nodeName != 'BODY') {
17958              
17959             var i = 0;
17960             // Prints the node tagName, such as <A>, <IMG>, etc
17961             if (tagName) {
17962                 var attr = [];
17963                 for(i = 0; i < currentElement.attributes.length;i++) {
17964                     // quoting?
17965                     var aname = currentElement.attributes.item(i).name;
17966                     if (!currentElement.attributes.item(i).value.length) {
17967                         continue;
17968                     }
17969                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17970                 }
17971                 
17972                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17973             } 
17974             else {
17975                 
17976                 // eack
17977             }
17978         } else {
17979             tagName = false;
17980         }
17981         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17982             return ret;
17983         }
17984         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17985             nopadtext = true;
17986         }
17987         
17988         
17989         // Traverse the tree
17990         i = 0;
17991         var currentElementChild = currentElement.childNodes.item(i);
17992         var allText = true;
17993         var innerHTML  = '';
17994         lastnode = '';
17995         while (currentElementChild) {
17996             // Formatting code (indent the tree so it looks nice on the screen)
17997             var nopad = nopadtext;
17998             if (lastnode == 'SPAN') {
17999                 nopad  = true;
18000             }
18001             // text
18002             if  (currentElementChild.nodeName == '#text') {
18003                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
18004                 toadd = nopadtext ? toadd : toadd.trim();
18005                 if (!nopad && toadd.length > 80) {
18006                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
18007                 }
18008                 innerHTML  += toadd;
18009                 
18010                 i++;
18011                 currentElementChild = currentElement.childNodes.item(i);
18012                 lastNode = '';
18013                 continue;
18014             }
18015             allText = false;
18016             
18017             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
18018                 
18019             // Recursively traverse the tree structure of the child node
18020             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
18021             lastnode = currentElementChild.nodeName;
18022             i++;
18023             currentElementChild=currentElement.childNodes.item(i);
18024         }
18025         
18026         ret += innerHTML;
18027         
18028         if (!allText) {
18029                 // The remaining code is mostly for formatting the tree
18030             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
18031         }
18032         
18033         
18034         if (tagName) {
18035             ret+= "</"+tagName+">";
18036         }
18037         return ret;
18038         
18039     },
18040         
18041     applyBlacklists : function()
18042     {
18043         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
18044         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
18045         
18046         this.white = [];
18047         this.black = [];
18048         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
18049             if (b.indexOf(tag) > -1) {
18050                 return;
18051             }
18052             this.white.push(tag);
18053             
18054         }, this);
18055         
18056         Roo.each(w, function(tag) {
18057             if (b.indexOf(tag) > -1) {
18058                 return;
18059             }
18060             if (this.white.indexOf(tag) > -1) {
18061                 return;
18062             }
18063             this.white.push(tag);
18064             
18065         }, this);
18066         
18067         
18068         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
18069             if (w.indexOf(tag) > -1) {
18070                 return;
18071             }
18072             this.black.push(tag);
18073             
18074         }, this);
18075         
18076         Roo.each(b, function(tag) {
18077             if (w.indexOf(tag) > -1) {
18078                 return;
18079             }
18080             if (this.black.indexOf(tag) > -1) {
18081                 return;
18082             }
18083             this.black.push(tag);
18084             
18085         }, this);
18086         
18087         
18088         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
18089         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
18090         
18091         this.cwhite = [];
18092         this.cblack = [];
18093         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
18094             if (b.indexOf(tag) > -1) {
18095                 return;
18096             }
18097             this.cwhite.push(tag);
18098             
18099         }, this);
18100         
18101         Roo.each(w, function(tag) {
18102             if (b.indexOf(tag) > -1) {
18103                 return;
18104             }
18105             if (this.cwhite.indexOf(tag) > -1) {
18106                 return;
18107             }
18108             this.cwhite.push(tag);
18109             
18110         }, this);
18111         
18112         
18113         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
18114             if (w.indexOf(tag) > -1) {
18115                 return;
18116             }
18117             this.cblack.push(tag);
18118             
18119         }, this);
18120         
18121         Roo.each(b, function(tag) {
18122             if (w.indexOf(tag) > -1) {
18123                 return;
18124             }
18125             if (this.cblack.indexOf(tag) > -1) {
18126                 return;
18127             }
18128             this.cblack.push(tag);
18129             
18130         }, this);
18131     },
18132     
18133     setStylesheets : function(stylesheets)
18134     {
18135         if(typeof(stylesheets) == 'string'){
18136             Roo.get(this.iframe.contentDocument.head).createChild({
18137                 tag : 'link',
18138                 rel : 'stylesheet',
18139                 type : 'text/css',
18140                 href : stylesheets
18141             });
18142             
18143             return;
18144         }
18145         var _this = this;
18146      
18147         Roo.each(stylesheets, function(s) {
18148             if(!s.length){
18149                 return;
18150             }
18151             
18152             Roo.get(_this.iframe.contentDocument.head).createChild({
18153                 tag : 'link',
18154                 rel : 'stylesheet',
18155                 type : 'text/css',
18156                 href : s
18157             });
18158         });
18159
18160         
18161     },
18162     
18163     removeStylesheets : function()
18164     {
18165         var _this = this;
18166         
18167         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
18168             s.remove();
18169         });
18170     }
18171     
18172     // hide stuff that is not compatible
18173     /**
18174      * @event blur
18175      * @hide
18176      */
18177     /**
18178      * @event change
18179      * @hide
18180      */
18181     /**
18182      * @event focus
18183      * @hide
18184      */
18185     /**
18186      * @event specialkey
18187      * @hide
18188      */
18189     /**
18190      * @cfg {String} fieldClass @hide
18191      */
18192     /**
18193      * @cfg {String} focusClass @hide
18194      */
18195     /**
18196      * @cfg {String} autoCreate @hide
18197      */
18198     /**
18199      * @cfg {String} inputType @hide
18200      */
18201     /**
18202      * @cfg {String} invalidClass @hide
18203      */
18204     /**
18205      * @cfg {String} invalidText @hide
18206      */
18207     /**
18208      * @cfg {String} msgFx @hide
18209      */
18210     /**
18211      * @cfg {String} validateOnBlur @hide
18212      */
18213 });
18214
18215 Roo.HtmlEditorCore.white = [
18216         'area', 'br', 'img', 'input', 'hr', 'wbr',
18217         
18218        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
18219        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
18220        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
18221        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
18222        'table',   'ul',         'xmp', 
18223        
18224        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
18225       'thead',   'tr', 
18226      
18227       'dir', 'menu', 'ol', 'ul', 'dl',
18228        
18229       'embed',  'object'
18230 ];
18231
18232
18233 Roo.HtmlEditorCore.black = [
18234     //    'embed',  'object', // enable - backend responsiblity to clean thiese
18235         'applet', // 
18236         'base',   'basefont', 'bgsound', 'blink',  'body', 
18237         'frame',  'frameset', 'head',    'html',   'ilayer', 
18238         'iframe', 'layer',  'link',     'meta',    'object',   
18239         'script', 'style' ,'title',  'xml' // clean later..
18240 ];
18241 Roo.HtmlEditorCore.clean = [
18242     'script', 'style', 'title', 'xml'
18243 ];
18244 Roo.HtmlEditorCore.remove = [
18245     'font'
18246 ];
18247 // attributes..
18248
18249 Roo.HtmlEditorCore.ablack = [
18250     'on'
18251 ];
18252     
18253 Roo.HtmlEditorCore.aclean = [ 
18254     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
18255 ];
18256
18257 // protocols..
18258 Roo.HtmlEditorCore.pwhite= [
18259         'http',  'https',  'mailto'
18260 ];
18261
18262 // white listed style attributes.
18263 Roo.HtmlEditorCore.cwhite= [
18264       //  'text-align', /// default is to allow most things..
18265       
18266          
18267 //        'font-size'//??
18268 ];
18269
18270 // black listed style attributes.
18271 Roo.HtmlEditorCore.cblack= [
18272       //  'font-size' -- this can be set by the project 
18273 ];
18274
18275
18276 Roo.HtmlEditorCore.swapCodes   =[ 
18277     [    8211, "--" ], 
18278     [    8212, "--" ], 
18279     [    8216,  "'" ],  
18280     [    8217, "'" ],  
18281     [    8220, '"' ],  
18282     [    8221, '"' ],  
18283     [    8226, "*" ],  
18284     [    8230, "..." ]
18285 ]; 
18286
18287     /*
18288  * - LGPL
18289  *
18290  * HtmlEditor
18291  * 
18292  */
18293
18294 /**
18295  * @class Roo.bootstrap.HtmlEditor
18296  * @extends Roo.bootstrap.TextArea
18297  * Bootstrap HtmlEditor class
18298
18299  * @constructor
18300  * Create a new HtmlEditor
18301  * @param {Object} config The config object
18302  */
18303
18304 Roo.bootstrap.HtmlEditor = function(config){
18305     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
18306     if (!this.toolbars) {
18307         this.toolbars = [];
18308     }
18309     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
18310     this.addEvents({
18311             /**
18312              * @event initialize
18313              * Fires when the editor is fully initialized (including the iframe)
18314              * @param {HtmlEditor} this
18315              */
18316             initialize: true,
18317             /**
18318              * @event activate
18319              * Fires when the editor is first receives the focus. Any insertion must wait
18320              * until after this event.
18321              * @param {HtmlEditor} this
18322              */
18323             activate: true,
18324              /**
18325              * @event beforesync
18326              * Fires before the textarea is updated with content from the editor iframe. Return false
18327              * to cancel the sync.
18328              * @param {HtmlEditor} this
18329              * @param {String} html
18330              */
18331             beforesync: true,
18332              /**
18333              * @event beforepush
18334              * Fires before the iframe editor is updated with content from the textarea. Return false
18335              * to cancel the push.
18336              * @param {HtmlEditor} this
18337              * @param {String} html
18338              */
18339             beforepush: true,
18340              /**
18341              * @event sync
18342              * Fires when the textarea is updated with content from the editor iframe.
18343              * @param {HtmlEditor} this
18344              * @param {String} html
18345              */
18346             sync: true,
18347              /**
18348              * @event push
18349              * Fires when the iframe editor is updated with content from the textarea.
18350              * @param {HtmlEditor} this
18351              * @param {String} html
18352              */
18353             push: true,
18354              /**
18355              * @event editmodechange
18356              * Fires when the editor switches edit modes
18357              * @param {HtmlEditor} this
18358              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
18359              */
18360             editmodechange: true,
18361             /**
18362              * @event editorevent
18363              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18364              * @param {HtmlEditor} this
18365              */
18366             editorevent: true,
18367             /**
18368              * @event firstfocus
18369              * Fires when on first focus - needed by toolbars..
18370              * @param {HtmlEditor} this
18371              */
18372             firstfocus: true,
18373             /**
18374              * @event autosave
18375              * Auto save the htmlEditor value as a file into Events
18376              * @param {HtmlEditor} this
18377              */
18378             autosave: true,
18379             /**
18380              * @event savedpreview
18381              * preview the saved version of htmlEditor
18382              * @param {HtmlEditor} this
18383              */
18384             savedpreview: true
18385         });
18386 };
18387
18388
18389 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
18390     
18391     
18392       /**
18393      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18394      */
18395     toolbars : false,
18396    
18397      /**
18398      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18399      *                        Roo.resizable.
18400      */
18401     resizable : false,
18402      /**
18403      * @cfg {Number} height (in pixels)
18404      */   
18405     height: 300,
18406    /**
18407      * @cfg {Number} width (in pixels)
18408      */   
18409     width: false,
18410     
18411     /**
18412      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18413      * 
18414      */
18415     stylesheets: false,
18416     
18417     // id of frame..
18418     frameId: false,
18419     
18420     // private properties
18421     validationEvent : false,
18422     deferHeight: true,
18423     initialized : false,
18424     activated : false,
18425     
18426     onFocus : Roo.emptyFn,
18427     iframePad:3,
18428     hideMode:'offsets',
18429     
18430     
18431     tbContainer : false,
18432     
18433     toolbarContainer :function() {
18434         return this.wrap.select('.x-html-editor-tb',true).first();
18435     },
18436
18437     /**
18438      * Protected method that will not generally be called directly. It
18439      * is called when the editor creates its toolbar. Override this method if you need to
18440      * add custom toolbar buttons.
18441      * @param {HtmlEditor} editor
18442      */
18443     createToolbar : function(){
18444         
18445         Roo.log("create toolbars");
18446         
18447         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18448         this.toolbars[0].render(this.toolbarContainer());
18449         
18450         return;
18451         
18452 //        if (!editor.toolbars || !editor.toolbars.length) {
18453 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18454 //        }
18455 //        
18456 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18457 //            editor.toolbars[i] = Roo.factory(
18458 //                    typeof(editor.toolbars[i]) == 'string' ?
18459 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18460 //                Roo.bootstrap.HtmlEditor);
18461 //            editor.toolbars[i].init(editor);
18462 //        }
18463     },
18464
18465      
18466     // private
18467     onRender : function(ct, position)
18468     {
18469        // Roo.log("Call onRender: " + this.xtype);
18470         var _t = this;
18471         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18472       
18473         this.wrap = this.inputEl().wrap({
18474             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18475         });
18476         
18477         this.editorcore.onRender(ct, position);
18478          
18479         if (this.resizable) {
18480             this.resizeEl = new Roo.Resizable(this.wrap, {
18481                 pinned : true,
18482                 wrap: true,
18483                 dynamic : true,
18484                 minHeight : this.height,
18485                 height: this.height,
18486                 handles : this.resizable,
18487                 width: this.width,
18488                 listeners : {
18489                     resize : function(r, w, h) {
18490                         _t.onResize(w,h); // -something
18491                     }
18492                 }
18493             });
18494             
18495         }
18496         this.createToolbar(this);
18497        
18498         
18499         if(!this.width && this.resizable){
18500             this.setSize(this.wrap.getSize());
18501         }
18502         if (this.resizeEl) {
18503             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18504             // should trigger onReize..
18505         }
18506         
18507     },
18508
18509     // private
18510     onResize : function(w, h)
18511     {
18512         Roo.log('resize: ' +w + ',' + h );
18513         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18514         var ew = false;
18515         var eh = false;
18516         
18517         if(this.inputEl() ){
18518             if(typeof w == 'number'){
18519                 var aw = w - this.wrap.getFrameWidth('lr');
18520                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18521                 ew = aw;
18522             }
18523             if(typeof h == 'number'){
18524                  var tbh = -11;  // fixme it needs to tool bar size!
18525                 for (var i =0; i < this.toolbars.length;i++) {
18526                     // fixme - ask toolbars for heights?
18527                     tbh += this.toolbars[i].el.getHeight();
18528                     //if (this.toolbars[i].footer) {
18529                     //    tbh += this.toolbars[i].footer.el.getHeight();
18530                     //}
18531                 }
18532               
18533                 
18534                 
18535                 
18536                 
18537                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18538                 ah -= 5; // knock a few pixes off for look..
18539                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18540                 var eh = ah;
18541             }
18542         }
18543         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18544         this.editorcore.onResize(ew,eh);
18545         
18546     },
18547
18548     /**
18549      * Toggles the editor between standard and source edit mode.
18550      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18551      */
18552     toggleSourceEdit : function(sourceEditMode)
18553     {
18554         this.editorcore.toggleSourceEdit(sourceEditMode);
18555         
18556         if(this.editorcore.sourceEditMode){
18557             Roo.log('editor - showing textarea');
18558             
18559 //            Roo.log('in');
18560 //            Roo.log(this.syncValue());
18561             this.syncValue();
18562             this.inputEl().removeClass(['hide', 'x-hidden']);
18563             this.inputEl().dom.removeAttribute('tabIndex');
18564             this.inputEl().focus();
18565         }else{
18566             Roo.log('editor - hiding textarea');
18567 //            Roo.log('out')
18568 //            Roo.log(this.pushValue()); 
18569             this.pushValue();
18570             
18571             this.inputEl().addClass(['hide', 'x-hidden']);
18572             this.inputEl().dom.setAttribute('tabIndex', -1);
18573             //this.deferFocus();
18574         }
18575          
18576         if(this.resizable){
18577             this.setSize(this.wrap.getSize());
18578         }
18579         
18580         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18581     },
18582  
18583     // private (for BoxComponent)
18584     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18585
18586     // private (for BoxComponent)
18587     getResizeEl : function(){
18588         return this.wrap;
18589     },
18590
18591     // private (for BoxComponent)
18592     getPositionEl : function(){
18593         return this.wrap;
18594     },
18595
18596     // private
18597     initEvents : function(){
18598         this.originalValue = this.getValue();
18599     },
18600
18601 //    /**
18602 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18603 //     * @method
18604 //     */
18605 //    markInvalid : Roo.emptyFn,
18606 //    /**
18607 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18608 //     * @method
18609 //     */
18610 //    clearInvalid : Roo.emptyFn,
18611
18612     setValue : function(v){
18613         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18614         this.editorcore.pushValue();
18615     },
18616
18617      
18618     // private
18619     deferFocus : function(){
18620         this.focus.defer(10, this);
18621     },
18622
18623     // doc'ed in Field
18624     focus : function(){
18625         this.editorcore.focus();
18626         
18627     },
18628       
18629
18630     // private
18631     onDestroy : function(){
18632         
18633         
18634         
18635         if(this.rendered){
18636             
18637             for (var i =0; i < this.toolbars.length;i++) {
18638                 // fixme - ask toolbars for heights?
18639                 this.toolbars[i].onDestroy();
18640             }
18641             
18642             this.wrap.dom.innerHTML = '';
18643             this.wrap.remove();
18644         }
18645     },
18646
18647     // private
18648     onFirstFocus : function(){
18649         //Roo.log("onFirstFocus");
18650         this.editorcore.onFirstFocus();
18651          for (var i =0; i < this.toolbars.length;i++) {
18652             this.toolbars[i].onFirstFocus();
18653         }
18654         
18655     },
18656     
18657     // private
18658     syncValue : function()
18659     {   
18660         this.editorcore.syncValue();
18661     },
18662     
18663     pushValue : function()
18664     {   
18665         this.editorcore.pushValue();
18666     }
18667      
18668     
18669     // hide stuff that is not compatible
18670     /**
18671      * @event blur
18672      * @hide
18673      */
18674     /**
18675      * @event change
18676      * @hide
18677      */
18678     /**
18679      * @event focus
18680      * @hide
18681      */
18682     /**
18683      * @event specialkey
18684      * @hide
18685      */
18686     /**
18687      * @cfg {String} fieldClass @hide
18688      */
18689     /**
18690      * @cfg {String} focusClass @hide
18691      */
18692     /**
18693      * @cfg {String} autoCreate @hide
18694      */
18695     /**
18696      * @cfg {String} inputType @hide
18697      */
18698     /**
18699      * @cfg {String} invalidClass @hide
18700      */
18701     /**
18702      * @cfg {String} invalidText @hide
18703      */
18704     /**
18705      * @cfg {String} msgFx @hide
18706      */
18707     /**
18708      * @cfg {String} validateOnBlur @hide
18709      */
18710 });
18711  
18712     
18713    
18714    
18715    
18716       
18717 Roo.namespace('Roo.bootstrap.htmleditor');
18718 /**
18719  * @class Roo.bootstrap.HtmlEditorToolbar1
18720  * Basic Toolbar
18721  * 
18722  * Usage:
18723  *
18724  new Roo.bootstrap.HtmlEditor({
18725     ....
18726     toolbars : [
18727         new Roo.bootstrap.HtmlEditorToolbar1({
18728             disable : { fonts: 1 , format: 1, ..., ... , ...],
18729             btns : [ .... ]
18730         })
18731     }
18732      
18733  * 
18734  * @cfg {Object} disable List of elements to disable..
18735  * @cfg {Array} btns List of additional buttons.
18736  * 
18737  * 
18738  * NEEDS Extra CSS? 
18739  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18740  */
18741  
18742 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18743 {
18744     
18745     Roo.apply(this, config);
18746     
18747     // default disabled, based on 'good practice'..
18748     this.disable = this.disable || {};
18749     Roo.applyIf(this.disable, {
18750         fontSize : true,
18751         colors : true,
18752         specialElements : true
18753     });
18754     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18755     
18756     this.editor = config.editor;
18757     this.editorcore = config.editor.editorcore;
18758     
18759     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18760     
18761     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18762     // dont call parent... till later.
18763 }
18764 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18765      
18766     bar : true,
18767     
18768     editor : false,
18769     editorcore : false,
18770     
18771     
18772     formats : [
18773         "p" ,  
18774         "h1","h2","h3","h4","h5","h6", 
18775         "pre", "code", 
18776         "abbr", "acronym", "address", "cite", "samp", "var",
18777         'div','span'
18778     ],
18779     
18780     onRender : function(ct, position)
18781     {
18782        // Roo.log("Call onRender: " + this.xtype);
18783         
18784        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18785        Roo.log(this.el);
18786        this.el.dom.style.marginBottom = '0';
18787        var _this = this;
18788        var editorcore = this.editorcore;
18789        var editor= this.editor;
18790        
18791        var children = [];
18792        var btn = function(id,cmd , toggle, handler){
18793        
18794             var  event = toggle ? 'toggle' : 'click';
18795        
18796             var a = {
18797                 size : 'sm',
18798                 xtype: 'Button',
18799                 xns: Roo.bootstrap,
18800                 glyphicon : id,
18801                 cmd : id || cmd,
18802                 enableToggle:toggle !== false,
18803                 //html : 'submit'
18804                 pressed : toggle ? false : null,
18805                 listeners : {}
18806             }
18807             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18808                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18809             }
18810             children.push(a);
18811             return a;
18812        }
18813         
18814         var style = {
18815                 xtype: 'Button',
18816                 size : 'sm',
18817                 xns: Roo.bootstrap,
18818                 glyphicon : 'font',
18819                 //html : 'submit'
18820                 menu : {
18821                     xtype: 'Menu',
18822                     xns: Roo.bootstrap,
18823                     items:  []
18824                 }
18825         };
18826         Roo.each(this.formats, function(f) {
18827             style.menu.items.push({
18828                 xtype :'MenuItem',
18829                 xns: Roo.bootstrap,
18830                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18831                 tagname : f,
18832                 listeners : {
18833                     click : function()
18834                     {
18835                         editorcore.insertTag(this.tagname);
18836                         editor.focus();
18837                     }
18838                 }
18839                 
18840             });
18841         });
18842          children.push(style);   
18843             
18844             
18845         btn('bold',false,true);
18846         btn('italic',false,true);
18847         btn('align-left', 'justifyleft',true);
18848         btn('align-center', 'justifycenter',true);
18849         btn('align-right' , 'justifyright',true);
18850         btn('link', false, false, function(btn) {
18851             //Roo.log("create link?");
18852             var url = prompt(this.createLinkText, this.defaultLinkValue);
18853             if(url && url != 'http:/'+'/'){
18854                 this.editorcore.relayCmd('createlink', url);
18855             }
18856         }),
18857         btn('list','insertunorderedlist',true);
18858         btn('pencil', false,true, function(btn){
18859                 Roo.log(this);
18860                 
18861                 this.toggleSourceEdit(btn.pressed);
18862         });
18863         /*
18864         var cog = {
18865                 xtype: 'Button',
18866                 size : 'sm',
18867                 xns: Roo.bootstrap,
18868                 glyphicon : 'cog',
18869                 //html : 'submit'
18870                 menu : {
18871                     xtype: 'Menu',
18872                     xns: Roo.bootstrap,
18873                     items:  []
18874                 }
18875         };
18876         
18877         cog.menu.items.push({
18878             xtype :'MenuItem',
18879             xns: Roo.bootstrap,
18880             html : Clean styles,
18881             tagname : f,
18882             listeners : {
18883                 click : function()
18884                 {
18885                     editorcore.insertTag(this.tagname);
18886                     editor.focus();
18887                 }
18888             }
18889             
18890         });
18891        */
18892         
18893          
18894        this.xtype = 'NavSimplebar';
18895         
18896         for(var i=0;i< children.length;i++) {
18897             
18898             this.buttons.add(this.addxtypeChild(children[i]));
18899             
18900         }
18901         
18902         editor.on('editorevent', this.updateToolbar, this);
18903     },
18904     onBtnClick : function(id)
18905     {
18906        this.editorcore.relayCmd(id);
18907        this.editorcore.focus();
18908     },
18909     
18910     /**
18911      * Protected method that will not generally be called directly. It triggers
18912      * a toolbar update by reading the markup state of the current selection in the editor.
18913      */
18914     updateToolbar: function(){
18915
18916         if(!this.editorcore.activated){
18917             this.editor.onFirstFocus(); // is this neeed?
18918             return;
18919         }
18920
18921         var btns = this.buttons; 
18922         var doc = this.editorcore.doc;
18923         btns.get('bold').setActive(doc.queryCommandState('bold'));
18924         btns.get('italic').setActive(doc.queryCommandState('italic'));
18925         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18926         
18927         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18928         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18929         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18930         
18931         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18932         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18933          /*
18934         
18935         var ans = this.editorcore.getAllAncestors();
18936         if (this.formatCombo) {
18937             
18938             
18939             var store = this.formatCombo.store;
18940             this.formatCombo.setValue("");
18941             for (var i =0; i < ans.length;i++) {
18942                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18943                     // select it..
18944                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18945                     break;
18946                 }
18947             }
18948         }
18949         
18950         
18951         
18952         // hides menus... - so this cant be on a menu...
18953         Roo.bootstrap.MenuMgr.hideAll();
18954         */
18955         Roo.bootstrap.MenuMgr.hideAll();
18956         //this.editorsyncValue();
18957     },
18958     onFirstFocus: function() {
18959         this.buttons.each(function(item){
18960            item.enable();
18961         });
18962     },
18963     toggleSourceEdit : function(sourceEditMode){
18964         
18965           
18966         if(sourceEditMode){
18967             Roo.log("disabling buttons");
18968            this.buttons.each( function(item){
18969                 if(item.cmd != 'pencil'){
18970                     item.disable();
18971                 }
18972             });
18973           
18974         }else{
18975             Roo.log("enabling buttons");
18976             if(this.editorcore.initialized){
18977                 this.buttons.each( function(item){
18978                     item.enable();
18979                 });
18980             }
18981             
18982         }
18983         Roo.log("calling toggole on editor");
18984         // tell the editor that it's been pressed..
18985         this.editor.toggleSourceEdit(sourceEditMode);
18986        
18987     }
18988 });
18989
18990
18991
18992
18993
18994 /**
18995  * @class Roo.bootstrap.Table.AbstractSelectionModel
18996  * @extends Roo.util.Observable
18997  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18998  * implemented by descendant classes.  This class should not be directly instantiated.
18999  * @constructor
19000  */
19001 Roo.bootstrap.Table.AbstractSelectionModel = function(){
19002     this.locked = false;
19003     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
19004 };
19005
19006
19007 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
19008     /** @ignore Called by the grid automatically. Do not call directly. */
19009     init : function(grid){
19010         this.grid = grid;
19011         this.initEvents();
19012     },
19013
19014     /**
19015      * Locks the selections.
19016      */
19017     lock : function(){
19018         this.locked = true;
19019     },
19020
19021     /**
19022      * Unlocks the selections.
19023      */
19024     unlock : function(){
19025         this.locked = false;
19026     },
19027
19028     /**
19029      * Returns true if the selections are locked.
19030      * @return {Boolean}
19031      */
19032     isLocked : function(){
19033         return this.locked;
19034     }
19035 });
19036 /**
19037  * @extends Roo.bootstrap.Table.AbstractSelectionModel
19038  * @class Roo.bootstrap.Table.RowSelectionModel
19039  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
19040  * It supports multiple selections and keyboard selection/navigation. 
19041  * @constructor
19042  * @param {Object} config
19043  */
19044
19045 Roo.bootstrap.Table.RowSelectionModel = function(config){
19046     Roo.apply(this, config);
19047     this.selections = new Roo.util.MixedCollection(false, function(o){
19048         return o.id;
19049     });
19050
19051     this.last = false;
19052     this.lastActive = false;
19053
19054     this.addEvents({
19055         /**
19056              * @event selectionchange
19057              * Fires when the selection changes
19058              * @param {SelectionModel} this
19059              */
19060             "selectionchange" : true,
19061         /**
19062              * @event afterselectionchange
19063              * Fires after the selection changes (eg. by key press or clicking)
19064              * @param {SelectionModel} this
19065              */
19066             "afterselectionchange" : true,
19067         /**
19068              * @event beforerowselect
19069              * Fires when a row is selected being selected, return false to cancel.
19070              * @param {SelectionModel} this
19071              * @param {Number} rowIndex The selected index
19072              * @param {Boolean} keepExisting False if other selections will be cleared
19073              */
19074             "beforerowselect" : true,
19075         /**
19076              * @event rowselect
19077              * Fires when a row is selected.
19078              * @param {SelectionModel} this
19079              * @param {Number} rowIndex The selected index
19080              * @param {Roo.data.Record} r The record
19081              */
19082             "rowselect" : true,
19083         /**
19084              * @event rowdeselect
19085              * Fires when a row is deselected.
19086              * @param {SelectionModel} this
19087              * @param {Number} rowIndex The selected index
19088              */
19089         "rowdeselect" : true
19090     });
19091     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
19092     this.locked = false;
19093 };
19094
19095 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
19096     /**
19097      * @cfg {Boolean} singleSelect
19098      * True to allow selection of only one row at a time (defaults to false)
19099      */
19100     singleSelect : false,
19101
19102     // private
19103     initEvents : function(){
19104
19105         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
19106             this.grid.on("mousedown", this.handleMouseDown, this);
19107         }else{ // allow click to work like normal
19108             this.grid.on("rowclick", this.handleDragableRowClick, this);
19109         }
19110
19111         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
19112             "up" : function(e){
19113                 if(!e.shiftKey){
19114                     this.selectPrevious(e.shiftKey);
19115                 }else if(this.last !== false && this.lastActive !== false){
19116                     var last = this.last;
19117                     this.selectRange(this.last,  this.lastActive-1);
19118                     this.grid.getView().focusRow(this.lastActive);
19119                     if(last !== false){
19120                         this.last = last;
19121                     }
19122                 }else{
19123                     this.selectFirstRow();
19124                 }
19125                 this.fireEvent("afterselectionchange", this);
19126             },
19127             "down" : function(e){
19128                 if(!e.shiftKey){
19129                     this.selectNext(e.shiftKey);
19130                 }else if(this.last !== false && this.lastActive !== false){
19131                     var last = this.last;
19132                     this.selectRange(this.last,  this.lastActive+1);
19133                     this.grid.getView().focusRow(this.lastActive);
19134                     if(last !== false){
19135                         this.last = last;
19136                     }
19137                 }else{
19138                     this.selectFirstRow();
19139                 }
19140                 this.fireEvent("afterselectionchange", this);
19141             },
19142             scope: this
19143         });
19144
19145         var view = this.grid.view;
19146         view.on("refresh", this.onRefresh, this);
19147         view.on("rowupdated", this.onRowUpdated, this);
19148         view.on("rowremoved", this.onRemove, this);
19149     },
19150
19151     // private
19152     onRefresh : function(){
19153         var ds = this.grid.dataSource, i, v = this.grid.view;
19154         var s = this.selections;
19155         s.each(function(r){
19156             if((i = ds.indexOfId(r.id)) != -1){
19157                 v.onRowSelect(i);
19158             }else{
19159                 s.remove(r);
19160             }
19161         });
19162     },
19163
19164     // private
19165     onRemove : function(v, index, r){
19166         this.selections.remove(r);
19167     },
19168
19169     // private
19170     onRowUpdated : function(v, index, r){
19171         if(this.isSelected(r)){
19172             v.onRowSelect(index);
19173         }
19174     },
19175
19176     /**
19177      * Select records.
19178      * @param {Array} records The records to select
19179      * @param {Boolean} keepExisting (optional) True to keep existing selections
19180      */
19181     selectRecords : function(records, keepExisting){
19182         if(!keepExisting){
19183             this.clearSelections();
19184         }
19185         var ds = this.grid.dataSource;
19186         for(var i = 0, len = records.length; i < len; i++){
19187             this.selectRow(ds.indexOf(records[i]), true);
19188         }
19189     },
19190
19191     /**
19192      * Gets the number of selected rows.
19193      * @return {Number}
19194      */
19195     getCount : function(){
19196         return this.selections.length;
19197     },
19198
19199     /**
19200      * Selects the first row in the grid.
19201      */
19202     selectFirstRow : function(){
19203         this.selectRow(0);
19204     },
19205
19206     /**
19207      * Select the last row.
19208      * @param {Boolean} keepExisting (optional) True to keep existing selections
19209      */
19210     selectLastRow : function(keepExisting){
19211         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
19212     },
19213
19214     /**
19215      * Selects the row immediately following the last selected row.
19216      * @param {Boolean} keepExisting (optional) True to keep existing selections
19217      */
19218     selectNext : function(keepExisting){
19219         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
19220             this.selectRow(this.last+1, keepExisting);
19221             this.grid.getView().focusRow(this.last);
19222         }
19223     },
19224
19225     /**
19226      * Selects the row that precedes the last selected row.
19227      * @param {Boolean} keepExisting (optional) True to keep existing selections
19228      */
19229     selectPrevious : function(keepExisting){
19230         if(this.last){
19231             this.selectRow(this.last-1, keepExisting);
19232             this.grid.getView().focusRow(this.last);
19233         }
19234     },
19235
19236     /**
19237      * Returns the selected records
19238      * @return {Array} Array of selected records
19239      */
19240     getSelections : function(){
19241         return [].concat(this.selections.items);
19242     },
19243
19244     /**
19245      * Returns the first selected record.
19246      * @return {Record}
19247      */
19248     getSelected : function(){
19249         return this.selections.itemAt(0);
19250     },
19251
19252
19253     /**
19254      * Clears all selections.
19255      */
19256     clearSelections : function(fast){
19257         if(this.locked) return;
19258         if(fast !== true){
19259             var ds = this.grid.dataSource;
19260             var s = this.selections;
19261             s.each(function(r){
19262                 this.deselectRow(ds.indexOfId(r.id));
19263             }, this);
19264             s.clear();
19265         }else{
19266             this.selections.clear();
19267         }
19268         this.last = false;
19269     },
19270
19271
19272     /**
19273      * Selects all rows.
19274      */
19275     selectAll : function(){
19276         if(this.locked) return;
19277         this.selections.clear();
19278         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
19279             this.selectRow(i, true);
19280         }
19281     },
19282
19283     /**
19284      * Returns True if there is a selection.
19285      * @return {Boolean}
19286      */
19287     hasSelection : function(){
19288         return this.selections.length > 0;
19289     },
19290
19291     /**
19292      * Returns True if the specified row is selected.
19293      * @param {Number/Record} record The record or index of the record to check
19294      * @return {Boolean}
19295      */
19296     isSelected : function(index){
19297         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
19298         return (r && this.selections.key(r.id) ? true : false);
19299     },
19300
19301     /**
19302      * Returns True if the specified record id is selected.
19303      * @param {String} id The id of record to check
19304      * @return {Boolean}
19305      */
19306     isIdSelected : function(id){
19307         return (this.selections.key(id) ? true : false);
19308     },
19309
19310     // private
19311     handleMouseDown : function(e, t){
19312         var view = this.grid.getView(), rowIndex;
19313         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
19314             return;
19315         };
19316         if(e.shiftKey && this.last !== false){
19317             var last = this.last;
19318             this.selectRange(last, rowIndex, e.ctrlKey);
19319             this.last = last; // reset the last
19320             view.focusRow(rowIndex);
19321         }else{
19322             var isSelected = this.isSelected(rowIndex);
19323             if(e.button !== 0 && isSelected){
19324                 view.focusRow(rowIndex);
19325             }else if(e.ctrlKey && isSelected){
19326                 this.deselectRow(rowIndex);
19327             }else if(!isSelected){
19328                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
19329                 view.focusRow(rowIndex);
19330             }
19331         }
19332         this.fireEvent("afterselectionchange", this);
19333     },
19334     // private
19335     handleDragableRowClick :  function(grid, rowIndex, e) 
19336     {
19337         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
19338             this.selectRow(rowIndex, false);
19339             grid.view.focusRow(rowIndex);
19340              this.fireEvent("afterselectionchange", this);
19341         }
19342     },
19343     
19344     /**
19345      * Selects multiple rows.
19346      * @param {Array} rows Array of the indexes of the row to select
19347      * @param {Boolean} keepExisting (optional) True to keep existing selections
19348      */
19349     selectRows : function(rows, keepExisting){
19350         if(!keepExisting){
19351             this.clearSelections();
19352         }
19353         for(var i = 0, len = rows.length; i < len; i++){
19354             this.selectRow(rows[i], true);
19355         }
19356     },
19357
19358     /**
19359      * Selects a range of rows. All rows in between startRow and endRow are also selected.
19360      * @param {Number} startRow The index of the first row in the range
19361      * @param {Number} endRow The index of the last row in the range
19362      * @param {Boolean} keepExisting (optional) True to retain existing selections
19363      */
19364     selectRange : function(startRow, endRow, keepExisting){
19365         if(this.locked) return;
19366         if(!keepExisting){
19367             this.clearSelections();
19368         }
19369         if(startRow <= endRow){
19370             for(var i = startRow; i <= endRow; i++){
19371                 this.selectRow(i, true);
19372             }
19373         }else{
19374             for(var i = startRow; i >= endRow; i--){
19375                 this.selectRow(i, true);
19376             }
19377         }
19378     },
19379
19380     /**
19381      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19382      * @param {Number} startRow The index of the first row in the range
19383      * @param {Number} endRow The index of the last row in the range
19384      */
19385     deselectRange : function(startRow, endRow, preventViewNotify){
19386         if(this.locked) return;
19387         for(var i = startRow; i <= endRow; i++){
19388             this.deselectRow(i, preventViewNotify);
19389         }
19390     },
19391
19392     /**
19393      * Selects a row.
19394      * @param {Number} row The index of the row to select
19395      * @param {Boolean} keepExisting (optional) True to keep existing selections
19396      */
19397     selectRow : function(index, keepExisting, preventViewNotify){
19398         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19399         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19400             if(!keepExisting || this.singleSelect){
19401                 this.clearSelections();
19402             }
19403             var r = this.grid.dataSource.getAt(index);
19404             this.selections.add(r);
19405             this.last = this.lastActive = index;
19406             if(!preventViewNotify){
19407                 this.grid.getView().onRowSelect(index);
19408             }
19409             this.fireEvent("rowselect", this, index, r);
19410             this.fireEvent("selectionchange", this);
19411         }
19412     },
19413
19414     /**
19415      * Deselects a row.
19416      * @param {Number} row The index of the row to deselect
19417      */
19418     deselectRow : function(index, preventViewNotify){
19419         if(this.locked) return;
19420         if(this.last == index){
19421             this.last = false;
19422         }
19423         if(this.lastActive == index){
19424             this.lastActive = false;
19425         }
19426         var r = this.grid.dataSource.getAt(index);
19427         this.selections.remove(r);
19428         if(!preventViewNotify){
19429             this.grid.getView().onRowDeselect(index);
19430         }
19431         this.fireEvent("rowdeselect", this, index);
19432         this.fireEvent("selectionchange", this);
19433     },
19434
19435     // private
19436     restoreLast : function(){
19437         if(this._last){
19438             this.last = this._last;
19439         }
19440     },
19441
19442     // private
19443     acceptsNav : function(row, col, cm){
19444         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19445     },
19446
19447     // private
19448     onEditorKey : function(field, e){
19449         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19450         if(k == e.TAB){
19451             e.stopEvent();
19452             ed.completeEdit();
19453             if(e.shiftKey){
19454                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19455             }else{
19456                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19457             }
19458         }else if(k == e.ENTER && !e.ctrlKey){
19459             e.stopEvent();
19460             ed.completeEdit();
19461             if(e.shiftKey){
19462                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19463             }else{
19464                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19465             }
19466         }else if(k == e.ESC){
19467             ed.cancelEdit();
19468         }
19469         if(newCell){
19470             g.startEditing(newCell[0], newCell[1]);
19471         }
19472     }
19473 });/*
19474  * Based on:
19475  * Ext JS Library 1.1.1
19476  * Copyright(c) 2006-2007, Ext JS, LLC.
19477  *
19478  * Originally Released Under LGPL - original licence link has changed is not relivant.
19479  *
19480  * Fork - LGPL
19481  * <script type="text/javascript">
19482  */
19483  
19484 /**
19485  * @class Roo.bootstrap.PagingToolbar
19486  * @extends Roo.Row
19487  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19488  * @constructor
19489  * Create a new PagingToolbar
19490  * @param {Object} config The config object
19491  */
19492 Roo.bootstrap.PagingToolbar = function(config)
19493 {
19494     // old args format still supported... - xtype is prefered..
19495         // created from xtype...
19496     var ds = config.dataSource;
19497     this.toolbarItems = [];
19498     if (config.items) {
19499         this.toolbarItems = config.items;
19500 //        config.items = [];
19501     }
19502     
19503     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19504     this.ds = ds;
19505     this.cursor = 0;
19506     if (ds) { 
19507         this.bind(ds);
19508     }
19509     
19510     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19511     
19512 };
19513
19514 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19515     /**
19516      * @cfg {Roo.data.Store} dataSource
19517      * The underlying data store providing the paged data
19518      */
19519     /**
19520      * @cfg {String/HTMLElement/Element} container
19521      * container The id or element that will contain the toolbar
19522      */
19523     /**
19524      * @cfg {Boolean} displayInfo
19525      * True to display the displayMsg (defaults to false)
19526      */
19527     /**
19528      * @cfg {Number} pageSize
19529      * The number of records to display per page (defaults to 20)
19530      */
19531     pageSize: 20,
19532     /**
19533      * @cfg {String} displayMsg
19534      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19535      */
19536     displayMsg : 'Displaying {0} - {1} of {2}',
19537     /**
19538      * @cfg {String} emptyMsg
19539      * The message to display when no records are found (defaults to "No data to display")
19540      */
19541     emptyMsg : 'No data to display',
19542     /**
19543      * Customizable piece of the default paging text (defaults to "Page")
19544      * @type String
19545      */
19546     beforePageText : "Page",
19547     /**
19548      * Customizable piece of the default paging text (defaults to "of %0")
19549      * @type String
19550      */
19551     afterPageText : "of {0}",
19552     /**
19553      * Customizable piece of the default paging text (defaults to "First Page")
19554      * @type String
19555      */
19556     firstText : "First Page",
19557     /**
19558      * Customizable piece of the default paging text (defaults to "Previous Page")
19559      * @type String
19560      */
19561     prevText : "Previous Page",
19562     /**
19563      * Customizable piece of the default paging text (defaults to "Next Page")
19564      * @type String
19565      */
19566     nextText : "Next Page",
19567     /**
19568      * Customizable piece of the default paging text (defaults to "Last Page")
19569      * @type String
19570      */
19571     lastText : "Last Page",
19572     /**
19573      * Customizable piece of the default paging text (defaults to "Refresh")
19574      * @type String
19575      */
19576     refreshText : "Refresh",
19577
19578     buttons : false,
19579     // private
19580     onRender : function(ct, position) 
19581     {
19582         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19583         this.navgroup.parentId = this.id;
19584         this.navgroup.onRender(this.el, null);
19585         // add the buttons to the navgroup
19586         
19587         if(this.displayInfo){
19588             Roo.log(this.el.select('ul.navbar-nav',true).first());
19589             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19590             this.displayEl = this.el.select('.x-paging-info', true).first();
19591 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19592 //            this.displayEl = navel.el.select('span',true).first();
19593         }
19594         
19595         var _this = this;
19596         
19597         if(this.buttons){
19598             Roo.each(_this.buttons, function(e){
19599                Roo.factory(e).onRender(_this.el, null);
19600             });
19601         }
19602             
19603         Roo.each(_this.toolbarItems, function(e) {
19604             _this.navgroup.addItem(e);
19605         });
19606         
19607         
19608         this.first = this.navgroup.addItem({
19609             tooltip: this.firstText,
19610             cls: "prev",
19611             icon : 'fa fa-backward',
19612             disabled: true,
19613             preventDefault: true,
19614             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19615         });
19616         
19617         this.prev =  this.navgroup.addItem({
19618             tooltip: this.prevText,
19619             cls: "prev",
19620             icon : 'fa fa-step-backward',
19621             disabled: true,
19622             preventDefault: true,
19623             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19624         });
19625     //this.addSeparator();
19626         
19627         
19628         var field = this.navgroup.addItem( {
19629             tagtype : 'span',
19630             cls : 'x-paging-position',
19631             
19632             html : this.beforePageText  +
19633                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19634                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19635          } ); //?? escaped?
19636         
19637         this.field = field.el.select('input', true).first();
19638         this.field.on("keydown", this.onPagingKeydown, this);
19639         this.field.on("focus", function(){this.dom.select();});
19640     
19641     
19642         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19643         //this.field.setHeight(18);
19644         //this.addSeparator();
19645         this.next = this.navgroup.addItem({
19646             tooltip: this.nextText,
19647             cls: "next",
19648             html : ' <i class="fa fa-step-forward">',
19649             disabled: true,
19650             preventDefault: true,
19651             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19652         });
19653         this.last = this.navgroup.addItem({
19654             tooltip: this.lastText,
19655             icon : 'fa fa-forward',
19656             cls: "next",
19657             disabled: true,
19658             preventDefault: true,
19659             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19660         });
19661     //this.addSeparator();
19662         this.loading = this.navgroup.addItem({
19663             tooltip: this.refreshText,
19664             icon: 'fa fa-refresh',
19665             preventDefault: true,
19666             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19667         });
19668
19669     },
19670
19671     // private
19672     updateInfo : function(){
19673         if(this.displayEl){
19674             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
19675             var msg = count == 0 ?
19676                 this.emptyMsg :
19677                 String.format(
19678                     this.displayMsg,
19679                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19680                 );
19681             this.displayEl.update(msg);
19682         }
19683     },
19684
19685     // private
19686     onLoad : function(ds, r, o){
19687        this.cursor = o.params ? o.params.start : 0;
19688        var d = this.getPageData(),
19689             ap = d.activePage,
19690             ps = d.pages;
19691         
19692        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19693        this.field.dom.value = ap;
19694        this.first.setDisabled(ap == 1);
19695        this.prev.setDisabled(ap == 1);
19696        this.next.setDisabled(ap == ps);
19697        this.last.setDisabled(ap == ps);
19698        this.loading.enable();
19699        this.updateInfo();
19700     },
19701
19702     // private
19703     getPageData : function(){
19704         var total = this.ds.getTotalCount();
19705         return {
19706             total : total,
19707             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19708             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19709         };
19710     },
19711
19712     // private
19713     onLoadError : function(){
19714         this.loading.enable();
19715     },
19716
19717     // private
19718     onPagingKeydown : function(e){
19719         var k = e.getKey();
19720         var d = this.getPageData();
19721         if(k == e.RETURN){
19722             var v = this.field.dom.value, pageNum;
19723             if(!v || isNaN(pageNum = parseInt(v, 10))){
19724                 this.field.dom.value = d.activePage;
19725                 return;
19726             }
19727             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19728             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19729             e.stopEvent();
19730         }
19731         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))
19732         {
19733           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19734           this.field.dom.value = pageNum;
19735           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19736           e.stopEvent();
19737         }
19738         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19739         {
19740           var v = this.field.dom.value, pageNum; 
19741           var increment = (e.shiftKey) ? 10 : 1;
19742           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19743             increment *= -1;
19744           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19745             this.field.dom.value = d.activePage;
19746             return;
19747           }
19748           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19749           {
19750             this.field.dom.value = parseInt(v, 10) + increment;
19751             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19752             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19753           }
19754           e.stopEvent();
19755         }
19756     },
19757
19758     // private
19759     beforeLoad : function(){
19760         if(this.loading){
19761             this.loading.disable();
19762         }
19763     },
19764
19765     // private
19766     onClick : function(which){
19767         
19768         var ds = this.ds;
19769         if (!ds) {
19770             return;
19771         }
19772         
19773         switch(which){
19774             case "first":
19775                 ds.load({params:{start: 0, limit: this.pageSize}});
19776             break;
19777             case "prev":
19778                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19779             break;
19780             case "next":
19781                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19782             break;
19783             case "last":
19784                 var total = ds.getTotalCount();
19785                 var extra = total % this.pageSize;
19786                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19787                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19788             break;
19789             case "refresh":
19790                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19791             break;
19792         }
19793     },
19794
19795     /**
19796      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19797      * @param {Roo.data.Store} store The data store to unbind
19798      */
19799     unbind : function(ds){
19800         ds.un("beforeload", this.beforeLoad, this);
19801         ds.un("load", this.onLoad, this);
19802         ds.un("loadexception", this.onLoadError, this);
19803         ds.un("remove", this.updateInfo, this);
19804         ds.un("add", this.updateInfo, this);
19805         this.ds = undefined;
19806     },
19807
19808     /**
19809      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19810      * @param {Roo.data.Store} store The data store to bind
19811      */
19812     bind : function(ds){
19813         ds.on("beforeload", this.beforeLoad, this);
19814         ds.on("load", this.onLoad, this);
19815         ds.on("loadexception", this.onLoadError, this);
19816         ds.on("remove", this.updateInfo, this);
19817         ds.on("add", this.updateInfo, this);
19818         this.ds = ds;
19819     }
19820 });/*
19821  * - LGPL
19822  *
19823  * element
19824  * 
19825  */
19826
19827 /**
19828  * @class Roo.bootstrap.MessageBar
19829  * @extends Roo.bootstrap.Component
19830  * Bootstrap MessageBar class
19831  * @cfg {String} html contents of the MessageBar
19832  * @cfg {String} weight (info | success | warning | danger) default info
19833  * @cfg {String} beforeClass insert the bar before the given class
19834  * @cfg {Boolean} closable (true | false) default false
19835  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19836  * 
19837  * @constructor
19838  * Create a new Element
19839  * @param {Object} config The config object
19840  */
19841
19842 Roo.bootstrap.MessageBar = function(config){
19843     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19844 };
19845
19846 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19847     
19848     html: '',
19849     weight: 'info',
19850     closable: false,
19851     fixed: false,
19852     beforeClass: 'bootstrap-sticky-wrap',
19853     
19854     getAutoCreate : function(){
19855         
19856         var cfg = {
19857             tag: 'div',
19858             cls: 'alert alert-dismissable alert-' + this.weight,
19859             cn: [
19860                 {
19861                     tag: 'span',
19862                     cls: 'message',
19863                     html: this.html || ''
19864                 }
19865             ]
19866         }
19867         
19868         if(this.fixed){
19869             cfg.cls += ' alert-messages-fixed';
19870         }
19871         
19872         if(this.closable){
19873             cfg.cn.push({
19874                 tag: 'button',
19875                 cls: 'close',
19876                 html: 'x'
19877             });
19878         }
19879         
19880         return cfg;
19881     },
19882     
19883     onRender : function(ct, position)
19884     {
19885         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19886         
19887         if(!this.el){
19888             var cfg = Roo.apply({},  this.getAutoCreate());
19889             cfg.id = Roo.id();
19890             
19891             if (this.cls) {
19892                 cfg.cls += ' ' + this.cls;
19893             }
19894             if (this.style) {
19895                 cfg.style = this.style;
19896             }
19897             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19898             
19899             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19900         }
19901         
19902         this.el.select('>button.close').on('click', this.hide, this);
19903         
19904     },
19905     
19906     show : function()
19907     {
19908         if (!this.rendered) {
19909             this.render();
19910         }
19911         
19912         this.el.show();
19913         
19914         this.fireEvent('show', this);
19915         
19916     },
19917     
19918     hide : function()
19919     {
19920         if (!this.rendered) {
19921             this.render();
19922         }
19923         
19924         this.el.hide();
19925         
19926         this.fireEvent('hide', this);
19927     },
19928     
19929     update : function()
19930     {
19931 //        var e = this.el.dom.firstChild;
19932 //        
19933 //        if(this.closable){
19934 //            e = e.nextSibling;
19935 //        }
19936 //        
19937 //        e.data = this.html || '';
19938
19939         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19940     }
19941    
19942 });
19943
19944  
19945
19946      /*
19947  * - LGPL
19948  *
19949  * Graph
19950  * 
19951  */
19952
19953
19954 /**
19955  * @class Roo.bootstrap.Graph
19956  * @extends Roo.bootstrap.Component
19957  * Bootstrap Graph class
19958 > Prameters
19959  -sm {number} sm 4
19960  -md {number} md 5
19961  @cfg {String} graphtype  bar | vbar | pie
19962  @cfg {number} g_x coodinator | centre x (pie)
19963  @cfg {number} g_y coodinator | centre y (pie)
19964  @cfg {number} g_r radius (pie)
19965  @cfg {number} g_height height of the chart (respected by all elements in the set)
19966  @cfg {number} g_width width of the chart (respected by all elements in the set)
19967  @cfg {Object} title The title of the chart
19968     
19969  -{Array}  values
19970  -opts (object) options for the chart 
19971      o {
19972      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19973      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19974      o vgutter (number)
19975      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.
19976      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19977      o to
19978      o stretch (boolean)
19979      o }
19980  -opts (object) options for the pie
19981      o{
19982      o cut
19983      o startAngle (number)
19984      o endAngle (number)
19985      } 
19986  *
19987  * @constructor
19988  * Create a new Input
19989  * @param {Object} config The config object
19990  */
19991
19992 Roo.bootstrap.Graph = function(config){
19993     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19994     
19995     this.addEvents({
19996         // img events
19997         /**
19998          * @event click
19999          * The img click event for the img.
20000          * @param {Roo.EventObject} e
20001          */
20002         "click" : true
20003     });
20004 };
20005
20006 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
20007     
20008     sm: 4,
20009     md: 5,
20010     graphtype: 'bar',
20011     g_height: 250,
20012     g_width: 400,
20013     g_x: 50,
20014     g_y: 50,
20015     g_r: 30,
20016     opts:{
20017         //g_colors: this.colors,
20018         g_type: 'soft',
20019         g_gutter: '20%'
20020
20021     },
20022     title : false,
20023
20024     getAutoCreate : function(){
20025         
20026         var cfg = {
20027             tag: 'div',
20028             html : null
20029         }
20030         
20031         
20032         return  cfg;
20033     },
20034
20035     onRender : function(ct,position){
20036         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
20037         this.raphael = Raphael(this.el.dom);
20038         
20039                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
20040                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
20041                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
20042                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
20043                 /*
20044                 r.text(160, 10, "Single Series Chart").attr(txtattr);
20045                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
20046                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
20047                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
20048                 
20049                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
20050                 r.barchart(330, 10, 300, 220, data1);
20051                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
20052                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
20053                 */
20054                 
20055                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
20056                 // r.barchart(30, 30, 560, 250,  xdata, {
20057                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
20058                 //     axis : "0 0 1 1",
20059                 //     axisxlabels :  xdata
20060                 //     //yvalues : cols,
20061                    
20062                 // });
20063 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
20064 //        
20065 //        this.load(null,xdata,{
20066 //                axis : "0 0 1 1",
20067 //                axisxlabels :  xdata
20068 //                });
20069
20070     },
20071
20072     load : function(graphtype,xdata,opts){
20073         this.raphael.clear();
20074         if(!graphtype) {
20075             graphtype = this.graphtype;
20076         }
20077         if(!opts){
20078             opts = this.opts;
20079         }
20080         var r = this.raphael,
20081             fin = function () {
20082                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
20083             },
20084             fout = function () {
20085                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
20086             },
20087             pfin = function() {
20088                 this.sector.stop();
20089                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
20090
20091                 if (this.label) {
20092                     this.label[0].stop();
20093                     this.label[0].attr({ r: 7.5 });
20094                     this.label[1].attr({ "font-weight": 800 });
20095                 }
20096             },
20097             pfout = function() {
20098                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
20099
20100                 if (this.label) {
20101                     this.label[0].animate({ r: 5 }, 500, "bounce");
20102                     this.label[1].attr({ "font-weight": 400 });
20103                 }
20104             };
20105
20106         switch(graphtype){
20107             case 'bar':
20108                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
20109                 break;
20110             case 'hbar':
20111                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
20112                 break;
20113             case 'pie':
20114 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
20115 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
20116 //            
20117                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
20118                 
20119                 break;
20120
20121         }
20122         
20123         if(this.title){
20124             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
20125         }
20126         
20127     },
20128     
20129     setTitle: function(o)
20130     {
20131         this.title = o;
20132     },
20133     
20134     initEvents: function() {
20135         
20136         if(!this.href){
20137             this.el.on('click', this.onClick, this);
20138         }
20139     },
20140     
20141     onClick : function(e)
20142     {
20143         Roo.log('img onclick');
20144         this.fireEvent('click', this, e);
20145     }
20146    
20147 });
20148
20149  
20150 /*
20151  * - LGPL
20152  *
20153  * numberBox
20154  * 
20155  */
20156 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20157
20158 /**
20159  * @class Roo.bootstrap.dash.NumberBox
20160  * @extends Roo.bootstrap.Component
20161  * Bootstrap NumberBox class
20162  * @cfg {String} headline Box headline
20163  * @cfg {String} content Box content
20164  * @cfg {String} icon Box icon
20165  * @cfg {String} footer Footer text
20166  * @cfg {String} fhref Footer href
20167  * 
20168  * @constructor
20169  * Create a new NumberBox
20170  * @param {Object} config The config object
20171  */
20172
20173
20174 Roo.bootstrap.dash.NumberBox = function(config){
20175     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
20176     
20177 };
20178
20179 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
20180     
20181     headline : '',
20182     content : '',
20183     icon : '',
20184     footer : '',
20185     fhref : '',
20186     ficon : '',
20187     
20188     getAutoCreate : function(){
20189         
20190         var cfg = {
20191             tag : 'div',
20192             cls : 'small-box ',
20193             cn : [
20194                 {
20195                     tag : 'div',
20196                     cls : 'inner',
20197                     cn :[
20198                         {
20199                             tag : 'h3',
20200                             cls : 'roo-headline',
20201                             html : this.headline
20202                         },
20203                         {
20204                             tag : 'p',
20205                             cls : 'roo-content',
20206                             html : this.content
20207                         }
20208                     ]
20209                 }
20210             ]
20211         }
20212         
20213         if(this.icon){
20214             cfg.cn.push({
20215                 tag : 'div',
20216                 cls : 'icon',
20217                 cn :[
20218                     {
20219                         tag : 'i',
20220                         cls : 'ion ' + this.icon
20221                     }
20222                 ]
20223             });
20224         }
20225         
20226         if(this.footer){
20227             var footer = {
20228                 tag : 'a',
20229                 cls : 'small-box-footer',
20230                 href : this.fhref || '#',
20231                 html : this.footer
20232             };
20233             
20234             cfg.cn.push(footer);
20235             
20236         }
20237         
20238         return  cfg;
20239     },
20240
20241     onRender : function(ct,position){
20242         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
20243
20244
20245        
20246                 
20247     },
20248
20249     setHeadline: function (value)
20250     {
20251         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
20252     },
20253     
20254     setFooter: function (value, href)
20255     {
20256         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
20257         
20258         if(href){
20259             this.el.select('a.small-box-footer',true).first().attr('href', href);
20260         }
20261         
20262     },
20263
20264     setContent: function (value)
20265     {
20266         this.el.select('.roo-content',true).first().dom.innerHTML = value;
20267     },
20268
20269     initEvents: function() 
20270     {   
20271         
20272     }
20273     
20274 });
20275
20276  
20277 /*
20278  * - LGPL
20279  *
20280  * TabBox
20281  * 
20282  */
20283 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20284
20285 /**
20286  * @class Roo.bootstrap.dash.TabBox
20287  * @extends Roo.bootstrap.Component
20288  * Bootstrap TabBox class
20289  * @cfg {String} title Title of the TabBox
20290  * @cfg {String} icon Icon of the TabBox
20291  * @cfg {Boolean} showtabs (true|false) show the tabs default true
20292  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
20293  * 
20294  * @constructor
20295  * Create a new TabBox
20296  * @param {Object} config The config object
20297  */
20298
20299
20300 Roo.bootstrap.dash.TabBox = function(config){
20301     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
20302     this.addEvents({
20303         // raw events
20304         /**
20305          * @event addpane
20306          * When a pane is added
20307          * @param {Roo.bootstrap.dash.TabPane} pane
20308          */
20309         "addpane" : true,
20310         /**
20311          * @event activatepane
20312          * When a pane is activated
20313          * @param {Roo.bootstrap.dash.TabPane} pane
20314          */
20315         "activatepane" : true
20316         
20317          
20318     });
20319     
20320     this.panes = [];
20321 };
20322
20323 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
20324
20325     title : '',
20326     icon : false,
20327     showtabs : true,
20328     tabScrollable : false,
20329     
20330     getChildContainer : function()
20331     {
20332         return this.el.select('.tab-content', true).first();
20333     },
20334     
20335     getAutoCreate : function(){
20336         
20337         var header = {
20338             tag: 'li',
20339             cls: 'pull-left header',
20340             html: this.title,
20341             cn : []
20342         };
20343         
20344         if(this.icon){
20345             header.cn.push({
20346                 tag: 'i',
20347                 cls: 'fa ' + this.icon
20348             });
20349         }
20350         
20351         var h = {
20352             tag: 'ul',
20353             cls: 'nav nav-tabs pull-right',
20354             cn: [
20355                 header
20356             ]
20357         };
20358         
20359         if(this.tabScrollable){
20360             h = {
20361                 tag: 'div',
20362                 cls: 'tab-header',
20363                 cn: [
20364                     {
20365                         tag: 'ul',
20366                         cls: 'nav nav-tabs pull-right',
20367                         cn: [
20368                             header
20369                         ]
20370                     }
20371                 ]
20372             }
20373         }
20374         
20375         var cfg = {
20376             tag: 'div',
20377             cls: 'nav-tabs-custom',
20378             cn: [
20379                 h,
20380                 {
20381                     tag: 'div',
20382                     cls: 'tab-content no-padding',
20383                     cn: []
20384                 }
20385             ]
20386         }
20387
20388         return  cfg;
20389     },
20390     initEvents : function()
20391     {
20392         //Roo.log('add add pane handler');
20393         this.on('addpane', this.onAddPane, this);
20394     },
20395      /**
20396      * Updates the box title
20397      * @param {String} html to set the title to.
20398      */
20399     setTitle : function(value)
20400     {
20401         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20402     },
20403     onAddPane : function(pane)
20404     {
20405         this.panes.push(pane);
20406         //Roo.log('addpane');
20407         //Roo.log(pane);
20408         // tabs are rendere left to right..
20409         if(!this.showtabs){
20410             return;
20411         }
20412         
20413         var ctr = this.el.select('.nav-tabs', true).first();
20414          
20415          
20416         var existing = ctr.select('.nav-tab',true);
20417         var qty = existing.getCount();;
20418         
20419         
20420         var tab = ctr.createChild({
20421             tag : 'li',
20422             cls : 'nav-tab' + (qty ? '' : ' active'),
20423             cn : [
20424                 {
20425                     tag : 'a',
20426                     href:'#',
20427                     html : pane.title
20428                 }
20429             ]
20430         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20431         pane.tab = tab;
20432         
20433         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20434         if (!qty) {
20435             pane.el.addClass('active');
20436         }
20437         
20438                 
20439     },
20440     onTabClick : function(ev,un,ob,pane)
20441     {
20442         //Roo.log('tab - prev default');
20443         ev.preventDefault();
20444         
20445         
20446         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20447         pane.tab.addClass('active');
20448         //Roo.log(pane.title);
20449         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20450         // technically we should have a deactivate event.. but maybe add later.
20451         // and it should not de-activate the selected tab...
20452         this.fireEvent('activatepane', pane);
20453         pane.el.addClass('active');
20454         pane.fireEvent('activate');
20455         
20456         
20457     },
20458     
20459     getActivePane : function()
20460     {
20461         var r = false;
20462         Roo.each(this.panes, function(p) {
20463             if(p.el.hasClass('active')){
20464                 r = p;
20465                 return false;
20466             }
20467             
20468             return;
20469         });
20470         
20471         return r;
20472     }
20473     
20474     
20475 });
20476
20477  
20478 /*
20479  * - LGPL
20480  *
20481  * Tab pane
20482  * 
20483  */
20484 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20485 /**
20486  * @class Roo.bootstrap.TabPane
20487  * @extends Roo.bootstrap.Component
20488  * Bootstrap TabPane class
20489  * @cfg {Boolean} active (false | true) Default false
20490  * @cfg {String} title title of panel
20491
20492  * 
20493  * @constructor
20494  * Create a new TabPane
20495  * @param {Object} config The config object
20496  */
20497
20498 Roo.bootstrap.dash.TabPane = function(config){
20499     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20500     
20501     this.addEvents({
20502         // raw events
20503         /**
20504          * @event activate
20505          * When a pane is activated
20506          * @param {Roo.bootstrap.dash.TabPane} pane
20507          */
20508         "activate" : true
20509          
20510     });
20511 };
20512
20513 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20514     
20515     active : false,
20516     title : '',
20517     
20518     // the tabBox that this is attached to.
20519     tab : false,
20520      
20521     getAutoCreate : function() 
20522     {
20523         var cfg = {
20524             tag: 'div',
20525             cls: 'tab-pane'
20526         }
20527         
20528         if(this.active){
20529             cfg.cls += ' active';
20530         }
20531         
20532         return cfg;
20533     },
20534     initEvents  : function()
20535     {
20536         //Roo.log('trigger add pane handler');
20537         this.parent().fireEvent('addpane', this)
20538     },
20539     
20540      /**
20541      * Updates the tab title 
20542      * @param {String} html to set the title to.
20543      */
20544     setTitle: function(str)
20545     {
20546         if (!this.tab) {
20547             return;
20548         }
20549         this.title = str;
20550         this.tab.select('a', true).first().dom.innerHTML = str;
20551         
20552     }
20553     
20554     
20555     
20556 });
20557
20558  
20559
20560
20561  /*
20562  * - LGPL
20563  *
20564  * menu
20565  * 
20566  */
20567 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20568
20569 /**
20570  * @class Roo.bootstrap.menu.Menu
20571  * @extends Roo.bootstrap.Component
20572  * Bootstrap Menu class - container for Menu
20573  * @cfg {String} html Text of the menu
20574  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20575  * @cfg {String} icon Font awesome icon
20576  * @cfg {String} pos Menu align to (top | bottom) default bottom
20577  * 
20578  * 
20579  * @constructor
20580  * Create a new Menu
20581  * @param {Object} config The config object
20582  */
20583
20584
20585 Roo.bootstrap.menu.Menu = function(config){
20586     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20587     
20588     this.addEvents({
20589         /**
20590          * @event beforeshow
20591          * Fires before this menu is displayed
20592          * @param {Roo.bootstrap.menu.Menu} this
20593          */
20594         beforeshow : true,
20595         /**
20596          * @event beforehide
20597          * Fires before this menu is hidden
20598          * @param {Roo.bootstrap.menu.Menu} this
20599          */
20600         beforehide : true,
20601         /**
20602          * @event show
20603          * Fires after this menu is displayed
20604          * @param {Roo.bootstrap.menu.Menu} this
20605          */
20606         show : true,
20607         /**
20608          * @event hide
20609          * Fires after this menu is hidden
20610          * @param {Roo.bootstrap.menu.Menu} this
20611          */
20612         hide : true,
20613         /**
20614          * @event click
20615          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20616          * @param {Roo.bootstrap.menu.Menu} this
20617          * @param {Roo.EventObject} e
20618          */
20619         click : true
20620     });
20621     
20622 };
20623
20624 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20625     
20626     submenu : false,
20627     html : '',
20628     weight : 'default',
20629     icon : false,
20630     pos : 'bottom',
20631     
20632     
20633     getChildContainer : function() {
20634         if(this.isSubMenu){
20635             return this.el;
20636         }
20637         
20638         return this.el.select('ul.dropdown-menu', true).first();  
20639     },
20640     
20641     getAutoCreate : function()
20642     {
20643         var text = [
20644             {
20645                 tag : 'span',
20646                 cls : 'roo-menu-text',
20647                 html : this.html
20648             }
20649         ];
20650         
20651         if(this.icon){
20652             text.unshift({
20653                 tag : 'i',
20654                 cls : 'fa ' + this.icon
20655             })
20656         }
20657         
20658         
20659         var cfg = {
20660             tag : 'div',
20661             cls : 'btn-group',
20662             cn : [
20663                 {
20664                     tag : 'button',
20665                     cls : 'dropdown-button btn btn-' + this.weight,
20666                     cn : text
20667                 },
20668                 {
20669                     tag : 'button',
20670                     cls : 'dropdown-toggle btn btn-' + this.weight,
20671                     cn : [
20672                         {
20673                             tag : 'span',
20674                             cls : 'caret'
20675                         }
20676                     ]
20677                 },
20678                 {
20679                     tag : 'ul',
20680                     cls : 'dropdown-menu'
20681                 }
20682             ]
20683             
20684         };
20685         
20686         if(this.pos == 'top'){
20687             cfg.cls += ' dropup';
20688         }
20689         
20690         if(this.isSubMenu){
20691             cfg = {
20692                 tag : 'ul',
20693                 cls : 'dropdown-menu'
20694             }
20695         }
20696         
20697         return cfg;
20698     },
20699     
20700     onRender : function(ct, position)
20701     {
20702         this.isSubMenu = ct.hasClass('dropdown-submenu');
20703         
20704         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20705     },
20706     
20707     initEvents : function() 
20708     {
20709         if(this.isSubMenu){
20710             return;
20711         }
20712         
20713         this.hidden = true;
20714         
20715         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20716         this.triggerEl.on('click', this.onTriggerPress, this);
20717         
20718         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20719         this.buttonEl.on('click', this.onClick, this);
20720         
20721     },
20722     
20723     list : function()
20724     {
20725         if(this.isSubMenu){
20726             return this.el;
20727         }
20728         
20729         return this.el.select('ul.dropdown-menu', true).first();
20730     },
20731     
20732     onClick : function(e)
20733     {
20734         this.fireEvent("click", this, e);
20735     },
20736     
20737     onTriggerPress  : function(e)
20738     {   
20739         if (this.isVisible()) {
20740             this.hide();
20741         } else {
20742             this.show();
20743         }
20744     },
20745     
20746     isVisible : function(){
20747         return !this.hidden;
20748     },
20749     
20750     show : function()
20751     {
20752         this.fireEvent("beforeshow", this);
20753         
20754         this.hidden = false;
20755         this.el.addClass('open');
20756         
20757         Roo.get(document).on("mouseup", this.onMouseUp, this);
20758         
20759         this.fireEvent("show", this);
20760         
20761         
20762     },
20763     
20764     hide : function()
20765     {
20766         this.fireEvent("beforehide", this);
20767         
20768         this.hidden = true;
20769         this.el.removeClass('open');
20770         
20771         Roo.get(document).un("mouseup", this.onMouseUp);
20772         
20773         this.fireEvent("hide", this);
20774     },
20775     
20776     onMouseUp : function()
20777     {
20778         this.hide();
20779     }
20780     
20781 });
20782
20783  
20784  /*
20785  * - LGPL
20786  *
20787  * menu item
20788  * 
20789  */
20790 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20791
20792 /**
20793  * @class Roo.bootstrap.menu.Item
20794  * @extends Roo.bootstrap.Component
20795  * Bootstrap MenuItem class
20796  * @cfg {Boolean} submenu (true | false) default false
20797  * @cfg {String} html text of the item
20798  * @cfg {String} href the link
20799  * @cfg {Boolean} disable (true | false) default false
20800  * @cfg {Boolean} preventDefault (true | false) default true
20801  * @cfg {String} icon Font awesome icon
20802  * @cfg {String} pos Submenu align to (left | right) default right 
20803  * 
20804  * 
20805  * @constructor
20806  * Create a new Item
20807  * @param {Object} config The config object
20808  */
20809
20810
20811 Roo.bootstrap.menu.Item = function(config){
20812     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20813     this.addEvents({
20814         /**
20815          * @event mouseover
20816          * Fires when the mouse is hovering over this menu
20817          * @param {Roo.bootstrap.menu.Item} this
20818          * @param {Roo.EventObject} e
20819          */
20820         mouseover : true,
20821         /**
20822          * @event mouseout
20823          * Fires when the mouse exits this menu
20824          * @param {Roo.bootstrap.menu.Item} this
20825          * @param {Roo.EventObject} e
20826          */
20827         mouseout : true,
20828         // raw events
20829         /**
20830          * @event click
20831          * The raw click event for the entire grid.
20832          * @param {Roo.EventObject} e
20833          */
20834         click : true
20835     });
20836 };
20837
20838 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20839     
20840     submenu : false,
20841     href : '',
20842     html : '',
20843     preventDefault: true,
20844     disable : false,
20845     icon : false,
20846     pos : 'right',
20847     
20848     getAutoCreate : function()
20849     {
20850         var text = [
20851             {
20852                 tag : 'span',
20853                 cls : 'roo-menu-item-text',
20854                 html : this.html
20855             }
20856         ];
20857         
20858         if(this.icon){
20859             text.unshift({
20860                 tag : 'i',
20861                 cls : 'fa ' + this.icon
20862             })
20863         }
20864         
20865         var cfg = {
20866             tag : 'li',
20867             cn : [
20868                 {
20869                     tag : 'a',
20870                     href : this.href || '#',
20871                     cn : text
20872                 }
20873             ]
20874         };
20875         
20876         if(this.disable){
20877             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20878         }
20879         
20880         if(this.submenu){
20881             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20882             
20883             if(this.pos == 'left'){
20884                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20885             }
20886         }
20887         
20888         return cfg;
20889     },
20890     
20891     initEvents : function() 
20892     {
20893         this.el.on('mouseover', this.onMouseOver, this);
20894         this.el.on('mouseout', this.onMouseOut, this);
20895         
20896         this.el.select('a', true).first().on('click', this.onClick, this);
20897         
20898     },
20899     
20900     onClick : function(e)
20901     {
20902         if(this.preventDefault){
20903             e.preventDefault();
20904         }
20905         
20906         this.fireEvent("click", this, e);
20907     },
20908     
20909     onMouseOver : function(e)
20910     {
20911         if(this.submenu && this.pos == 'left'){
20912             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20913         }
20914         
20915         this.fireEvent("mouseover", this, e);
20916     },
20917     
20918     onMouseOut : function(e)
20919     {
20920         this.fireEvent("mouseout", this, e);
20921     }
20922 });
20923
20924  
20925
20926  /*
20927  * - LGPL
20928  *
20929  * menu separator
20930  * 
20931  */
20932 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20933
20934 /**
20935  * @class Roo.bootstrap.menu.Separator
20936  * @extends Roo.bootstrap.Component
20937  * Bootstrap Separator class
20938  * 
20939  * @constructor
20940  * Create a new Separator
20941  * @param {Object} config The config object
20942  */
20943
20944
20945 Roo.bootstrap.menu.Separator = function(config){
20946     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20947 };
20948
20949 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20950     
20951     getAutoCreate : function(){
20952         var cfg = {
20953             tag : 'li',
20954             cls: 'divider'
20955         };
20956         
20957         return cfg;
20958     }
20959    
20960 });
20961
20962  
20963
20964  /*
20965  * - LGPL
20966  *
20967  * Tooltip
20968  * 
20969  */
20970
20971 /**
20972  * @class Roo.bootstrap.Tooltip
20973  * Bootstrap Tooltip class
20974  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
20975  * to determine which dom element triggers the tooltip.
20976  * 
20977  * It needs to add support for additional attributes like tooltip-position
20978  * 
20979  * @constructor
20980  * Create a new Toolti
20981  * @param {Object} config The config object
20982  */
20983
20984 Roo.bootstrap.Tooltip = function(config){
20985     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
20986 };
20987
20988 Roo.apply(Roo.bootstrap.Tooltip, {
20989     /**
20990      * @function init initialize tooltip monitoring.
20991      * @static
20992      */
20993     currentEl : false,
20994     currentTip : false,
20995     currentRegion : false,
20996     
20997     //  init : delay?
20998     
20999     init : function()
21000     {
21001         Roo.get(document).on('mouseover', this.enter ,this);
21002         Roo.get(document).on('mouseout', this.leave, this);
21003          
21004         
21005         this.currentTip = new Roo.bootstrap.Tooltip();
21006     },
21007     
21008     enter : function(ev)
21009     {
21010         var dom = ev.getTarget();
21011         //Roo.log(['enter',dom]);
21012         var el = Roo.fly(dom);
21013         if (this.currentEl) {
21014             //Roo.log(dom);
21015             //Roo.log(this.currentEl);
21016             //Roo.log(this.currentEl.contains(dom));
21017             if (this.currentEl == el) {
21018                 return;
21019             }
21020             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
21021                 return;
21022             }
21023
21024         }
21025         
21026         
21027         
21028         if (this.currentTip.el) {
21029             this.currentTip.el.hide(); // force hiding...
21030         }    
21031         //Roo.log(el);
21032         if (!el.attr('tooltip')) { // parents who have tip?
21033             return;
21034         }
21035         this.currentEl = el;
21036         this.currentTip.bind(el);
21037         this.currentRegion = Roo.lib.Region.getRegion(dom);
21038         this.currentTip.enter();
21039         
21040     },
21041     leave : function(ev)
21042     {
21043         var dom = ev.getTarget();
21044         //Roo.log(['leave',dom]);
21045         if (!this.currentEl) {
21046             return;
21047         }
21048         
21049         
21050         if (dom != this.currentEl.dom) {
21051             return;
21052         }
21053         var xy = ev.getXY();
21054         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
21055             return;
21056         }
21057         // only activate leave if mouse cursor is outside... bounding box..
21058         
21059         
21060         
21061         
21062         if (this.currentTip) {
21063             this.currentTip.leave();
21064         }
21065         //Roo.log('clear currentEl');
21066         this.currentEl = false;
21067         
21068         
21069     },
21070     alignment : {
21071         'left' : ['r-l', [-2,0], 'right'],
21072         'right' : ['l-r', [2,0], 'left'],
21073         'bottom' : ['t-b', [0,2], 'top'],
21074         'top' : [ 'b-t', [0,-2], 'bottom']
21075     }
21076     
21077 });
21078
21079
21080 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
21081     
21082     
21083     bindEl : false,
21084     
21085     delay : null, // can be { show : 300 , hide: 500}
21086     
21087     timeout : null,
21088     
21089     hoverState : null, //???
21090     
21091     placement : 'bottom', 
21092     
21093     getAutoCreate : function(){
21094     
21095         var cfg = {
21096            cls : 'tooltip',
21097            role : 'tooltip',
21098            cn : [
21099                 {
21100                     cls : 'tooltip-arrow'
21101                 },
21102                 {
21103                     cls : 'tooltip-inner'
21104                 }
21105            ]
21106         };
21107         
21108         return cfg;
21109     },
21110     bind : function(el)
21111     {
21112         this.bindEl = el;
21113     },
21114       
21115     
21116     enter : function () {
21117        
21118         if (this.timeout != null) {
21119             clearTimeout(this.timeout);
21120         }
21121         
21122         this.hoverState = 'in'
21123          //Roo.log("enter - show");
21124         if (!this.delay || !this.delay.show) {
21125             this.show();
21126             return;
21127         }
21128         var _t = this;
21129         this.timeout = setTimeout(function () {
21130             if (_t.hoverState == 'in') {
21131                 _t.show();
21132             }
21133         }, this.delay.show);
21134     },
21135     leave : function()
21136     {
21137         clearTimeout(this.timeout);
21138     
21139         this.hoverState = 'out'
21140          if (!this.delay || !this.delay.hide) {
21141             this.hide();
21142             return 
21143         }
21144        
21145         var _t = this;
21146         this.timeout = setTimeout(function () {
21147             //Roo.log("leave - timeout");
21148             
21149             if (_t.hoverState == 'out') {
21150                 _t.hide();
21151                 Roo.bootstrap.Tooltip.currentEl = false;
21152             }
21153         }, delay)
21154     },
21155     
21156     show : function ()
21157     {
21158         if (!this.el) {
21159             this.render(document.body);
21160         }
21161         // set content.
21162         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
21163         this.el.select('.tooltip-inner',true).first().dom.innerHTML = this.bindEl.attr('tooltip');
21164         
21165         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
21166         
21167         var placement = typeof this.placement == 'function' ?
21168             this.placement.call(this, this.el, on_el) :
21169             this.placement;
21170             
21171         var autoToken = /\s?auto?\s?/i;
21172         var autoPlace = autoToken.test(placement);
21173         if (autoPlace) {
21174             placement = placement.replace(autoToken, '') || 'top';
21175         }
21176         
21177         //this.el.detach()
21178         //this.el.setXY([0,0]);
21179         this.el.show();
21180         //this.el.dom.style.display='block';
21181         this.el.addClass(placement);
21182         
21183         //this.el.appendTo(on_el);
21184         
21185         var p = this.getPosition();
21186         var box = this.el.getBox();
21187         
21188         if (autoPlace) {
21189             // fixme..
21190         }
21191         var align = Roo.bootstrap.Tooltip.alignment[placement]
21192         this.el.alignTo(this.bindEl, align[0],align[1]);
21193         //var arrow = this.el.select('.arrow',true).first();
21194         //arrow.set(align[2], 
21195         
21196         this.el.addClass('in fade');
21197         this.hoverState = null;
21198         
21199         if (this.el.hasClass('fade')) {
21200             // fade it?
21201         }
21202         
21203     },
21204     hide : function()
21205     {
21206          
21207         if (!this.el) {
21208             return;
21209         }
21210         //this.el.setXY([0,0]);
21211         this.el.removeClass('in');
21212         //this.el.hide();
21213         
21214     }
21215     
21216 });
21217  
21218
21219  /*
21220  * - LGPL
21221  *
21222  * Location Picker
21223  * 
21224  */
21225
21226 /**
21227  * @class Roo.bootstrap.LocationPicker
21228  * @extends Roo.bootstrap.Component
21229  * Bootstrap LocationPicker class
21230  * @cfg {Number} latitude Position when init default 0
21231  * @cfg {Number} longitude Position when init default 0
21232  * @cfg {Number} zoom default 15
21233  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
21234  * @cfg {Boolean} mapTypeControl default false
21235  * @cfg {Boolean} disableDoubleClickZoom default false
21236  * @cfg {Boolean} scrollwheel default true
21237  * @cfg {Boolean} streetViewControl default false
21238  * @cfg {Number} radius default 0
21239  * @cfg {String} locationName
21240  * @cfg {Boolean} draggable default true
21241  * @cfg {Boolean} enableAutocomplete default false
21242  * @cfg {Boolean} enableReverseGeocode default true
21243  * @cfg {String} markerTitle
21244  * 
21245  * @constructor
21246  * Create a new LocationPicker
21247  * @param {Object} config The config object
21248  */
21249
21250
21251 Roo.bootstrap.LocationPicker = function(config){
21252     
21253     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
21254     
21255      this.addEvents({
21256             /**
21257              * @event initial
21258              * Fires when the picker initialized.
21259              * @param {Roo.bootstrap.LocationPicker} this
21260              * @param {Google Location} location
21261              */
21262             initial : true,
21263             /**
21264              * @event positionchanged
21265              * Fires when the picker position changed.
21266              * @param {Roo.bootstrap.LocationPicker} this
21267              * @param {Google Location} location
21268              */
21269             positionchanged : true,
21270             /**
21271              * @event resize
21272              * Fires when the map resize.
21273              * @param {Roo.bootstrap.LocationPicker} this
21274              */
21275             resize : true,
21276             /**
21277              * @event show
21278              * Fires when the map show.
21279              * @param {Roo.bootstrap.LocationPicker} this
21280              */
21281             show : true,
21282             /**
21283              * @event hide
21284              * Fires when the map hide.
21285              * @param {Roo.bootstrap.LocationPicker} this
21286              */
21287             hide : true
21288         });
21289         
21290 };
21291
21292 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
21293     
21294     gMapContext: false,
21295     
21296     latitude: 0,
21297     longitude: 0,
21298     zoom: 15,
21299     mapTypeId: false,
21300     mapTypeControl: false,
21301     disableDoubleClickZoom: false,
21302     scrollwheel: true,
21303     streetViewControl: false,
21304     radius: 0,
21305     locationName: '',
21306     draggable: true,
21307     enableAutocomplete: false,
21308     enableReverseGeocode: true,
21309     markerTitle: '',
21310     
21311     getAutoCreate: function()
21312     {
21313
21314         var cfg = {
21315             tag: 'div',
21316             cls: 'roo-location-picker'
21317         };
21318         
21319         return cfg
21320     },
21321     
21322     initEvents: function(ct, position)
21323     {   
21324         if(!this.mapTypeId){
21325             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
21326         }
21327             
21328         if(!this.el.getWidth() || this.isApplied()){
21329             return;
21330         }
21331         
21332         this.el.setVisibilityMode(Roo.Element.DISPLAY);
21333         
21334         this.initial();
21335     },
21336     
21337     initial: function()
21338     {
21339         this.gMapContext = this.GMapContext();
21340         
21341         var _this = this;
21342         
21343         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
21344             _this.setPosition(_this.gMapContext.marker.position);
21345         });
21346         
21347         this.setPosition(this.gMapContext.location);
21348         
21349         this.fireEvent('initial', this, this.gMapContext.location);
21350     },
21351     
21352     isApplied: function() 
21353     {
21354         return this.getGmapContext() == false ? false : true;
21355     },
21356     
21357     getGmapContext: function() 
21358     {
21359         return this.gMapContext
21360     },
21361     
21362     GMapContext: function() 
21363     {
21364         var _map = new google.maps.Map(this.el.dom, this);
21365         var _marker = new google.maps.Marker({
21366             position: new google.maps.LatLng(this.latitude, this.longitude),
21367             map: _map,
21368             title: this.markerTitle,
21369             draggable: this.draggable
21370         });
21371         
21372         return {
21373             map: _map,
21374             marker: _marker,
21375             circle: null,
21376             location: _marker.position,
21377             radius: this.radius,
21378             locationName: this.locationName,
21379             addressComponents: {
21380                 formatted_address: null,
21381                 addressLine1: null,
21382                 addressLine2: null,
21383                 streetName: null,
21384                 streetNumber: null,
21385                 city: null,
21386                 district: null,
21387                 state: null,
21388                 stateOrProvince: null
21389             },
21390             settings: this,
21391             domContainer: this.el.dom,
21392             geodecoder: new google.maps.Geocoder()
21393         };
21394     },
21395     
21396     drawCircle: function(center, radius, options) 
21397     {
21398         if (this.gMapContext.circle != null) {
21399             this.gMapContext.circle.setMap(null);
21400         }
21401         if (radius > 0) {
21402             radius *= 1;
21403             options = Roo.apply({}, options, {
21404                 strokeColor: "#0000FF",
21405                 strokeOpacity: .35,
21406                 strokeWeight: 2,
21407                 fillColor: "#0000FF",
21408                 fillOpacity: .2
21409             });
21410             
21411             options.map = this.gMapContext.map;
21412             options.radius = radius;
21413             options.center = center;
21414             this.gMapContext.circle = new google.maps.Circle(options);
21415             return this.gMapContext.circle;
21416         }
21417         
21418         return null;
21419     },
21420     
21421     setPosition: function(location) 
21422     {
21423         this.gMapContext.location = location;
21424         this.gMapContext.marker.setPosition(location);
21425         this.gMapContext.map.panTo(location);
21426         this.drawCircle(location, this.gMapContext.radius, {});
21427         
21428         var _this = this;
21429         
21430         if (this.gMapContext.settings.enableReverseGeocode) {
21431             this.gMapContext.geodecoder.geocode({
21432                 latLng: this.gMapContext.location
21433             }, function(results, status) {
21434                 
21435                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
21436                     _this.gMapContext.locationName = results[0].formatted_address;
21437                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
21438                     
21439                     _this.fireEvent('positionchanged', this, location);
21440                 }
21441             });
21442             
21443             return;
21444         }
21445         
21446         this.fireEvent('positionchanged', this, location);
21447     },
21448     
21449     resize: function()
21450     {
21451         google.maps.event.trigger(this.gMapContext.map, "resize");
21452         
21453         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
21454         
21455         this.fireEvent('resize', this);
21456     },
21457     
21458     setPositionByLatLng: function(latitude, longitude)
21459     {
21460         this.setPosition(new google.maps.LatLng(latitude, longitude));
21461     },
21462     
21463     getCurrentPosition: function() 
21464     {
21465         return {
21466             latitude: this.gMapContext.location.lat(),
21467             longitude: this.gMapContext.location.lng()
21468         };
21469     },
21470     
21471     getAddressName: function() 
21472     {
21473         return this.gMapContext.locationName;
21474     },
21475     
21476     getAddressComponents: function() 
21477     {
21478         return this.gMapContext.addressComponents;
21479     },
21480     
21481     address_component_from_google_geocode: function(address_components) 
21482     {
21483         var result = {};
21484         
21485         for (var i = 0; i < address_components.length; i++) {
21486             var component = address_components[i];
21487             if (component.types.indexOf("postal_code") >= 0) {
21488                 result.postalCode = component.short_name;
21489             } else if (component.types.indexOf("street_number") >= 0) {
21490                 result.streetNumber = component.short_name;
21491             } else if (component.types.indexOf("route") >= 0) {
21492                 result.streetName = component.short_name;
21493             } else if (component.types.indexOf("neighborhood") >= 0) {
21494                 result.city = component.short_name;
21495             } else if (component.types.indexOf("locality") >= 0) {
21496                 result.city = component.short_name;
21497             } else if (component.types.indexOf("sublocality") >= 0) {
21498                 result.district = component.short_name;
21499             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
21500                 result.stateOrProvince = component.short_name;
21501             } else if (component.types.indexOf("country") >= 0) {
21502                 result.country = component.short_name;
21503             }
21504         }
21505         
21506         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
21507         result.addressLine2 = "";
21508         return result;
21509     },
21510     
21511     setZoomLevel: function(zoom)
21512     {
21513         this.gMapContext.map.setZoom(zoom);
21514     },
21515     
21516     show: function()
21517     {
21518         if(!this.el){
21519             return;
21520         }
21521         
21522         this.el.show();
21523         
21524         this.resize();
21525         
21526         this.fireEvent('show', this);
21527     },
21528     
21529     hide: function()
21530     {
21531         if(!this.el){
21532             return;
21533         }
21534         
21535         this.el.hide();
21536         
21537         this.fireEvent('hide', this);
21538     }
21539     
21540 });
21541 /*
21542  * - LGPL
21543  *
21544  * Alert
21545  * 
21546  */
21547
21548 /**
21549  * @class Roo.bootstrap.Alert
21550  * @extends Roo.bootstrap.Component
21551  * Bootstrap Alert class
21552  * @cfg {String} title The title of alert
21553  * @cfg {String} html The content of alert
21554  * @cfg {String} weight (  success | info | warning | danger )
21555  * @cfg {String} faicon font-awesomeicon
21556  * 
21557  * @constructor
21558  * Create a new alert
21559  * @param {Object} config The config object
21560  */
21561
21562
21563 Roo.bootstrap.Alert = function(config){
21564     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
21565     
21566 };
21567
21568 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
21569     
21570     title: '',
21571     html: '',
21572     weight: false,
21573     faicon: false,
21574     
21575     getAutoCreate : function()
21576     {
21577         
21578         var cfg = {
21579             tag : 'div',
21580             cls : 'alert',
21581             cn : [
21582                 {
21583                     tag : 'i',
21584                     cls : 'roo-alert-icon'
21585                     
21586                 },
21587                 {
21588                     tag : 'b',
21589                     cls : 'roo-alert-title',
21590                     html : this.title
21591                 },
21592                 {
21593                     tag : 'span',
21594                     cls : 'roo-alert-text',
21595                     html : this.html
21596                 }
21597             ]
21598         };
21599         
21600         if(this.faicon){
21601             cfg.cn[0].cls += ' fa ' + this.faicon;
21602         }
21603         
21604         if(this.weight){
21605             cfg.cls += ' alert-' + this.weight;
21606         }
21607         
21608         return cfg;
21609     },
21610     
21611     initEvents: function() 
21612     {
21613         this.el.setVisibilityMode(Roo.Element.DISPLAY);
21614     },
21615     
21616     setTitle : function(str)
21617     {
21618         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
21619     },
21620     
21621     setText : function(str)
21622     {
21623         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
21624     },
21625     
21626     setWeight : function(weight)
21627     {
21628         if(this.weight){
21629             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
21630         }
21631         
21632         this.weight = weight;
21633         
21634         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
21635     },
21636     
21637     setIcon : function(icon)
21638     {
21639         if(this.faicon){
21640             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
21641         }
21642         
21643         this.faicon = icon
21644         
21645         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
21646     },
21647     
21648     hide: function() 
21649     {
21650         this.el.hide();   
21651     },
21652     
21653     show: function() 
21654     {  
21655         this.el.show();   
21656     }
21657     
21658 });
21659
21660