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         
6921         iter(this);
6922         return r;
6923         
6924         
6925         
6926         
6927     }
6928     
6929 });
6930
6931  
6932 /*
6933  * Based on:
6934  * Ext JS Library 1.1.1
6935  * Copyright(c) 2006-2007, Ext JS, LLC.
6936  *
6937  * Originally Released Under LGPL - original licence link has changed is not relivant.
6938  *
6939  * Fork - LGPL
6940  * <script type="text/javascript">
6941  */
6942 /**
6943  * @class Roo.form.VTypes
6944  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6945  * @singleton
6946  */
6947 Roo.form.VTypes = function(){
6948     // closure these in so they are only created once.
6949     var alpha = /^[a-zA-Z_]+$/;
6950     var alphanum = /^[a-zA-Z0-9_]+$/;
6951     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6952     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6953
6954     // All these messages and functions are configurable
6955     return {
6956         /**
6957          * The function used to validate email addresses
6958          * @param {String} value The email address
6959          */
6960         'email' : function(v){
6961             return email.test(v);
6962         },
6963         /**
6964          * The error text to display when the email validation function returns false
6965          * @type String
6966          */
6967         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6968         /**
6969          * The keystroke filter mask to be applied on email input
6970          * @type RegExp
6971          */
6972         'emailMask' : /[a-z0-9_\.\-@]/i,
6973
6974         /**
6975          * The function used to validate URLs
6976          * @param {String} value The URL
6977          */
6978         'url' : function(v){
6979             return url.test(v);
6980         },
6981         /**
6982          * The error text to display when the url validation function returns false
6983          * @type String
6984          */
6985         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6986         
6987         /**
6988          * The function used to validate alpha values
6989          * @param {String} value The value
6990          */
6991         'alpha' : function(v){
6992             return alpha.test(v);
6993         },
6994         /**
6995          * The error text to display when the alpha validation function returns false
6996          * @type String
6997          */
6998         'alphaText' : 'This field should only contain letters and _',
6999         /**
7000          * The keystroke filter mask to be applied on alpha input
7001          * @type RegExp
7002          */
7003         'alphaMask' : /[a-z_]/i,
7004
7005         /**
7006          * The function used to validate alphanumeric values
7007          * @param {String} value The value
7008          */
7009         'alphanum' : function(v){
7010             return alphanum.test(v);
7011         },
7012         /**
7013          * The error text to display when the alphanumeric validation function returns false
7014          * @type String
7015          */
7016         'alphanumText' : 'This field should only contain letters, numbers and _',
7017         /**
7018          * The keystroke filter mask to be applied on alphanumeric input
7019          * @type RegExp
7020          */
7021         'alphanumMask' : /[a-z0-9_]/i
7022     };
7023 }();/*
7024  * - LGPL
7025  *
7026  * Input
7027  * 
7028  */
7029
7030 /**
7031  * @class Roo.bootstrap.Input
7032  * @extends Roo.bootstrap.Component
7033  * Bootstrap Input class
7034  * @cfg {Boolean} disabled is it disabled
7035  * @cfg {String} fieldLabel - the label associated
7036  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7037  * @cfg {String} name name of the input
7038  * @cfg {string} fieldLabel - the label associated
7039  * @cfg {string}  inputType - input / file submit ...
7040  * @cfg {string} placeholder - placeholder to put in text.
7041  * @cfg {string}  before - input group add on before
7042  * @cfg {string} after - input group add on after
7043  * @cfg {string} size - (lg|sm) or leave empty..
7044  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7045  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7046  * @cfg {Number} md colspan out of 12 for computer-sized screens
7047  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7048  * @cfg {string} value default value of the input
7049  * @cfg {Number} labelWidth set the width of label (0-12)
7050  * @cfg {String} labelAlign (top|left)
7051  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7052  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7053
7054  * @cfg {String} align (left|center|right) Default left
7055  * 
7056  * 
7057  * 
7058  * @constructor
7059  * Create a new Input
7060  * @param {Object} config The config object
7061  */
7062
7063 Roo.bootstrap.Input = function(config){
7064     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7065    
7066         this.addEvents({
7067             /**
7068              * @event focus
7069              * Fires when this field receives input focus.
7070              * @param {Roo.form.Field} this
7071              */
7072             focus : true,
7073             /**
7074              * @event blur
7075              * Fires when this field loses input focus.
7076              * @param {Roo.form.Field} this
7077              */
7078             blur : true,
7079             /**
7080              * @event specialkey
7081              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7082              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7083              * @param {Roo.form.Field} this
7084              * @param {Roo.EventObject} e The event object
7085              */
7086             specialkey : true,
7087             /**
7088              * @event change
7089              * Fires just before the field blurs if the field value has changed.
7090              * @param {Roo.form.Field} this
7091              * @param {Mixed} newValue The new value
7092              * @param {Mixed} oldValue The original value
7093              */
7094             change : true,
7095             /**
7096              * @event invalid
7097              * Fires after the field has been marked as invalid.
7098              * @param {Roo.form.Field} this
7099              * @param {String} msg The validation message
7100              */
7101             invalid : true,
7102             /**
7103              * @event valid
7104              * Fires after the field has been validated with no errors.
7105              * @param {Roo.form.Field} this
7106              */
7107             valid : true,
7108              /**
7109              * @event keyup
7110              * Fires after the key up
7111              * @param {Roo.form.Field} this
7112              * @param {Roo.EventObject}  e The event Object
7113              */
7114             keyup : true
7115         });
7116 };
7117
7118 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7119      /**
7120      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7121       automatic validation (defaults to "keyup").
7122      */
7123     validationEvent : "keyup",
7124      /**
7125      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7126      */
7127     validateOnBlur : true,
7128     /**
7129      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7130      */
7131     validationDelay : 250,
7132      /**
7133      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7134      */
7135     focusClass : "x-form-focus",  // not needed???
7136     
7137        
7138     /**
7139      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7140      */
7141     invalidClass : "has-warning",
7142     
7143     /**
7144      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7145      */
7146     validClass : "has-success",
7147     
7148     /**
7149      * @cfg {Boolean} hasFeedback (true|false) default true
7150      */
7151     hasFeedback : true,
7152     
7153     /**
7154      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7155      */
7156     invalidFeedbackClass : "glyphicon-warning-sign",
7157     
7158     /**
7159      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7160      */
7161     validFeedbackClass : "glyphicon-ok",
7162     
7163     /**
7164      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7165      */
7166     selectOnFocus : false,
7167     
7168      /**
7169      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7170      */
7171     maskRe : null,
7172        /**
7173      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7174      */
7175     vtype : null,
7176     
7177       /**
7178      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7179      */
7180     disableKeyFilter : false,
7181     
7182        /**
7183      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7184      */
7185     disabled : false,
7186      /**
7187      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7188      */
7189     allowBlank : true,
7190     /**
7191      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7192      */
7193     blankText : "This field is required",
7194     
7195      /**
7196      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7197      */
7198     minLength : 0,
7199     /**
7200      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7201      */
7202     maxLength : Number.MAX_VALUE,
7203     /**
7204      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7205      */
7206     minLengthText : "The minimum length for this field is {0}",
7207     /**
7208      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7209      */
7210     maxLengthText : "The maximum length for this field is {0}",
7211   
7212     
7213     /**
7214      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7215      * If available, this function will be called only after the basic validators all return true, and will be passed the
7216      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7217      */
7218     validator : null,
7219     /**
7220      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7221      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7222      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7223      */
7224     regex : null,
7225     /**
7226      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7227      */
7228     regexText : "",
7229     
7230     autocomplete: false,
7231     
7232     
7233     fieldLabel : '',
7234     inputType : 'text',
7235     
7236     name : false,
7237     placeholder: false,
7238     before : false,
7239     after : false,
7240     size : false,
7241     hasFocus : false,
7242     preventMark: false,
7243     isFormField : true,
7244     value : '',
7245     labelWidth : 2,
7246     labelAlign : false,
7247     readOnly : false,
7248     align : false,
7249     formatedValue : false,
7250     
7251     parentLabelAlign : function()
7252     {
7253         var parent = this;
7254         while (parent.parent()) {
7255             parent = parent.parent();
7256             if (typeof(parent.labelAlign) !='undefined') {
7257                 return parent.labelAlign;
7258             }
7259         }
7260         return 'left';
7261         
7262     },
7263     
7264     getAutoCreate : function(){
7265         
7266         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7267         
7268         var id = Roo.id();
7269         
7270         var cfg = {};
7271         
7272         if(this.inputType != 'hidden'){
7273             cfg.cls = 'form-group' //input-group
7274         }
7275         
7276         var input =  {
7277             tag: 'input',
7278             id : id,
7279             type : this.inputType,
7280             value : this.value,
7281             cls : 'form-control',
7282             placeholder : this.placeholder || '',
7283             autocomplete : this.autocomplete || 'new-password'
7284         };
7285         
7286         
7287         if(this.align){
7288             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7289         }
7290         
7291         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7292             input.maxLength = this.maxLength;
7293         }
7294         
7295         if (this.disabled) {
7296             input.disabled=true;
7297         }
7298         
7299         if (this.readOnly) {
7300             input.readonly=true;
7301         }
7302         
7303         if (this.name) {
7304             input.name = this.name;
7305         }
7306         if (this.size) {
7307             input.cls += ' input-' + this.size;
7308         }
7309         var settings=this;
7310         ['xs','sm','md','lg'].map(function(size){
7311             if (settings[size]) {
7312                 cfg.cls += ' col-' + size + '-' + settings[size];
7313             }
7314         });
7315         
7316         var inputblock = input;
7317         
7318         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7319             
7320             var feedback = {
7321                 tag: 'span',
7322                 cls: 'glyphicon form-control-feedback'
7323             };
7324
7325             inputblock = {
7326                 cls : 'has-feedback',
7327                 cn :  [
7328                     input,
7329                     feedback
7330                 ] 
7331             };  
7332         }
7333          
7334 //        var inputblock = input;
7335         
7336         if (this.before || this.after) {
7337             
7338             inputblock = {
7339                 cls : 'input-group',
7340                 cn :  [] 
7341             };
7342             
7343             if (this.before && typeof(this.before) == 'string') {
7344                 
7345                 inputblock.cn.push({
7346                     tag :'span',
7347                     cls : 'roo-input-before input-group-addon',
7348                     html : this.before
7349                 });
7350             }
7351             if (this.before && typeof(this.before) == 'object') {
7352                 this.before = Roo.factory(this.before);
7353                 Roo.log(this.before);
7354                 inputblock.cn.push({
7355                     tag :'span',
7356                     cls : 'roo-input-before input-group-' +
7357                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7358                 });
7359             }
7360             
7361             inputblock.cn.push(input);
7362             
7363             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7364                 inputblock.cls += ' has-feedback';
7365                 inputblock.cn.push(feedback);
7366             }
7367             
7368             if (this.after && typeof(this.after) == 'string') {
7369                 inputblock.cn.push({
7370                     tag :'span',
7371                     cls : 'roo-input-after input-group-addon',
7372                     html : this.after
7373                 });
7374             }
7375             if (this.after && typeof(this.after) == 'object') {
7376                 this.after = Roo.factory(this.after);
7377                 Roo.log(this.after);
7378                 inputblock.cn.push({
7379                     tag :'span',
7380                     cls : 'roo-input-after input-group-' +
7381                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7382                 });
7383             }
7384         };
7385         
7386         if (align ==='left' && this.fieldLabel.length) {
7387                 Roo.log("left and has label");
7388                 cfg.cn = [
7389                     
7390                     {
7391                         tag: 'label',
7392                         'for' :  id,
7393                         cls : 'control-label col-sm-' + this.labelWidth,
7394                         html : this.fieldLabel
7395                         
7396                     },
7397                     {
7398                         cls : "col-sm-" + (12 - this.labelWidth), 
7399                         cn: [
7400                             inputblock
7401                         ]
7402                     }
7403                     
7404                 ];
7405         } else if ( this.fieldLabel.length) {
7406                 Roo.log(" label");
7407                  cfg.cn = [
7408                    
7409                     {
7410                         tag: 'label',
7411                         //cls : 'input-group-addon',
7412                         html : this.fieldLabel
7413                         
7414                     },
7415                     
7416                     inputblock
7417                     
7418                 ];
7419
7420         } else {
7421             
7422                 Roo.log(" no label && no align");
7423                 cfg.cn = [
7424                     
7425                         inputblock
7426                     
7427                 ];
7428                 
7429                 
7430         };
7431         Roo.log('input-parentType: ' + this.parentType);
7432         
7433         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7434            cfg.cls += ' navbar-form';
7435            Roo.log(cfg);
7436         }
7437         
7438         return cfg;
7439         
7440     },
7441     /**
7442      * return the real input element.
7443      */
7444     inputEl: function ()
7445     {
7446         return this.el.select('input.form-control',true).first();
7447     },
7448     
7449     tooltipEl : function()
7450     {
7451         return this.inputEl();
7452     },
7453     
7454     setDisabled : function(v)
7455     {
7456         var i  = this.inputEl().dom;
7457         if (!v) {
7458             i.removeAttribute('disabled');
7459             return;
7460             
7461         }
7462         i.setAttribute('disabled','true');
7463     },
7464     initEvents : function()
7465     {
7466           
7467         this.inputEl().on("keydown" , this.fireKey,  this);
7468         this.inputEl().on("focus", this.onFocus,  this);
7469         this.inputEl().on("blur", this.onBlur,  this);
7470         
7471         this.inputEl().relayEvent('keyup', this);
7472
7473         // reference to original value for reset
7474         this.originalValue = this.getValue();
7475         //Roo.form.TextField.superclass.initEvents.call(this);
7476         if(this.validationEvent == 'keyup'){
7477             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7478             this.inputEl().on('keyup', this.filterValidation, this);
7479         }
7480         else if(this.validationEvent !== false){
7481             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7482         }
7483         
7484         if(this.selectOnFocus){
7485             this.on("focus", this.preFocus, this);
7486             
7487         }
7488         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7489             this.inputEl().on("keypress", this.filterKeys, this);
7490         }
7491        /* if(this.grow){
7492             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7493             this.el.on("click", this.autoSize,  this);
7494         }
7495         */
7496         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7497             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7498         }
7499         
7500         if (typeof(this.before) == 'object') {
7501             this.before.render(this.el.select('.roo-input-before',true).first());
7502         }
7503         if (typeof(this.after) == 'object') {
7504             this.after.render(this.el.select('.roo-input-after',true).first());
7505         }
7506         
7507         
7508     },
7509     filterValidation : function(e){
7510         if(!e.isNavKeyPress()){
7511             this.validationTask.delay(this.validationDelay);
7512         }
7513     },
7514      /**
7515      * Validates the field value
7516      * @return {Boolean} True if the value is valid, else false
7517      */
7518     validate : function(){
7519         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7520         if(this.disabled || this.validateValue(this.getRawValue())){
7521             this.markValid();
7522             return true;
7523         }
7524         
7525         this.markInvalid();
7526         return false;
7527     },
7528     
7529     
7530     /**
7531      * Validates a value according to the field's validation rules and marks the field as invalid
7532      * if the validation fails
7533      * @param {Mixed} value The value to validate
7534      * @return {Boolean} True if the value is valid, else false
7535      */
7536     validateValue : function(value){
7537         if(value.length < 1)  { // if it's blank
7538             if(this.allowBlank){
7539                 return true;
7540             }
7541             return false;
7542         }
7543         
7544         if(value.length < this.minLength){
7545             return false;
7546         }
7547         if(value.length > this.maxLength){
7548             return false;
7549         }
7550         if(this.vtype){
7551             var vt = Roo.form.VTypes;
7552             if(!vt[this.vtype](value, this)){
7553                 return false;
7554             }
7555         }
7556         if(typeof this.validator == "function"){
7557             var msg = this.validator(value);
7558             if(msg !== true){
7559                 return false;
7560             }
7561         }
7562         
7563         if(this.regex && !this.regex.test(value)){
7564             return false;
7565         }
7566         
7567         return true;
7568     },
7569
7570     
7571     
7572      // private
7573     fireKey : function(e){
7574         //Roo.log('field ' + e.getKey());
7575         if(e.isNavKeyPress()){
7576             this.fireEvent("specialkey", this, e);
7577         }
7578     },
7579     focus : function (selectText){
7580         if(this.rendered){
7581             this.inputEl().focus();
7582             if(selectText === true){
7583                 this.inputEl().dom.select();
7584             }
7585         }
7586         return this;
7587     } ,
7588     
7589     onFocus : function(){
7590         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7591            // this.el.addClass(this.focusClass);
7592         }
7593         if(!this.hasFocus){
7594             this.hasFocus = true;
7595             this.startValue = this.getValue();
7596             this.fireEvent("focus", this);
7597         }
7598     },
7599     
7600     beforeBlur : Roo.emptyFn,
7601
7602     
7603     // private
7604     onBlur : function(){
7605         this.beforeBlur();
7606         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7607             //this.el.removeClass(this.focusClass);
7608         }
7609         this.hasFocus = false;
7610         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7611             this.validate();
7612         }
7613         var v = this.getValue();
7614         if(String(v) !== String(this.startValue)){
7615             this.fireEvent('change', this, v, this.startValue);
7616         }
7617         this.fireEvent("blur", this);
7618     },
7619     
7620     /**
7621      * Resets the current field value to the originally loaded value and clears any validation messages
7622      */
7623     reset : function(){
7624         this.setValue(this.originalValue);
7625         this.validate();
7626     },
7627      /**
7628      * Returns the name of the field
7629      * @return {Mixed} name The name field
7630      */
7631     getName: function(){
7632         return this.name;
7633     },
7634      /**
7635      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7636      * @return {Mixed} value The field value
7637      */
7638     getValue : function(){
7639         
7640         var v = this.inputEl().getValue();
7641         
7642         return v;
7643     },
7644     /**
7645      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7646      * @return {Mixed} value The field value
7647      */
7648     getRawValue : function(){
7649         var v = this.inputEl().getValue();
7650         
7651         return v;
7652     },
7653     
7654     /**
7655      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7656      * @param {Mixed} value The value to set
7657      */
7658     setRawValue : function(v){
7659         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7660     },
7661     
7662     selectText : function(start, end){
7663         var v = this.getRawValue();
7664         if(v.length > 0){
7665             start = start === undefined ? 0 : start;
7666             end = end === undefined ? v.length : end;
7667             var d = this.inputEl().dom;
7668             if(d.setSelectionRange){
7669                 d.setSelectionRange(start, end);
7670             }else if(d.createTextRange){
7671                 var range = d.createTextRange();
7672                 range.moveStart("character", start);
7673                 range.moveEnd("character", v.length-end);
7674                 range.select();
7675             }
7676         }
7677     },
7678     
7679     /**
7680      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7681      * @param {Mixed} value The value to set
7682      */
7683     setValue : function(v){
7684         this.value = v;
7685         if(this.rendered){
7686             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7687             this.validate();
7688         }
7689     },
7690     
7691     /*
7692     processValue : function(value){
7693         if(this.stripCharsRe){
7694             var newValue = value.replace(this.stripCharsRe, '');
7695             if(newValue !== value){
7696                 this.setRawValue(newValue);
7697                 return newValue;
7698             }
7699         }
7700         return value;
7701     },
7702   */
7703     preFocus : function(){
7704         
7705         if(this.selectOnFocus){
7706             this.inputEl().dom.select();
7707         }
7708     },
7709     filterKeys : function(e){
7710         var k = e.getKey();
7711         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7712             return;
7713         }
7714         var c = e.getCharCode(), cc = String.fromCharCode(c);
7715         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7716             return;
7717         }
7718         if(!this.maskRe.test(cc)){
7719             e.stopEvent();
7720         }
7721     },
7722      /**
7723      * Clear any invalid styles/messages for this field
7724      */
7725     clearInvalid : function(){
7726         
7727         if(!this.el || this.preventMark){ // not rendered
7728             return;
7729         }
7730         this.el.removeClass(this.invalidClass);
7731         
7732         this.fireEvent('valid', this);
7733     },
7734     
7735      /**
7736      * Mark this field as valid
7737      */
7738     markValid : function(){
7739         if(!this.el  || this.preventMark){ // not rendered
7740             return;
7741         }
7742         
7743         this.el.removeClass([this.invalidClass, this.validClass]);
7744         
7745         if(this.disabled || this.allowBlank){
7746             return;
7747         }
7748         
7749         this.el.addClass(this.validClass);
7750         
7751         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
7752             
7753             var feedback = this.el.select('.form-control-feedback', true).first();
7754             
7755             if(feedback){
7756                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7757                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
7758             }
7759             
7760         }
7761         
7762         this.fireEvent('valid', this);
7763     },
7764     
7765      /**
7766      * Mark this field as invalid
7767      * @param {String} msg The validation message
7768      */
7769     markInvalid : function(msg){
7770         if(!this.el  || this.preventMark){ // not rendered
7771             return;
7772         }
7773         
7774         this.el.removeClass([this.invalidClass, this.validClass]);
7775         
7776         if(this.disabled || this.allowBlank){
7777             return;
7778         }
7779         
7780         this.el.addClass(this.invalidClass);
7781         
7782         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7783             
7784             var feedback = this.el.select('.form-control-feedback', true).first();
7785             
7786             if(feedback){
7787                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7788                 
7789                 if(this.getValue().length){
7790                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
7791                 }
7792                 
7793             }
7794             
7795         }
7796         
7797         this.fireEvent('invalid', this, msg);
7798     },
7799     // private
7800     SafariOnKeyDown : function(event)
7801     {
7802         // this is a workaround for a password hang bug on chrome/ webkit.
7803         
7804         var isSelectAll = false;
7805         
7806         if(this.inputEl().dom.selectionEnd > 0){
7807             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7808         }
7809         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7810             event.preventDefault();
7811             this.setValue('');
7812             return;
7813         }
7814         
7815         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
7816             
7817             event.preventDefault();
7818             // this is very hacky as keydown always get's upper case.
7819             //
7820             var cc = String.fromCharCode(event.getCharCode());
7821             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7822             
7823         }
7824     },
7825     adjustWidth : function(tag, w){
7826         tag = tag.toLowerCase();
7827         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7828             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7829                 if(tag == 'input'){
7830                     return w + 2;
7831                 }
7832                 if(tag == 'textarea'){
7833                     return w-2;
7834                 }
7835             }else if(Roo.isOpera){
7836                 if(tag == 'input'){
7837                     return w + 2;
7838                 }
7839                 if(tag == 'textarea'){
7840                     return w-2;
7841                 }
7842             }
7843         }
7844         return w;
7845     }
7846     
7847 });
7848
7849  
7850 /*
7851  * - LGPL
7852  *
7853  * Input
7854  * 
7855  */
7856
7857 /**
7858  * @class Roo.bootstrap.TextArea
7859  * @extends Roo.bootstrap.Input
7860  * Bootstrap TextArea class
7861  * @cfg {Number} cols Specifies the visible width of a text area
7862  * @cfg {Number} rows Specifies the visible number of lines in a text area
7863  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7864  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7865  * @cfg {string} html text
7866  * 
7867  * @constructor
7868  * Create a new TextArea
7869  * @param {Object} config The config object
7870  */
7871
7872 Roo.bootstrap.TextArea = function(config){
7873     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7874    
7875 };
7876
7877 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7878      
7879     cols : false,
7880     rows : 5,
7881     readOnly : false,
7882     warp : 'soft',
7883     resize : false,
7884     value: false,
7885     html: false,
7886     
7887     getAutoCreate : function(){
7888         
7889         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7890         
7891         var id = Roo.id();
7892         
7893         var cfg = {};
7894         
7895         var input =  {
7896             tag: 'textarea',
7897             id : id,
7898             warp : this.warp,
7899             rows : this.rows,
7900             value : this.value || '',
7901             html: this.html || '',
7902             cls : 'form-control',
7903             placeholder : this.placeholder || '' 
7904             
7905         };
7906         
7907         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7908             input.maxLength = this.maxLength;
7909         }
7910         
7911         if(this.resize){
7912             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7913         }
7914         
7915         if(this.cols){
7916             input.cols = this.cols;
7917         }
7918         
7919         if (this.readOnly) {
7920             input.readonly = true;
7921         }
7922         
7923         if (this.name) {
7924             input.name = this.name;
7925         }
7926         
7927         if (this.size) {
7928             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7929         }
7930         
7931         var settings=this;
7932         ['xs','sm','md','lg'].map(function(size){
7933             if (settings[size]) {
7934                 cfg.cls += ' col-' + size + '-' + settings[size];
7935             }
7936         });
7937         
7938         var inputblock = input;
7939         
7940         if(this.hasFeedback && !this.allowBlank){
7941             
7942             var feedback = {
7943                 tag: 'span',
7944                 cls: 'glyphicon form-control-feedback'
7945             };
7946
7947             inputblock = {
7948                 cls : 'has-feedback',
7949                 cn :  [
7950                     input,
7951                     feedback
7952                 ] 
7953             };  
7954         }
7955         
7956         
7957         if (this.before || this.after) {
7958             
7959             inputblock = {
7960                 cls : 'input-group',
7961                 cn :  [] 
7962             };
7963             if (this.before) {
7964                 inputblock.cn.push({
7965                     tag :'span',
7966                     cls : 'input-group-addon',
7967                     html : this.before
7968                 });
7969             }
7970             
7971             inputblock.cn.push(input);
7972             
7973             if(this.hasFeedback && !this.allowBlank){
7974                 inputblock.cls += ' has-feedback';
7975                 inputblock.cn.push(feedback);
7976             }
7977             
7978             if (this.after) {
7979                 inputblock.cn.push({
7980                     tag :'span',
7981                     cls : 'input-group-addon',
7982                     html : this.after
7983                 });
7984             }
7985             
7986         }
7987         
7988         if (align ==='left' && this.fieldLabel.length) {
7989                 Roo.log("left and has label");
7990                 cfg.cn = [
7991                     
7992                     {
7993                         tag: 'label',
7994                         'for' :  id,
7995                         cls : 'control-label col-sm-' + this.labelWidth,
7996                         html : this.fieldLabel
7997                         
7998                     },
7999                     {
8000                         cls : "col-sm-" + (12 - this.labelWidth), 
8001                         cn: [
8002                             inputblock
8003                         ]
8004                     }
8005                     
8006                 ];
8007         } else if ( this.fieldLabel.length) {
8008                 Roo.log(" label");
8009                  cfg.cn = [
8010                    
8011                     {
8012                         tag: 'label',
8013                         //cls : 'input-group-addon',
8014                         html : this.fieldLabel
8015                         
8016                     },
8017                     
8018                     inputblock
8019                     
8020                 ];
8021
8022         } else {
8023             
8024                    Roo.log(" no label && no align");
8025                 cfg.cn = [
8026                     
8027                         inputblock
8028                     
8029                 ];
8030                 
8031                 
8032         }
8033         
8034         if (this.disabled) {
8035             input.disabled=true;
8036         }
8037         
8038         return cfg;
8039         
8040     },
8041     /**
8042      * return the real textarea element.
8043      */
8044     inputEl: function ()
8045     {
8046         return this.el.select('textarea.form-control',true).first();
8047     }
8048 });
8049
8050  
8051 /*
8052  * - LGPL
8053  *
8054  * trigger field - base class for combo..
8055  * 
8056  */
8057  
8058 /**
8059  * @class Roo.bootstrap.TriggerField
8060  * @extends Roo.bootstrap.Input
8061  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8062  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8063  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8064  * for which you can provide a custom implementation.  For example:
8065  * <pre><code>
8066 var trigger = new Roo.bootstrap.TriggerField();
8067 trigger.onTriggerClick = myTriggerFn;
8068 trigger.applyTo('my-field');
8069 </code></pre>
8070  *
8071  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8072  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8073  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8074  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8075  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8076
8077  * @constructor
8078  * Create a new TriggerField.
8079  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8080  * to the base TextField)
8081  */
8082 Roo.bootstrap.TriggerField = function(config){
8083     this.mimicing = false;
8084     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8085 };
8086
8087 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8088     /**
8089      * @cfg {String} triggerClass A CSS class to apply to the trigger
8090      */
8091      /**
8092      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8093      */
8094     hideTrigger:false,
8095
8096     /** @cfg {Boolean} grow @hide */
8097     /** @cfg {Number} growMin @hide */
8098     /** @cfg {Number} growMax @hide */
8099
8100     /**
8101      * @hide 
8102      * @method
8103      */
8104     autoSize: Roo.emptyFn,
8105     // private
8106     monitorTab : true,
8107     // private
8108     deferHeight : true,
8109
8110     
8111     actionMode : 'wrap',
8112     
8113     caret : false,
8114     
8115     
8116     getAutoCreate : function(){
8117        
8118         var align = this.labelAlign || this.parentLabelAlign();
8119         
8120         var id = Roo.id();
8121         
8122         var cfg = {
8123             cls: 'form-group' //input-group
8124         };
8125         
8126         
8127         var input =  {
8128             tag: 'input',
8129             id : id,
8130             type : this.inputType,
8131             cls : 'form-control',
8132             autocomplete: 'new-password',
8133             placeholder : this.placeholder || '' 
8134             
8135         };
8136         if (this.name) {
8137             input.name = this.name;
8138         }
8139         if (this.size) {
8140             input.cls += ' input-' + this.size;
8141         }
8142         
8143         if (this.disabled) {
8144             input.disabled=true;
8145         }
8146         
8147         var inputblock = input;
8148         
8149         if(this.hasFeedback && !this.allowBlank){
8150             
8151             var feedback = {
8152                 tag: 'span',
8153                 cls: 'glyphicon form-control-feedback'
8154             };
8155
8156             inputblock = {
8157                 cls : 'has-feedback',
8158                 cn :  [
8159                     input,
8160                     feedback
8161                 ] 
8162             };  
8163         }
8164         
8165         if (this.before || this.after) {
8166             
8167             inputblock = {
8168                 cls : 'input-group',
8169                 cn :  [] 
8170             };
8171             if (this.before) {
8172                 inputblock.cn.push({
8173                     tag :'span',
8174                     cls : 'input-group-addon',
8175                     html : this.before
8176                 });
8177             }
8178             
8179             inputblock.cn.push(input);
8180             
8181             if(this.hasFeedback && !this.allowBlank){
8182                 inputblock.cls += ' has-feedback';
8183                 inputblock.cn.push(feedback);
8184             }
8185             
8186             if (this.after) {
8187                 inputblock.cn.push({
8188                     tag :'span',
8189                     cls : 'input-group-addon',
8190                     html : this.after
8191                 });
8192             }
8193             
8194         };
8195         
8196         var box = {
8197             tag: 'div',
8198             cn: [
8199                 {
8200                     tag: 'input',
8201                     type : 'hidden',
8202                     cls: 'form-hidden-field'
8203                 },
8204                 inputblock
8205             ]
8206             
8207         };
8208         
8209         if(this.multiple){
8210             Roo.log('multiple');
8211             
8212             box = {
8213                 tag: 'div',
8214                 cn: [
8215                     {
8216                         tag: 'input',
8217                         type : 'hidden',
8218                         cls: 'form-hidden-field'
8219                     },
8220                     {
8221                         tag: 'ul',
8222                         cls: 'select2-choices',
8223                         cn:[
8224                             {
8225                                 tag: 'li',
8226                                 cls: 'select2-search-field',
8227                                 cn: [
8228
8229                                     inputblock
8230                                 ]
8231                             }
8232                         ]
8233                     }
8234                 ]
8235             }
8236         };
8237         
8238         var combobox = {
8239             cls: 'select2-container input-group',
8240             cn: [
8241                 box
8242 //                {
8243 //                    tag: 'ul',
8244 //                    cls: 'typeahead typeahead-long dropdown-menu',
8245 //                    style: 'display:none'
8246 //                }
8247             ]
8248         };
8249         
8250         if(!this.multiple && this.showToggleBtn){
8251             
8252             var caret = {
8253                         tag: 'span',
8254                         cls: 'caret'
8255              };
8256             if (this.caret != false) {
8257                 caret = {
8258                      tag: 'i',
8259                      cls: 'fa fa-' + this.caret
8260                 };
8261                 
8262             }
8263             
8264             combobox.cn.push({
8265                 tag :'span',
8266                 cls : 'input-group-addon btn dropdown-toggle',
8267                 cn : [
8268                     caret,
8269                     {
8270                         tag: 'span',
8271                         cls: 'combobox-clear',
8272                         cn  : [
8273                             {
8274                                 tag : 'i',
8275                                 cls: 'icon-remove'
8276                             }
8277                         ]
8278                     }
8279                 ]
8280
8281             })
8282         }
8283         
8284         if(this.multiple){
8285             combobox.cls += ' select2-container-multi';
8286         }
8287         
8288         if (align ==='left' && this.fieldLabel.length) {
8289             
8290                 Roo.log("left and has label");
8291                 cfg.cn = [
8292                     
8293                     {
8294                         tag: 'label',
8295                         'for' :  id,
8296                         cls : 'control-label col-sm-' + this.labelWidth,
8297                         html : this.fieldLabel
8298                         
8299                     },
8300                     {
8301                         cls : "col-sm-" + (12 - this.labelWidth), 
8302                         cn: [
8303                             combobox
8304                         ]
8305                     }
8306                     
8307                 ];
8308         } else if ( this.fieldLabel.length) {
8309                 Roo.log(" label");
8310                  cfg.cn = [
8311                    
8312                     {
8313                         tag: 'label',
8314                         //cls : 'input-group-addon',
8315                         html : this.fieldLabel
8316                         
8317                     },
8318                     
8319                     combobox
8320                     
8321                 ];
8322
8323         } else {
8324             
8325                 Roo.log(" no label && no align");
8326                 cfg = combobox
8327                      
8328                 
8329         }
8330          
8331         var settings=this;
8332         ['xs','sm','md','lg'].map(function(size){
8333             if (settings[size]) {
8334                 cfg.cls += ' col-' + size + '-' + settings[size];
8335             }
8336         });
8337         
8338         return cfg;
8339         
8340     },
8341     
8342     
8343     
8344     // private
8345     onResize : function(w, h){
8346 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8347 //        if(typeof w == 'number'){
8348 //            var x = w - this.trigger.getWidth();
8349 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8350 //            this.trigger.setStyle('left', x+'px');
8351 //        }
8352     },
8353
8354     // private
8355     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8356
8357     // private
8358     getResizeEl : function(){
8359         return this.inputEl();
8360     },
8361
8362     // private
8363     getPositionEl : function(){
8364         return this.inputEl();
8365     },
8366
8367     // private
8368     alignErrorIcon : function(){
8369         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8370     },
8371
8372     // private
8373     initEvents : function(){
8374         
8375         this.createList();
8376         
8377         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8378         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8379         if(!this.multiple && this.showToggleBtn){
8380             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8381             if(this.hideTrigger){
8382                 this.trigger.setDisplayed(false);
8383             }
8384             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8385         }
8386         
8387         if(this.multiple){
8388             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8389         }
8390         
8391         //this.trigger.addClassOnOver('x-form-trigger-over');
8392         //this.trigger.addClassOnClick('x-form-trigger-click');
8393         
8394         //if(!this.width){
8395         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8396         //}
8397     },
8398     
8399     createList : function()
8400     {
8401         this.list = Roo.get(document.body).createChild({
8402             tag: 'ul',
8403             cls: 'typeahead typeahead-long dropdown-menu',
8404             style: 'display:none'
8405         });
8406         
8407         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8408         
8409     },
8410
8411     // private
8412     initTrigger : function(){
8413        
8414     },
8415
8416     // private
8417     onDestroy : function(){
8418         if(this.trigger){
8419             this.trigger.removeAllListeners();
8420           //  this.trigger.remove();
8421         }
8422         //if(this.wrap){
8423         //    this.wrap.remove();
8424         //}
8425         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8426     },
8427
8428     // private
8429     onFocus : function(){
8430         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8431         /*
8432         if(!this.mimicing){
8433             this.wrap.addClass('x-trigger-wrap-focus');
8434             this.mimicing = true;
8435             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8436             if(this.monitorTab){
8437                 this.el.on("keydown", this.checkTab, this);
8438             }
8439         }
8440         */
8441     },
8442
8443     // private
8444     checkTab : function(e){
8445         if(e.getKey() == e.TAB){
8446             this.triggerBlur();
8447         }
8448     },
8449
8450     // private
8451     onBlur : function(){
8452         // do nothing
8453     },
8454
8455     // private
8456     mimicBlur : function(e, t){
8457         /*
8458         if(!this.wrap.contains(t) && this.validateBlur()){
8459             this.triggerBlur();
8460         }
8461         */
8462     },
8463
8464     // private
8465     triggerBlur : function(){
8466         this.mimicing = false;
8467         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8468         if(this.monitorTab){
8469             this.el.un("keydown", this.checkTab, this);
8470         }
8471         //this.wrap.removeClass('x-trigger-wrap-focus');
8472         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8473     },
8474
8475     // private
8476     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8477     validateBlur : function(e, t){
8478         return true;
8479     },
8480
8481     // private
8482     onDisable : function(){
8483         this.inputEl().dom.disabled = true;
8484         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8485         //if(this.wrap){
8486         //    this.wrap.addClass('x-item-disabled');
8487         //}
8488     },
8489
8490     // private
8491     onEnable : function(){
8492         this.inputEl().dom.disabled = false;
8493         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8494         //if(this.wrap){
8495         //    this.el.removeClass('x-item-disabled');
8496         //}
8497     },
8498
8499     // private
8500     onShow : function(){
8501         var ae = this.getActionEl();
8502         
8503         if(ae){
8504             ae.dom.style.display = '';
8505             ae.dom.style.visibility = 'visible';
8506         }
8507     },
8508
8509     // private
8510     
8511     onHide : function(){
8512         var ae = this.getActionEl();
8513         ae.dom.style.display = 'none';
8514     },
8515
8516     /**
8517      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8518      * by an implementing function.
8519      * @method
8520      * @param {EventObject} e
8521      */
8522     onTriggerClick : Roo.emptyFn
8523 });
8524  /*
8525  * Based on:
8526  * Ext JS Library 1.1.1
8527  * Copyright(c) 2006-2007, Ext JS, LLC.
8528  *
8529  * Originally Released Under LGPL - original licence link has changed is not relivant.
8530  *
8531  * Fork - LGPL
8532  * <script type="text/javascript">
8533  */
8534
8535
8536 /**
8537  * @class Roo.data.SortTypes
8538  * @singleton
8539  * Defines the default sorting (casting?) comparison functions used when sorting data.
8540  */
8541 Roo.data.SortTypes = {
8542     /**
8543      * Default sort that does nothing
8544      * @param {Mixed} s The value being converted
8545      * @return {Mixed} The comparison value
8546      */
8547     none : function(s){
8548         return s;
8549     },
8550     
8551     /**
8552      * The regular expression used to strip tags
8553      * @type {RegExp}
8554      * @property
8555      */
8556     stripTagsRE : /<\/?[^>]+>/gi,
8557     
8558     /**
8559      * Strips all HTML tags to sort on text only
8560      * @param {Mixed} s The value being converted
8561      * @return {String} The comparison value
8562      */
8563     asText : function(s){
8564         return String(s).replace(this.stripTagsRE, "");
8565     },
8566     
8567     /**
8568      * Strips all HTML tags to sort on text only - Case insensitive
8569      * @param {Mixed} s The value being converted
8570      * @return {String} The comparison value
8571      */
8572     asUCText : function(s){
8573         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8574     },
8575     
8576     /**
8577      * Case insensitive string
8578      * @param {Mixed} s The value being converted
8579      * @return {String} The comparison value
8580      */
8581     asUCString : function(s) {
8582         return String(s).toUpperCase();
8583     },
8584     
8585     /**
8586      * Date sorting
8587      * @param {Mixed} s The value being converted
8588      * @return {Number} The comparison value
8589      */
8590     asDate : function(s) {
8591         if(!s){
8592             return 0;
8593         }
8594         if(s instanceof Date){
8595             return s.getTime();
8596         }
8597         return Date.parse(String(s));
8598     },
8599     
8600     /**
8601      * Float sorting
8602      * @param {Mixed} s The value being converted
8603      * @return {Float} The comparison value
8604      */
8605     asFloat : function(s) {
8606         var val = parseFloat(String(s).replace(/,/g, ""));
8607         if(isNaN(val)) val = 0;
8608         return val;
8609     },
8610     
8611     /**
8612      * Integer sorting
8613      * @param {Mixed} s The value being converted
8614      * @return {Number} The comparison value
8615      */
8616     asInt : function(s) {
8617         var val = parseInt(String(s).replace(/,/g, ""));
8618         if(isNaN(val)) val = 0;
8619         return val;
8620     }
8621 };/*
8622  * Based on:
8623  * Ext JS Library 1.1.1
8624  * Copyright(c) 2006-2007, Ext JS, LLC.
8625  *
8626  * Originally Released Under LGPL - original licence link has changed is not relivant.
8627  *
8628  * Fork - LGPL
8629  * <script type="text/javascript">
8630  */
8631
8632 /**
8633 * @class Roo.data.Record
8634  * Instances of this class encapsulate both record <em>definition</em> information, and record
8635  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8636  * to access Records cached in an {@link Roo.data.Store} object.<br>
8637  * <p>
8638  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8639  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8640  * objects.<br>
8641  * <p>
8642  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8643  * @constructor
8644  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8645  * {@link #create}. The parameters are the same.
8646  * @param {Array} data An associative Array of data values keyed by the field name.
8647  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8648  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8649  * not specified an integer id is generated.
8650  */
8651 Roo.data.Record = function(data, id){
8652     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8653     this.data = data;
8654 };
8655
8656 /**
8657  * Generate a constructor for a specific record layout.
8658  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8659  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8660  * Each field definition object may contain the following properties: <ul>
8661  * <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,
8662  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8663  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8664  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8665  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8666  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8667  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8668  * this may be omitted.</p></li>
8669  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8670  * <ul><li>auto (Default, implies no conversion)</li>
8671  * <li>string</li>
8672  * <li>int</li>
8673  * <li>float</li>
8674  * <li>boolean</li>
8675  * <li>date</li></ul></p></li>
8676  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8677  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8678  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8679  * by the Reader into an object that will be stored in the Record. It is passed the
8680  * following parameters:<ul>
8681  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8682  * </ul></p></li>
8683  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8684  * </ul>
8685  * <br>usage:<br><pre><code>
8686 var TopicRecord = Roo.data.Record.create(
8687     {name: 'title', mapping: 'topic_title'},
8688     {name: 'author', mapping: 'username'},
8689     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8690     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8691     {name: 'lastPoster', mapping: 'user2'},
8692     {name: 'excerpt', mapping: 'post_text'}
8693 );
8694
8695 var myNewRecord = new TopicRecord({
8696     title: 'Do my job please',
8697     author: 'noobie',
8698     totalPosts: 1,
8699     lastPost: new Date(),
8700     lastPoster: 'Animal',
8701     excerpt: 'No way dude!'
8702 });
8703 myStore.add(myNewRecord);
8704 </code></pre>
8705  * @method create
8706  * @static
8707  */
8708 Roo.data.Record.create = function(o){
8709     var f = function(){
8710         f.superclass.constructor.apply(this, arguments);
8711     };
8712     Roo.extend(f, Roo.data.Record);
8713     var p = f.prototype;
8714     p.fields = new Roo.util.MixedCollection(false, function(field){
8715         return field.name;
8716     });
8717     for(var i = 0, len = o.length; i < len; i++){
8718         p.fields.add(new Roo.data.Field(o[i]));
8719     }
8720     f.getField = function(name){
8721         return p.fields.get(name);  
8722     };
8723     return f;
8724 };
8725
8726 Roo.data.Record.AUTO_ID = 1000;
8727 Roo.data.Record.EDIT = 'edit';
8728 Roo.data.Record.REJECT = 'reject';
8729 Roo.data.Record.COMMIT = 'commit';
8730
8731 Roo.data.Record.prototype = {
8732     /**
8733      * Readonly flag - true if this record has been modified.
8734      * @type Boolean
8735      */
8736     dirty : false,
8737     editing : false,
8738     error: null,
8739     modified: null,
8740
8741     // private
8742     join : function(store){
8743         this.store = store;
8744     },
8745
8746     /**
8747      * Set the named field to the specified value.
8748      * @param {String} name The name of the field to set.
8749      * @param {Object} value The value to set the field to.
8750      */
8751     set : function(name, value){
8752         if(this.data[name] == value){
8753             return;
8754         }
8755         this.dirty = true;
8756         if(!this.modified){
8757             this.modified = {};
8758         }
8759         if(typeof this.modified[name] == 'undefined'){
8760             this.modified[name] = this.data[name];
8761         }
8762         this.data[name] = value;
8763         if(!this.editing && this.store){
8764             this.store.afterEdit(this);
8765         }       
8766     },
8767
8768     /**
8769      * Get the value of the named field.
8770      * @param {String} name The name of the field to get the value of.
8771      * @return {Object} The value of the field.
8772      */
8773     get : function(name){
8774         return this.data[name]; 
8775     },
8776
8777     // private
8778     beginEdit : function(){
8779         this.editing = true;
8780         this.modified = {}; 
8781     },
8782
8783     // private
8784     cancelEdit : function(){
8785         this.editing = false;
8786         delete this.modified;
8787     },
8788
8789     // private
8790     endEdit : function(){
8791         this.editing = false;
8792         if(this.dirty && this.store){
8793             this.store.afterEdit(this);
8794         }
8795     },
8796
8797     /**
8798      * Usually called by the {@link Roo.data.Store} which owns the Record.
8799      * Rejects all changes made to the Record since either creation, or the last commit operation.
8800      * Modified fields are reverted to their original values.
8801      * <p>
8802      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8803      * of reject operations.
8804      */
8805     reject : function(){
8806         var m = this.modified;
8807         for(var n in m){
8808             if(typeof m[n] != "function"){
8809                 this.data[n] = m[n];
8810             }
8811         }
8812         this.dirty = false;
8813         delete this.modified;
8814         this.editing = false;
8815         if(this.store){
8816             this.store.afterReject(this);
8817         }
8818     },
8819
8820     /**
8821      * Usually called by the {@link Roo.data.Store} which owns the Record.
8822      * Commits all changes made to the Record since either creation, or the last commit operation.
8823      * <p>
8824      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8825      * of commit operations.
8826      */
8827     commit : function(){
8828         this.dirty = false;
8829         delete this.modified;
8830         this.editing = false;
8831         if(this.store){
8832             this.store.afterCommit(this);
8833         }
8834     },
8835
8836     // private
8837     hasError : function(){
8838         return this.error != null;
8839     },
8840
8841     // private
8842     clearError : function(){
8843         this.error = null;
8844     },
8845
8846     /**
8847      * Creates a copy of this record.
8848      * @param {String} id (optional) A new record id if you don't want to use this record's id
8849      * @return {Record}
8850      */
8851     copy : function(newId) {
8852         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8853     }
8854 };/*
8855  * Based on:
8856  * Ext JS Library 1.1.1
8857  * Copyright(c) 2006-2007, Ext JS, LLC.
8858  *
8859  * Originally Released Under LGPL - original licence link has changed is not relivant.
8860  *
8861  * Fork - LGPL
8862  * <script type="text/javascript">
8863  */
8864
8865
8866
8867 /**
8868  * @class Roo.data.Store
8869  * @extends Roo.util.Observable
8870  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8871  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8872  * <p>
8873  * 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
8874  * has no knowledge of the format of the data returned by the Proxy.<br>
8875  * <p>
8876  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8877  * instances from the data object. These records are cached and made available through accessor functions.
8878  * @constructor
8879  * Creates a new Store.
8880  * @param {Object} config A config object containing the objects needed for the Store to access data,
8881  * and read the data into Records.
8882  */
8883 Roo.data.Store = function(config){
8884     this.data = new Roo.util.MixedCollection(false);
8885     this.data.getKey = function(o){
8886         return o.id;
8887     };
8888     this.baseParams = {};
8889     // private
8890     this.paramNames = {
8891         "start" : "start",
8892         "limit" : "limit",
8893         "sort" : "sort",
8894         "dir" : "dir",
8895         "multisort" : "_multisort"
8896     };
8897
8898     if(config && config.data){
8899         this.inlineData = config.data;
8900         delete config.data;
8901     }
8902
8903     Roo.apply(this, config);
8904     
8905     if(this.reader){ // reader passed
8906         this.reader = Roo.factory(this.reader, Roo.data);
8907         this.reader.xmodule = this.xmodule || false;
8908         if(!this.recordType){
8909             this.recordType = this.reader.recordType;
8910         }
8911         if(this.reader.onMetaChange){
8912             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8913         }
8914     }
8915
8916     if(this.recordType){
8917         this.fields = this.recordType.prototype.fields;
8918     }
8919     this.modified = [];
8920
8921     this.addEvents({
8922         /**
8923          * @event datachanged
8924          * Fires when the data cache has changed, and a widget which is using this Store
8925          * as a Record cache should refresh its view.
8926          * @param {Store} this
8927          */
8928         datachanged : true,
8929         /**
8930          * @event metachange
8931          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8932          * @param {Store} this
8933          * @param {Object} meta The JSON metadata
8934          */
8935         metachange : true,
8936         /**
8937          * @event add
8938          * Fires when Records have been added to the Store
8939          * @param {Store} this
8940          * @param {Roo.data.Record[]} records The array of Records added
8941          * @param {Number} index The index at which the record(s) were added
8942          */
8943         add : true,
8944         /**
8945          * @event remove
8946          * Fires when a Record has been removed from the Store
8947          * @param {Store} this
8948          * @param {Roo.data.Record} record The Record that was removed
8949          * @param {Number} index The index at which the record was removed
8950          */
8951         remove : true,
8952         /**
8953          * @event update
8954          * Fires when a Record has been updated
8955          * @param {Store} this
8956          * @param {Roo.data.Record} record The Record that was updated
8957          * @param {String} operation The update operation being performed.  Value may be one of:
8958          * <pre><code>
8959  Roo.data.Record.EDIT
8960  Roo.data.Record.REJECT
8961  Roo.data.Record.COMMIT
8962          * </code></pre>
8963          */
8964         update : true,
8965         /**
8966          * @event clear
8967          * Fires when the data cache has been cleared.
8968          * @param {Store} this
8969          */
8970         clear : true,
8971         /**
8972          * @event beforeload
8973          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8974          * the load action will be canceled.
8975          * @param {Store} this
8976          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8977          */
8978         beforeload : true,
8979         /**
8980          * @event beforeloadadd
8981          * Fires after a new set of Records has been loaded.
8982          * @param {Store} this
8983          * @param {Roo.data.Record[]} records The Records that were loaded
8984          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8985          */
8986         beforeloadadd : true,
8987         /**
8988          * @event load
8989          * Fires after a new set of Records has been loaded, before they are added to the store.
8990          * @param {Store} this
8991          * @param {Roo.data.Record[]} records The Records that were loaded
8992          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8993          * @params {Object} return from reader
8994          */
8995         load : true,
8996         /**
8997          * @event loadexception
8998          * Fires if an exception occurs in the Proxy during loading.
8999          * Called with the signature of the Proxy's "loadexception" event.
9000          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9001          * 
9002          * @param {Proxy} 
9003          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9004          * @param {Object} load options 
9005          * @param {Object} jsonData from your request (normally this contains the Exception)
9006          */
9007         loadexception : true
9008     });
9009     
9010     if(this.proxy){
9011         this.proxy = Roo.factory(this.proxy, Roo.data);
9012         this.proxy.xmodule = this.xmodule || false;
9013         this.relayEvents(this.proxy,  ["loadexception"]);
9014     }
9015     this.sortToggle = {};
9016     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9017
9018     Roo.data.Store.superclass.constructor.call(this);
9019
9020     if(this.inlineData){
9021         this.loadData(this.inlineData);
9022         delete this.inlineData;
9023     }
9024 };
9025
9026 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9027      /**
9028     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9029     * without a remote query - used by combo/forms at present.
9030     */
9031     
9032     /**
9033     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9034     */
9035     /**
9036     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9037     */
9038     /**
9039     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9040     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9041     */
9042     /**
9043     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9044     * on any HTTP request
9045     */
9046     /**
9047     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9048     */
9049     /**
9050     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9051     */
9052     multiSort: false,
9053     /**
9054     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9055     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9056     */
9057     remoteSort : false,
9058
9059     /**
9060     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9061      * loaded or when a record is removed. (defaults to false).
9062     */
9063     pruneModifiedRecords : false,
9064
9065     // private
9066     lastOptions : null,
9067
9068     /**
9069      * Add Records to the Store and fires the add event.
9070      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9071      */
9072     add : function(records){
9073         records = [].concat(records);
9074         for(var i = 0, len = records.length; i < len; i++){
9075             records[i].join(this);
9076         }
9077         var index = this.data.length;
9078         this.data.addAll(records);
9079         this.fireEvent("add", this, records, index);
9080     },
9081
9082     /**
9083      * Remove a Record from the Store and fires the remove event.
9084      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9085      */
9086     remove : function(record){
9087         var index = this.data.indexOf(record);
9088         this.data.removeAt(index);
9089         if(this.pruneModifiedRecords){
9090             this.modified.remove(record);
9091         }
9092         this.fireEvent("remove", this, record, index);
9093     },
9094
9095     /**
9096      * Remove all Records from the Store and fires the clear event.
9097      */
9098     removeAll : function(){
9099         this.data.clear();
9100         if(this.pruneModifiedRecords){
9101             this.modified = [];
9102         }
9103         this.fireEvent("clear", this);
9104     },
9105
9106     /**
9107      * Inserts Records to the Store at the given index and fires the add event.
9108      * @param {Number} index The start index at which to insert the passed Records.
9109      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9110      */
9111     insert : function(index, records){
9112         records = [].concat(records);
9113         for(var i = 0, len = records.length; i < len; i++){
9114             this.data.insert(index, records[i]);
9115             records[i].join(this);
9116         }
9117         this.fireEvent("add", this, records, index);
9118     },
9119
9120     /**
9121      * Get the index within the cache of the passed Record.
9122      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9123      * @return {Number} The index of the passed Record. Returns -1 if not found.
9124      */
9125     indexOf : function(record){
9126         return this.data.indexOf(record);
9127     },
9128
9129     /**
9130      * Get the index within the cache of the Record with the passed id.
9131      * @param {String} id The id of the Record to find.
9132      * @return {Number} The index of the Record. Returns -1 if not found.
9133      */
9134     indexOfId : function(id){
9135         return this.data.indexOfKey(id);
9136     },
9137
9138     /**
9139      * Get the Record with the specified id.
9140      * @param {String} id The id of the Record to find.
9141      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9142      */
9143     getById : function(id){
9144         return this.data.key(id);
9145     },
9146
9147     /**
9148      * Get the Record at the specified index.
9149      * @param {Number} index The index of the Record to find.
9150      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9151      */
9152     getAt : function(index){
9153         return this.data.itemAt(index);
9154     },
9155
9156     /**
9157      * Returns a range of Records between specified indices.
9158      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9159      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9160      * @return {Roo.data.Record[]} An array of Records
9161      */
9162     getRange : function(start, end){
9163         return this.data.getRange(start, end);
9164     },
9165
9166     // private
9167     storeOptions : function(o){
9168         o = Roo.apply({}, o);
9169         delete o.callback;
9170         delete o.scope;
9171         this.lastOptions = o;
9172     },
9173
9174     /**
9175      * Loads the Record cache from the configured Proxy using the configured Reader.
9176      * <p>
9177      * If using remote paging, then the first load call must specify the <em>start</em>
9178      * and <em>limit</em> properties in the options.params property to establish the initial
9179      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9180      * <p>
9181      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9182      * and this call will return before the new data has been loaded. Perform any post-processing
9183      * in a callback function, or in a "load" event handler.</strong>
9184      * <p>
9185      * @param {Object} options An object containing properties which control loading options:<ul>
9186      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9187      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9188      * passed the following arguments:<ul>
9189      * <li>r : Roo.data.Record[]</li>
9190      * <li>options: Options object from the load call</li>
9191      * <li>success: Boolean success indicator</li></ul></li>
9192      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9193      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9194      * </ul>
9195      */
9196     load : function(options){
9197         options = options || {};
9198         if(this.fireEvent("beforeload", this, options) !== false){
9199             this.storeOptions(options);
9200             var p = Roo.apply(options.params || {}, this.baseParams);
9201             // if meta was not loaded from remote source.. try requesting it.
9202             if (!this.reader.metaFromRemote) {
9203                 p._requestMeta = 1;
9204             }
9205             if(this.sortInfo && this.remoteSort){
9206                 var pn = this.paramNames;
9207                 p[pn["sort"]] = this.sortInfo.field;
9208                 p[pn["dir"]] = this.sortInfo.direction;
9209             }
9210             if (this.multiSort) {
9211                 var pn = this.paramNames;
9212                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9213             }
9214             
9215             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9216         }
9217     },
9218
9219     /**
9220      * Reloads the Record cache from the configured Proxy using the configured Reader and
9221      * the options from the last load operation performed.
9222      * @param {Object} options (optional) An object containing properties which may override the options
9223      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9224      * the most recently used options are reused).
9225      */
9226     reload : function(options){
9227         this.load(Roo.applyIf(options||{}, this.lastOptions));
9228     },
9229
9230     // private
9231     // Called as a callback by the Reader during a load operation.
9232     loadRecords : function(o, options, success){
9233         if(!o || success === false){
9234             if(success !== false){
9235                 this.fireEvent("load", this, [], options, o);
9236             }
9237             if(options.callback){
9238                 options.callback.call(options.scope || this, [], options, false);
9239             }
9240             return;
9241         }
9242         // if data returned failure - throw an exception.
9243         if (o.success === false) {
9244             // show a message if no listener is registered.
9245             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9246                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9247             }
9248             // loadmask wil be hooked into this..
9249             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9250             return;
9251         }
9252         var r = o.records, t = o.totalRecords || r.length;
9253         
9254         this.fireEvent("beforeloadadd", this, r, options, o);
9255         
9256         if(!options || options.add !== true){
9257             if(this.pruneModifiedRecords){
9258                 this.modified = [];
9259             }
9260             for(var i = 0, len = r.length; i < len; i++){
9261                 r[i].join(this);
9262             }
9263             if(this.snapshot){
9264                 this.data = this.snapshot;
9265                 delete this.snapshot;
9266             }
9267             this.data.clear();
9268             this.data.addAll(r);
9269             this.totalLength = t;
9270             this.applySort();
9271             this.fireEvent("datachanged", this);
9272         }else{
9273             this.totalLength = Math.max(t, this.data.length+r.length);
9274             this.add(r);
9275         }
9276         this.fireEvent("load", this, r, options, o);
9277         if(options.callback){
9278             options.callback.call(options.scope || this, r, options, true);
9279         }
9280     },
9281
9282
9283     /**
9284      * Loads data from a passed data block. A Reader which understands the format of the data
9285      * must have been configured in the constructor.
9286      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9287      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9288      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9289      */
9290     loadData : function(o, append){
9291         var r = this.reader.readRecords(o);
9292         this.loadRecords(r, {add: append}, true);
9293     },
9294
9295     /**
9296      * Gets the number of cached records.
9297      * <p>
9298      * <em>If using paging, this may not be the total size of the dataset. If the data object
9299      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9300      * the data set size</em>
9301      */
9302     getCount : function(){
9303         return this.data.length || 0;
9304     },
9305
9306     /**
9307      * Gets the total number of records in the dataset as returned by the server.
9308      * <p>
9309      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9310      * the dataset size</em>
9311      */
9312     getTotalCount : function(){
9313         return this.totalLength || 0;
9314     },
9315
9316     /**
9317      * Returns the sort state of the Store as an object with two properties:
9318      * <pre><code>
9319  field {String} The name of the field by which the Records are sorted
9320  direction {String} The sort order, "ASC" or "DESC"
9321      * </code></pre>
9322      */
9323     getSortState : function(){
9324         return this.sortInfo;
9325     },
9326
9327     // private
9328     applySort : function(){
9329         if(this.sortInfo && !this.remoteSort){
9330             var s = this.sortInfo, f = s.field;
9331             var st = this.fields.get(f).sortType;
9332             var fn = function(r1, r2){
9333                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9334                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9335             };
9336             this.data.sort(s.direction, fn);
9337             if(this.snapshot && this.snapshot != this.data){
9338                 this.snapshot.sort(s.direction, fn);
9339             }
9340         }
9341     },
9342
9343     /**
9344      * Sets the default sort column and order to be used by the next load operation.
9345      * @param {String} fieldName The name of the field to sort by.
9346      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9347      */
9348     setDefaultSort : function(field, dir){
9349         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9350     },
9351
9352     /**
9353      * Sort the Records.
9354      * If remote sorting is used, the sort is performed on the server, and the cache is
9355      * reloaded. If local sorting is used, the cache is sorted internally.
9356      * @param {String} fieldName The name of the field to sort by.
9357      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9358      */
9359     sort : function(fieldName, dir){
9360         var f = this.fields.get(fieldName);
9361         if(!dir){
9362             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9363             
9364             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9365                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9366             }else{
9367                 dir = f.sortDir;
9368             }
9369         }
9370         this.sortToggle[f.name] = dir;
9371         this.sortInfo = {field: f.name, direction: dir};
9372         if(!this.remoteSort){
9373             this.applySort();
9374             this.fireEvent("datachanged", this);
9375         }else{
9376             this.load(this.lastOptions);
9377         }
9378     },
9379
9380     /**
9381      * Calls the specified function for each of the Records in the cache.
9382      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9383      * Returning <em>false</em> aborts and exits the iteration.
9384      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9385      */
9386     each : function(fn, scope){
9387         this.data.each(fn, scope);
9388     },
9389
9390     /**
9391      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9392      * (e.g., during paging).
9393      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9394      */
9395     getModifiedRecords : function(){
9396         return this.modified;
9397     },
9398
9399     // private
9400     createFilterFn : function(property, value, anyMatch){
9401         if(!value.exec){ // not a regex
9402             value = String(value);
9403             if(value.length == 0){
9404                 return false;
9405             }
9406             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9407         }
9408         return function(r){
9409             return value.test(r.data[property]);
9410         };
9411     },
9412
9413     /**
9414      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9415      * @param {String} property A field on your records
9416      * @param {Number} start The record index to start at (defaults to 0)
9417      * @param {Number} end The last record index to include (defaults to length - 1)
9418      * @return {Number} The sum
9419      */
9420     sum : function(property, start, end){
9421         var rs = this.data.items, v = 0;
9422         start = start || 0;
9423         end = (end || end === 0) ? end : rs.length-1;
9424
9425         for(var i = start; i <= end; i++){
9426             v += (rs[i].data[property] || 0);
9427         }
9428         return v;
9429     },
9430
9431     /**
9432      * Filter the records by a specified property.
9433      * @param {String} field A field on your records
9434      * @param {String/RegExp} value Either a string that the field
9435      * should start with or a RegExp to test against the field
9436      * @param {Boolean} anyMatch True to match any part not just the beginning
9437      */
9438     filter : function(property, value, anyMatch){
9439         var fn = this.createFilterFn(property, value, anyMatch);
9440         return fn ? this.filterBy(fn) : this.clearFilter();
9441     },
9442
9443     /**
9444      * Filter by a function. The specified function will be called with each
9445      * record in this data source. If the function returns true the record is included,
9446      * otherwise it is filtered.
9447      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9448      * @param {Object} scope (optional) The scope of the function (defaults to this)
9449      */
9450     filterBy : function(fn, scope){
9451         this.snapshot = this.snapshot || this.data;
9452         this.data = this.queryBy(fn, scope||this);
9453         this.fireEvent("datachanged", this);
9454     },
9455
9456     /**
9457      * Query the records by a specified property.
9458      * @param {String} field A field on your records
9459      * @param {String/RegExp} value Either a string that the field
9460      * should start with or a RegExp to test against the field
9461      * @param {Boolean} anyMatch True to match any part not just the beginning
9462      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9463      */
9464     query : function(property, value, anyMatch){
9465         var fn = this.createFilterFn(property, value, anyMatch);
9466         return fn ? this.queryBy(fn) : this.data.clone();
9467     },
9468
9469     /**
9470      * Query by a function. The specified function will be called with each
9471      * record in this data source. If the function returns true the record is included
9472      * in the results.
9473      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9474      * @param {Object} scope (optional) The scope of the function (defaults to this)
9475       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9476      **/
9477     queryBy : function(fn, scope){
9478         var data = this.snapshot || this.data;
9479         return data.filterBy(fn, scope||this);
9480     },
9481
9482     /**
9483      * Collects unique values for a particular dataIndex from this store.
9484      * @param {String} dataIndex The property to collect
9485      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9486      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9487      * @return {Array} An array of the unique values
9488      **/
9489     collect : function(dataIndex, allowNull, bypassFilter){
9490         var d = (bypassFilter === true && this.snapshot) ?
9491                 this.snapshot.items : this.data.items;
9492         var v, sv, r = [], l = {};
9493         for(var i = 0, len = d.length; i < len; i++){
9494             v = d[i].data[dataIndex];
9495             sv = String(v);
9496             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9497                 l[sv] = true;
9498                 r[r.length] = v;
9499             }
9500         }
9501         return r;
9502     },
9503
9504     /**
9505      * Revert to a view of the Record cache with no filtering applied.
9506      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9507      */
9508     clearFilter : function(suppressEvent){
9509         if(this.snapshot && this.snapshot != this.data){
9510             this.data = this.snapshot;
9511             delete this.snapshot;
9512             if(suppressEvent !== true){
9513                 this.fireEvent("datachanged", this);
9514             }
9515         }
9516     },
9517
9518     // private
9519     afterEdit : function(record){
9520         if(this.modified.indexOf(record) == -1){
9521             this.modified.push(record);
9522         }
9523         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9524     },
9525     
9526     // private
9527     afterReject : function(record){
9528         this.modified.remove(record);
9529         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9530     },
9531
9532     // private
9533     afterCommit : function(record){
9534         this.modified.remove(record);
9535         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9536     },
9537
9538     /**
9539      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9540      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9541      */
9542     commitChanges : function(){
9543         var m = this.modified.slice(0);
9544         this.modified = [];
9545         for(var i = 0, len = m.length; i < len; i++){
9546             m[i].commit();
9547         }
9548     },
9549
9550     /**
9551      * Cancel outstanding changes on all changed records.
9552      */
9553     rejectChanges : function(){
9554         var m = this.modified.slice(0);
9555         this.modified = [];
9556         for(var i = 0, len = m.length; i < len; i++){
9557             m[i].reject();
9558         }
9559     },
9560
9561     onMetaChange : function(meta, rtype, o){
9562         this.recordType = rtype;
9563         this.fields = rtype.prototype.fields;
9564         delete this.snapshot;
9565         this.sortInfo = meta.sortInfo || this.sortInfo;
9566         this.modified = [];
9567         this.fireEvent('metachange', this, this.reader.meta);
9568     },
9569     
9570     moveIndex : function(data, type)
9571     {
9572         var index = this.indexOf(data);
9573         
9574         var newIndex = index + type;
9575         
9576         this.remove(data);
9577         
9578         this.insert(newIndex, data);
9579         
9580     }
9581 });/*
9582  * Based on:
9583  * Ext JS Library 1.1.1
9584  * Copyright(c) 2006-2007, Ext JS, LLC.
9585  *
9586  * Originally Released Under LGPL - original licence link has changed is not relivant.
9587  *
9588  * Fork - LGPL
9589  * <script type="text/javascript">
9590  */
9591
9592 /**
9593  * @class Roo.data.SimpleStore
9594  * @extends Roo.data.Store
9595  * Small helper class to make creating Stores from Array data easier.
9596  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9597  * @cfg {Array} fields An array of field definition objects, or field name strings.
9598  * @cfg {Array} data The multi-dimensional array of data
9599  * @constructor
9600  * @param {Object} config
9601  */
9602 Roo.data.SimpleStore = function(config){
9603     Roo.data.SimpleStore.superclass.constructor.call(this, {
9604         isLocal : true,
9605         reader: new Roo.data.ArrayReader({
9606                 id: config.id
9607             },
9608             Roo.data.Record.create(config.fields)
9609         ),
9610         proxy : new Roo.data.MemoryProxy(config.data)
9611     });
9612     this.load();
9613 };
9614 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9615  * Based on:
9616  * Ext JS Library 1.1.1
9617  * Copyright(c) 2006-2007, Ext JS, LLC.
9618  *
9619  * Originally Released Under LGPL - original licence link has changed is not relivant.
9620  *
9621  * Fork - LGPL
9622  * <script type="text/javascript">
9623  */
9624
9625 /**
9626 /**
9627  * @extends Roo.data.Store
9628  * @class Roo.data.JsonStore
9629  * Small helper class to make creating Stores for JSON data easier. <br/>
9630 <pre><code>
9631 var store = new Roo.data.JsonStore({
9632     url: 'get-images.php',
9633     root: 'images',
9634     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9635 });
9636 </code></pre>
9637  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9638  * JsonReader and HttpProxy (unless inline data is provided).</b>
9639  * @cfg {Array} fields An array of field definition objects, or field name strings.
9640  * @constructor
9641  * @param {Object} config
9642  */
9643 Roo.data.JsonStore = function(c){
9644     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9645         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9646         reader: new Roo.data.JsonReader(c, c.fields)
9647     }));
9648 };
9649 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9650  * Based on:
9651  * Ext JS Library 1.1.1
9652  * Copyright(c) 2006-2007, Ext JS, LLC.
9653  *
9654  * Originally Released Under LGPL - original licence link has changed is not relivant.
9655  *
9656  * Fork - LGPL
9657  * <script type="text/javascript">
9658  */
9659
9660  
9661 Roo.data.Field = function(config){
9662     if(typeof config == "string"){
9663         config = {name: config};
9664     }
9665     Roo.apply(this, config);
9666     
9667     if(!this.type){
9668         this.type = "auto";
9669     }
9670     
9671     var st = Roo.data.SortTypes;
9672     // named sortTypes are supported, here we look them up
9673     if(typeof this.sortType == "string"){
9674         this.sortType = st[this.sortType];
9675     }
9676     
9677     // set default sortType for strings and dates
9678     if(!this.sortType){
9679         switch(this.type){
9680             case "string":
9681                 this.sortType = st.asUCString;
9682                 break;
9683             case "date":
9684                 this.sortType = st.asDate;
9685                 break;
9686             default:
9687                 this.sortType = st.none;
9688         }
9689     }
9690
9691     // define once
9692     var stripRe = /[\$,%]/g;
9693
9694     // prebuilt conversion function for this field, instead of
9695     // switching every time we're reading a value
9696     if(!this.convert){
9697         var cv, dateFormat = this.dateFormat;
9698         switch(this.type){
9699             case "":
9700             case "auto":
9701             case undefined:
9702                 cv = function(v){ return v; };
9703                 break;
9704             case "string":
9705                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9706                 break;
9707             case "int":
9708                 cv = function(v){
9709                     return v !== undefined && v !== null && v !== '' ?
9710                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9711                     };
9712                 break;
9713             case "float":
9714                 cv = function(v){
9715                     return v !== undefined && v !== null && v !== '' ?
9716                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9717                     };
9718                 break;
9719             case "bool":
9720             case "boolean":
9721                 cv = function(v){ return v === true || v === "true" || v == 1; };
9722                 break;
9723             case "date":
9724                 cv = function(v){
9725                     if(!v){
9726                         return '';
9727                     }
9728                     if(v instanceof Date){
9729                         return v;
9730                     }
9731                     if(dateFormat){
9732                         if(dateFormat == "timestamp"){
9733                             return new Date(v*1000);
9734                         }
9735                         return Date.parseDate(v, dateFormat);
9736                     }
9737                     var parsed = Date.parse(v);
9738                     return parsed ? new Date(parsed) : null;
9739                 };
9740              break;
9741             
9742         }
9743         this.convert = cv;
9744     }
9745 };
9746
9747 Roo.data.Field.prototype = {
9748     dateFormat: null,
9749     defaultValue: "",
9750     mapping: null,
9751     sortType : null,
9752     sortDir : "ASC"
9753 };/*
9754  * Based on:
9755  * Ext JS Library 1.1.1
9756  * Copyright(c) 2006-2007, Ext JS, LLC.
9757  *
9758  * Originally Released Under LGPL - original licence link has changed is not relivant.
9759  *
9760  * Fork - LGPL
9761  * <script type="text/javascript">
9762  */
9763  
9764 // Base class for reading structured data from a data source.  This class is intended to be
9765 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9766
9767 /**
9768  * @class Roo.data.DataReader
9769  * Base class for reading structured data from a data source.  This class is intended to be
9770  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9771  */
9772
9773 Roo.data.DataReader = function(meta, recordType){
9774     
9775     this.meta = meta;
9776     
9777     this.recordType = recordType instanceof Array ? 
9778         Roo.data.Record.create(recordType) : recordType;
9779 };
9780
9781 Roo.data.DataReader.prototype = {
9782      /**
9783      * Create an empty record
9784      * @param {Object} data (optional) - overlay some values
9785      * @return {Roo.data.Record} record created.
9786      */
9787     newRow :  function(d) {
9788         var da =  {};
9789         this.recordType.prototype.fields.each(function(c) {
9790             switch( c.type) {
9791                 case 'int' : da[c.name] = 0; break;
9792                 case 'date' : da[c.name] = new Date(); break;
9793                 case 'float' : da[c.name] = 0.0; break;
9794                 case 'boolean' : da[c.name] = false; break;
9795                 default : da[c.name] = ""; break;
9796             }
9797             
9798         });
9799         return new this.recordType(Roo.apply(da, d));
9800     }
9801     
9802 };/*
9803  * Based on:
9804  * Ext JS Library 1.1.1
9805  * Copyright(c) 2006-2007, Ext JS, LLC.
9806  *
9807  * Originally Released Under LGPL - original licence link has changed is not relivant.
9808  *
9809  * Fork - LGPL
9810  * <script type="text/javascript">
9811  */
9812
9813 /**
9814  * @class Roo.data.DataProxy
9815  * @extends Roo.data.Observable
9816  * This class is an abstract base class for implementations which provide retrieval of
9817  * unformatted data objects.<br>
9818  * <p>
9819  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9820  * (of the appropriate type which knows how to parse the data object) to provide a block of
9821  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9822  * <p>
9823  * Custom implementations must implement the load method as described in
9824  * {@link Roo.data.HttpProxy#load}.
9825  */
9826 Roo.data.DataProxy = function(){
9827     this.addEvents({
9828         /**
9829          * @event beforeload
9830          * Fires before a network request is made to retrieve a data object.
9831          * @param {Object} This DataProxy object.
9832          * @param {Object} params The params parameter to the load function.
9833          */
9834         beforeload : true,
9835         /**
9836          * @event load
9837          * Fires before the load method's callback is called.
9838          * @param {Object} This DataProxy object.
9839          * @param {Object} o The data object.
9840          * @param {Object} arg The callback argument object passed to the load function.
9841          */
9842         load : true,
9843         /**
9844          * @event loadexception
9845          * Fires if an Exception occurs during data retrieval.
9846          * @param {Object} This DataProxy object.
9847          * @param {Object} o The data object.
9848          * @param {Object} arg The callback argument object passed to the load function.
9849          * @param {Object} e The Exception.
9850          */
9851         loadexception : true
9852     });
9853     Roo.data.DataProxy.superclass.constructor.call(this);
9854 };
9855
9856 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9857
9858     /**
9859      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9860      */
9861 /*
9862  * Based on:
9863  * Ext JS Library 1.1.1
9864  * Copyright(c) 2006-2007, Ext JS, LLC.
9865  *
9866  * Originally Released Under LGPL - original licence link has changed is not relivant.
9867  *
9868  * Fork - LGPL
9869  * <script type="text/javascript">
9870  */
9871 /**
9872  * @class Roo.data.MemoryProxy
9873  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9874  * to the Reader when its load method is called.
9875  * @constructor
9876  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9877  */
9878 Roo.data.MemoryProxy = function(data){
9879     if (data.data) {
9880         data = data.data;
9881     }
9882     Roo.data.MemoryProxy.superclass.constructor.call(this);
9883     this.data = data;
9884 };
9885
9886 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9887     /**
9888      * Load data from the requested source (in this case an in-memory
9889      * data object passed to the constructor), read the data object into
9890      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9891      * process that block using the passed callback.
9892      * @param {Object} params This parameter is not used by the MemoryProxy class.
9893      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9894      * object into a block of Roo.data.Records.
9895      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9896      * The function must be passed <ul>
9897      * <li>The Record block object</li>
9898      * <li>The "arg" argument from the load function</li>
9899      * <li>A boolean success indicator</li>
9900      * </ul>
9901      * @param {Object} scope The scope in which to call the callback
9902      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9903      */
9904     load : function(params, reader, callback, scope, arg){
9905         params = params || {};
9906         var result;
9907         try {
9908             result = reader.readRecords(this.data);
9909         }catch(e){
9910             this.fireEvent("loadexception", this, arg, null, e);
9911             callback.call(scope, null, arg, false);
9912             return;
9913         }
9914         callback.call(scope, result, arg, true);
9915     },
9916     
9917     // private
9918     update : function(params, records){
9919         
9920     }
9921 });/*
9922  * Based on:
9923  * Ext JS Library 1.1.1
9924  * Copyright(c) 2006-2007, Ext JS, LLC.
9925  *
9926  * Originally Released Under LGPL - original licence link has changed is not relivant.
9927  *
9928  * Fork - LGPL
9929  * <script type="text/javascript">
9930  */
9931 /**
9932  * @class Roo.data.HttpProxy
9933  * @extends Roo.data.DataProxy
9934  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9935  * configured to reference a certain URL.<br><br>
9936  * <p>
9937  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9938  * from which the running page was served.<br><br>
9939  * <p>
9940  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9941  * <p>
9942  * Be aware that to enable the browser to parse an XML document, the server must set
9943  * the Content-Type header in the HTTP response to "text/xml".
9944  * @constructor
9945  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9946  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9947  * will be used to make the request.
9948  */
9949 Roo.data.HttpProxy = function(conn){
9950     Roo.data.HttpProxy.superclass.constructor.call(this);
9951     // is conn a conn config or a real conn?
9952     this.conn = conn;
9953     this.useAjax = !conn || !conn.events;
9954   
9955 };
9956
9957 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9958     // thse are take from connection...
9959     
9960     /**
9961      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9962      */
9963     /**
9964      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9965      * extra parameters to each request made by this object. (defaults to undefined)
9966      */
9967     /**
9968      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9969      *  to each request made by this object. (defaults to undefined)
9970      */
9971     /**
9972      * @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)
9973      */
9974     /**
9975      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9976      */
9977      /**
9978      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9979      * @type Boolean
9980      */
9981   
9982
9983     /**
9984      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9985      * @type Boolean
9986      */
9987     /**
9988      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9989      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9990      * a finer-grained basis than the DataProxy events.
9991      */
9992     getConnection : function(){
9993         return this.useAjax ? Roo.Ajax : this.conn;
9994     },
9995
9996     /**
9997      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9998      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9999      * process that block using the passed callback.
10000      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10001      * for the request to the remote server.
10002      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10003      * object into a block of Roo.data.Records.
10004      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10005      * The function must be passed <ul>
10006      * <li>The Record block object</li>
10007      * <li>The "arg" argument from the load function</li>
10008      * <li>A boolean success indicator</li>
10009      * </ul>
10010      * @param {Object} scope The scope in which to call the callback
10011      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10012      */
10013     load : function(params, reader, callback, scope, arg){
10014         if(this.fireEvent("beforeload", this, params) !== false){
10015             var  o = {
10016                 params : params || {},
10017                 request: {
10018                     callback : callback,
10019                     scope : scope,
10020                     arg : arg
10021                 },
10022                 reader: reader,
10023                 callback : this.loadResponse,
10024                 scope: this
10025             };
10026             if(this.useAjax){
10027                 Roo.applyIf(o, this.conn);
10028                 if(this.activeRequest){
10029                     Roo.Ajax.abort(this.activeRequest);
10030                 }
10031                 this.activeRequest = Roo.Ajax.request(o);
10032             }else{
10033                 this.conn.request(o);
10034             }
10035         }else{
10036             callback.call(scope||this, null, arg, false);
10037         }
10038     },
10039
10040     // private
10041     loadResponse : function(o, success, response){
10042         delete this.activeRequest;
10043         if(!success){
10044             this.fireEvent("loadexception", this, o, response);
10045             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10046             return;
10047         }
10048         var result;
10049         try {
10050             result = o.reader.read(response);
10051         }catch(e){
10052             this.fireEvent("loadexception", this, o, response, e);
10053             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10054             return;
10055         }
10056         
10057         this.fireEvent("load", this, o, o.request.arg);
10058         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10059     },
10060
10061     // private
10062     update : function(dataSet){
10063
10064     },
10065
10066     // private
10067     updateResponse : function(dataSet){
10068
10069     }
10070 });/*
10071  * Based on:
10072  * Ext JS Library 1.1.1
10073  * Copyright(c) 2006-2007, Ext JS, LLC.
10074  *
10075  * Originally Released Under LGPL - original licence link has changed is not relivant.
10076  *
10077  * Fork - LGPL
10078  * <script type="text/javascript">
10079  */
10080
10081 /**
10082  * @class Roo.data.ScriptTagProxy
10083  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10084  * other than the originating domain of the running page.<br><br>
10085  * <p>
10086  * <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
10087  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10088  * <p>
10089  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10090  * source code that is used as the source inside a &lt;script> tag.<br><br>
10091  * <p>
10092  * In order for the browser to process the returned data, the server must wrap the data object
10093  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10094  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10095  * depending on whether the callback name was passed:
10096  * <p>
10097  * <pre><code>
10098 boolean scriptTag = false;
10099 String cb = request.getParameter("callback");
10100 if (cb != null) {
10101     scriptTag = true;
10102     response.setContentType("text/javascript");
10103 } else {
10104     response.setContentType("application/x-json");
10105 }
10106 Writer out = response.getWriter();
10107 if (scriptTag) {
10108     out.write(cb + "(");
10109 }
10110 out.print(dataBlock.toJsonString());
10111 if (scriptTag) {
10112     out.write(");");
10113 }
10114 </pre></code>
10115  *
10116  * @constructor
10117  * @param {Object} config A configuration object.
10118  */
10119 Roo.data.ScriptTagProxy = function(config){
10120     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10121     Roo.apply(this, config);
10122     this.head = document.getElementsByTagName("head")[0];
10123 };
10124
10125 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10126
10127 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10128     /**
10129      * @cfg {String} url The URL from which to request the data object.
10130      */
10131     /**
10132      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10133      */
10134     timeout : 30000,
10135     /**
10136      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10137      * the server the name of the callback function set up by the load call to process the returned data object.
10138      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10139      * javascript output which calls this named function passing the data object as its only parameter.
10140      */
10141     callbackParam : "callback",
10142     /**
10143      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10144      * name to the request.
10145      */
10146     nocache : true,
10147
10148     /**
10149      * Load data from the configured URL, read the data object into
10150      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10151      * process that block using the passed callback.
10152      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10153      * for the request to the remote server.
10154      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10155      * object into a block of Roo.data.Records.
10156      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10157      * The function must be passed <ul>
10158      * <li>The Record block object</li>
10159      * <li>The "arg" argument from the load function</li>
10160      * <li>A boolean success indicator</li>
10161      * </ul>
10162      * @param {Object} scope The scope in which to call the callback
10163      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10164      */
10165     load : function(params, reader, callback, scope, arg){
10166         if(this.fireEvent("beforeload", this, params) !== false){
10167
10168             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10169
10170             var url = this.url;
10171             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10172             if(this.nocache){
10173                 url += "&_dc=" + (new Date().getTime());
10174             }
10175             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10176             var trans = {
10177                 id : transId,
10178                 cb : "stcCallback"+transId,
10179                 scriptId : "stcScript"+transId,
10180                 params : params,
10181                 arg : arg,
10182                 url : url,
10183                 callback : callback,
10184                 scope : scope,
10185                 reader : reader
10186             };
10187             var conn = this;
10188
10189             window[trans.cb] = function(o){
10190                 conn.handleResponse(o, trans);
10191             };
10192
10193             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10194
10195             if(this.autoAbort !== false){
10196                 this.abort();
10197             }
10198
10199             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10200
10201             var script = document.createElement("script");
10202             script.setAttribute("src", url);
10203             script.setAttribute("type", "text/javascript");
10204             script.setAttribute("id", trans.scriptId);
10205             this.head.appendChild(script);
10206
10207             this.trans = trans;
10208         }else{
10209             callback.call(scope||this, null, arg, false);
10210         }
10211     },
10212
10213     // private
10214     isLoading : function(){
10215         return this.trans ? true : false;
10216     },
10217
10218     /**
10219      * Abort the current server request.
10220      */
10221     abort : function(){
10222         if(this.isLoading()){
10223             this.destroyTrans(this.trans);
10224         }
10225     },
10226
10227     // private
10228     destroyTrans : function(trans, isLoaded){
10229         this.head.removeChild(document.getElementById(trans.scriptId));
10230         clearTimeout(trans.timeoutId);
10231         if(isLoaded){
10232             window[trans.cb] = undefined;
10233             try{
10234                 delete window[trans.cb];
10235             }catch(e){}
10236         }else{
10237             // if hasn't been loaded, wait for load to remove it to prevent script error
10238             window[trans.cb] = function(){
10239                 window[trans.cb] = undefined;
10240                 try{
10241                     delete window[trans.cb];
10242                 }catch(e){}
10243             };
10244         }
10245     },
10246
10247     // private
10248     handleResponse : function(o, trans){
10249         this.trans = false;
10250         this.destroyTrans(trans, true);
10251         var result;
10252         try {
10253             result = trans.reader.readRecords(o);
10254         }catch(e){
10255             this.fireEvent("loadexception", this, o, trans.arg, e);
10256             trans.callback.call(trans.scope||window, null, trans.arg, false);
10257             return;
10258         }
10259         this.fireEvent("load", this, o, trans.arg);
10260         trans.callback.call(trans.scope||window, result, trans.arg, true);
10261     },
10262
10263     // private
10264     handleFailure : function(trans){
10265         this.trans = false;
10266         this.destroyTrans(trans, false);
10267         this.fireEvent("loadexception", this, null, trans.arg);
10268         trans.callback.call(trans.scope||window, null, trans.arg, false);
10269     }
10270 });/*
10271  * Based on:
10272  * Ext JS Library 1.1.1
10273  * Copyright(c) 2006-2007, Ext JS, LLC.
10274  *
10275  * Originally Released Under LGPL - original licence link has changed is not relivant.
10276  *
10277  * Fork - LGPL
10278  * <script type="text/javascript">
10279  */
10280
10281 /**
10282  * @class Roo.data.JsonReader
10283  * @extends Roo.data.DataReader
10284  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10285  * based on mappings in a provided Roo.data.Record constructor.
10286  * 
10287  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10288  * in the reply previously. 
10289  * 
10290  * <p>
10291  * Example code:
10292  * <pre><code>
10293 var RecordDef = Roo.data.Record.create([
10294     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10295     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10296 ]);
10297 var myReader = new Roo.data.JsonReader({
10298     totalProperty: "results",    // The property which contains the total dataset size (optional)
10299     root: "rows",                // The property which contains an Array of row objects
10300     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10301 }, RecordDef);
10302 </code></pre>
10303  * <p>
10304  * This would consume a JSON file like this:
10305  * <pre><code>
10306 { 'results': 2, 'rows': [
10307     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10308     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10309 }
10310 </code></pre>
10311  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10312  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10313  * paged from the remote server.
10314  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10315  * @cfg {String} root name of the property which contains the Array of row objects.
10316  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10317  * @constructor
10318  * Create a new JsonReader
10319  * @param {Object} meta Metadata configuration options
10320  * @param {Object} recordType Either an Array of field definition objects,
10321  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10322  */
10323 Roo.data.JsonReader = function(meta, recordType){
10324     
10325     meta = meta || {};
10326     // set some defaults:
10327     Roo.applyIf(meta, {
10328         totalProperty: 'total',
10329         successProperty : 'success',
10330         root : 'data',
10331         id : 'id'
10332     });
10333     
10334     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10335 };
10336 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10337     
10338     /**
10339      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10340      * Used by Store query builder to append _requestMeta to params.
10341      * 
10342      */
10343     metaFromRemote : false,
10344     /**
10345      * This method is only used by a DataProxy which has retrieved data from a remote server.
10346      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10347      * @return {Object} data A data block which is used by an Roo.data.Store object as
10348      * a cache of Roo.data.Records.
10349      */
10350     read : function(response){
10351         var json = response.responseText;
10352        
10353         var o = /* eval:var:o */ eval("("+json+")");
10354         if(!o) {
10355             throw {message: "JsonReader.read: Json object not found"};
10356         }
10357         
10358         if(o.metaData){
10359             
10360             delete this.ef;
10361             this.metaFromRemote = true;
10362             this.meta = o.metaData;
10363             this.recordType = Roo.data.Record.create(o.metaData.fields);
10364             this.onMetaChange(this.meta, this.recordType, o);
10365         }
10366         return this.readRecords(o);
10367     },
10368
10369     // private function a store will implement
10370     onMetaChange : function(meta, recordType, o){
10371
10372     },
10373
10374     /**
10375          * @ignore
10376          */
10377     simpleAccess: function(obj, subsc) {
10378         return obj[subsc];
10379     },
10380
10381         /**
10382          * @ignore
10383          */
10384     getJsonAccessor: function(){
10385         var re = /[\[\.]/;
10386         return function(expr) {
10387             try {
10388                 return(re.test(expr))
10389                     ? new Function("obj", "return obj." + expr)
10390                     : function(obj){
10391                         return obj[expr];
10392                     };
10393             } catch(e){}
10394             return Roo.emptyFn;
10395         };
10396     }(),
10397
10398     /**
10399      * Create a data block containing Roo.data.Records from an XML document.
10400      * @param {Object} o An object which contains an Array of row objects in the property specified
10401      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10402      * which contains the total size of the dataset.
10403      * @return {Object} data A data block which is used by an Roo.data.Store object as
10404      * a cache of Roo.data.Records.
10405      */
10406     readRecords : function(o){
10407         /**
10408          * After any data loads, the raw JSON data is available for further custom processing.
10409          * @type Object
10410          */
10411         this.o = o;
10412         var s = this.meta, Record = this.recordType,
10413             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10414
10415 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10416         if (!this.ef) {
10417             if(s.totalProperty) {
10418                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10419                 }
10420                 if(s.successProperty) {
10421                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10422                 }
10423                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10424                 if (s.id) {
10425                         var g = this.getJsonAccessor(s.id);
10426                         this.getId = function(rec) {
10427                                 var r = g(rec);  
10428                                 return (r === undefined || r === "") ? null : r;
10429                         };
10430                 } else {
10431                         this.getId = function(){return null;};
10432                 }
10433             this.ef = [];
10434             for(var jj = 0; jj < fl; jj++){
10435                 f = fi[jj];
10436                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10437                 this.ef[jj] = this.getJsonAccessor(map);
10438             }
10439         }
10440
10441         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10442         if(s.totalProperty){
10443             var vt = parseInt(this.getTotal(o), 10);
10444             if(!isNaN(vt)){
10445                 totalRecords = vt;
10446             }
10447         }
10448         if(s.successProperty){
10449             var vs = this.getSuccess(o);
10450             if(vs === false || vs === 'false'){
10451                 success = false;
10452             }
10453         }
10454         var records = [];
10455         for(var i = 0; i < c; i++){
10456                 var n = root[i];
10457             var values = {};
10458             var id = this.getId(n);
10459             for(var j = 0; j < fl; j++){
10460                 f = fi[j];
10461             var v = this.ef[j](n);
10462             if (!f.convert) {
10463                 Roo.log('missing convert for ' + f.name);
10464                 Roo.log(f);
10465                 continue;
10466             }
10467             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10468             }
10469             var record = new Record(values, id);
10470             record.json = n;
10471             records[i] = record;
10472         }
10473         return {
10474             raw : o,
10475             success : success,
10476             records : records,
10477             totalRecords : totalRecords
10478         };
10479     }
10480 });/*
10481  * Based on:
10482  * Ext JS Library 1.1.1
10483  * Copyright(c) 2006-2007, Ext JS, LLC.
10484  *
10485  * Originally Released Under LGPL - original licence link has changed is not relivant.
10486  *
10487  * Fork - LGPL
10488  * <script type="text/javascript">
10489  */
10490
10491 /**
10492  * @class Roo.data.ArrayReader
10493  * @extends Roo.data.DataReader
10494  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10495  * Each element of that Array represents a row of data fields. The
10496  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10497  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10498  * <p>
10499  * Example code:.
10500  * <pre><code>
10501 var RecordDef = Roo.data.Record.create([
10502     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10503     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10504 ]);
10505 var myReader = new Roo.data.ArrayReader({
10506     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10507 }, RecordDef);
10508 </code></pre>
10509  * <p>
10510  * This would consume an Array like this:
10511  * <pre><code>
10512 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10513   </code></pre>
10514  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10515  * @constructor
10516  * Create a new JsonReader
10517  * @param {Object} meta Metadata configuration options.
10518  * @param {Object} recordType Either an Array of field definition objects
10519  * as specified to {@link Roo.data.Record#create},
10520  * or an {@link Roo.data.Record} object
10521  * created using {@link Roo.data.Record#create}.
10522  */
10523 Roo.data.ArrayReader = function(meta, recordType){
10524     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10525 };
10526
10527 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10528     /**
10529      * Create a data block containing Roo.data.Records from an XML document.
10530      * @param {Object} o An Array of row objects which represents the dataset.
10531      * @return {Object} data A data block which is used by an Roo.data.Store object as
10532      * a cache of Roo.data.Records.
10533      */
10534     readRecords : function(o){
10535         var sid = this.meta ? this.meta.id : null;
10536         var recordType = this.recordType, fields = recordType.prototype.fields;
10537         var records = [];
10538         var root = o;
10539             for(var i = 0; i < root.length; i++){
10540                     var n = root[i];
10541                 var values = {};
10542                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10543                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10544                 var f = fields.items[j];
10545                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10546                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10547                 v = f.convert(v);
10548                 values[f.name] = v;
10549             }
10550                 var record = new recordType(values, id);
10551                 record.json = n;
10552                 records[records.length] = record;
10553             }
10554             return {
10555                 records : records,
10556                 totalRecords : records.length
10557             };
10558     }
10559 });/*
10560  * - LGPL
10561  * * 
10562  */
10563
10564 /**
10565  * @class Roo.bootstrap.ComboBox
10566  * @extends Roo.bootstrap.TriggerField
10567  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10568  * @cfg {Boolean} append (true|false) default false
10569  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10570  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10571  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10572  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10573  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10574  * @constructor
10575  * Create a new ComboBox.
10576  * @param {Object} config Configuration options
10577  */
10578 Roo.bootstrap.ComboBox = function(config){
10579     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10580     this.addEvents({
10581         /**
10582          * @event expand
10583          * Fires when the dropdown list is expanded
10584              * @param {Roo.bootstrap.ComboBox} combo This combo box
10585              */
10586         'expand' : true,
10587         /**
10588          * @event collapse
10589          * Fires when the dropdown list is collapsed
10590              * @param {Roo.bootstrap.ComboBox} combo This combo box
10591              */
10592         'collapse' : true,
10593         /**
10594          * @event beforeselect
10595          * Fires before a list item is selected. Return false to cancel the selection.
10596              * @param {Roo.bootstrap.ComboBox} combo This combo box
10597              * @param {Roo.data.Record} record The data record returned from the underlying store
10598              * @param {Number} index The index of the selected item in the dropdown list
10599              */
10600         'beforeselect' : true,
10601         /**
10602          * @event select
10603          * Fires when a list item is selected
10604              * @param {Roo.bootstrap.ComboBox} combo This combo box
10605              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10606              * @param {Number} index The index of the selected item in the dropdown list
10607              */
10608         'select' : true,
10609         /**
10610          * @event beforequery
10611          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10612          * The event object passed has these properties:
10613              * @param {Roo.bootstrap.ComboBox} combo This combo box
10614              * @param {String} query The query
10615              * @param {Boolean} forceAll true to force "all" query
10616              * @param {Boolean} cancel true to cancel the query
10617              * @param {Object} e The query event object
10618              */
10619         'beforequery': true,
10620          /**
10621          * @event add
10622          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10623              * @param {Roo.bootstrap.ComboBox} combo This combo box
10624              */
10625         'add' : true,
10626         /**
10627          * @event edit
10628          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10629              * @param {Roo.bootstrap.ComboBox} combo This combo box
10630              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10631              */
10632         'edit' : true,
10633         /**
10634          * @event remove
10635          * Fires when the remove value from the combobox array
10636              * @param {Roo.bootstrap.ComboBox} combo This combo box
10637              */
10638         'remove' : true
10639         
10640     });
10641     
10642     this.item = [];
10643     this.tickItems = [];
10644     
10645     this.selectedIndex = -1;
10646     if(this.mode == 'local'){
10647         if(config.queryDelay === undefined){
10648             this.queryDelay = 10;
10649         }
10650         if(config.minChars === undefined){
10651             this.minChars = 0;
10652         }
10653     }
10654 };
10655
10656 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10657      
10658     /**
10659      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10660      * rendering into an Roo.Editor, defaults to false)
10661      */
10662     /**
10663      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10664      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10665      */
10666     /**
10667      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10668      */
10669     /**
10670      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10671      * the dropdown list (defaults to undefined, with no header element)
10672      */
10673
10674      /**
10675      * @cfg {String/Roo.Template} tpl The template to use to render the output
10676      */
10677      
10678      /**
10679      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10680      */
10681     listWidth: undefined,
10682     /**
10683      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10684      * mode = 'remote' or 'text' if mode = 'local')
10685      */
10686     displayField: undefined,
10687     /**
10688      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10689      * mode = 'remote' or 'value' if mode = 'local'). 
10690      * Note: use of a valueField requires the user make a selection
10691      * in order for a value to be mapped.
10692      */
10693     valueField: undefined,
10694     
10695     
10696     /**
10697      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10698      * field's data value (defaults to the underlying DOM element's name)
10699      */
10700     hiddenName: undefined,
10701     /**
10702      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10703      */
10704     listClass: '',
10705     /**
10706      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10707      */
10708     selectedClass: 'active',
10709     
10710     /**
10711      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10712      */
10713     shadow:'sides',
10714     /**
10715      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10716      * anchor positions (defaults to 'tl-bl')
10717      */
10718     listAlign: 'tl-bl?',
10719     /**
10720      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10721      */
10722     maxHeight: 300,
10723     /**
10724      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10725      * query specified by the allQuery config option (defaults to 'query')
10726      */
10727     triggerAction: 'query',
10728     /**
10729      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10730      * (defaults to 4, does not apply if editable = false)
10731      */
10732     minChars : 4,
10733     /**
10734      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10735      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10736      */
10737     typeAhead: false,
10738     /**
10739      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10740      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10741      */
10742     queryDelay: 500,
10743     /**
10744      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10745      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10746      */
10747     pageSize: 0,
10748     /**
10749      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10750      * when editable = true (defaults to false)
10751      */
10752     selectOnFocus:false,
10753     /**
10754      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10755      */
10756     queryParam: 'query',
10757     /**
10758      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10759      * when mode = 'remote' (defaults to 'Loading...')
10760      */
10761     loadingText: 'Loading...',
10762     /**
10763      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10764      */
10765     resizable: false,
10766     /**
10767      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10768      */
10769     handleHeight : 8,
10770     /**
10771      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10772      * traditional select (defaults to true)
10773      */
10774     editable: true,
10775     /**
10776      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10777      */
10778     allQuery: '',
10779     /**
10780      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10781      */
10782     mode: 'remote',
10783     /**
10784      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10785      * listWidth has a higher value)
10786      */
10787     minListWidth : 70,
10788     /**
10789      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10790      * allow the user to set arbitrary text into the field (defaults to false)
10791      */
10792     forceSelection:false,
10793     /**
10794      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10795      * if typeAhead = true (defaults to 250)
10796      */
10797     typeAheadDelay : 250,
10798     /**
10799      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10800      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10801      */
10802     valueNotFoundText : undefined,
10803     /**
10804      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10805      */
10806     blockFocus : false,
10807     
10808     /**
10809      * @cfg {Boolean} disableClear Disable showing of clear button.
10810      */
10811     disableClear : false,
10812     /**
10813      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10814      */
10815     alwaysQuery : false,
10816     
10817     /**
10818      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10819      */
10820     multiple : false,
10821     
10822     /**
10823      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
10824      */
10825     invalidClass : "has-warning",
10826     
10827     /**
10828      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
10829      */
10830     validClass : "has-success",
10831     
10832     //private
10833     addicon : false,
10834     editicon: false,
10835     
10836     page: 0,
10837     hasQuery: false,
10838     append: false,
10839     loadNext: false,
10840     autoFocus : true,
10841     tickable : false,
10842     btnPosition : 'right',
10843     triggerList : true,
10844     showToggleBtn : true,
10845     // element that contains real text value.. (when hidden is used..)
10846     
10847     getAutoCreate : function()
10848     {
10849         var cfg = false;
10850         
10851         /*
10852          *  Normal ComboBox
10853          */
10854         if(!this.tickable){
10855             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10856             return cfg;
10857         }
10858         
10859         /*
10860          *  ComboBox with tickable selections
10861          */
10862              
10863         var align = this.labelAlign || this.parentLabelAlign();
10864         
10865         cfg = {
10866             cls : 'form-group roo-combobox-tickable' //input-group
10867         };
10868         
10869         
10870         var buttons = {
10871             tag : 'div',
10872             cls : 'tickable-buttons',
10873             cn : [
10874                 {
10875                     tag : 'button',
10876                     type : 'button',
10877                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10878                     html : 'Edit'
10879                 },
10880                 {
10881                     tag : 'button',
10882                     type : 'button',
10883                     name : 'ok',
10884                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10885                     html : 'Done'
10886                 },
10887                 {
10888                     tag : 'button',
10889                     type : 'button',
10890                     name : 'cancel',
10891                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10892                     html : 'Cancel'
10893                 }
10894             ]
10895         };
10896         
10897         var _this = this;
10898         Roo.each(buttons.cn, function(c){
10899             if (_this.size) {
10900                 c.cls += ' btn-' + _this.size;
10901             }
10902
10903             if (_this.disabled) {
10904                 c.disabled = true;
10905             }
10906         });
10907         
10908         var box = {
10909             tag: 'div',
10910             cn: [
10911                 {
10912                     tag: 'input',
10913                     type : 'hidden',
10914                     cls: 'form-hidden-field'
10915                 },
10916                 {
10917                     tag: 'ul',
10918                     cls: 'select2-choices',
10919                     cn:[
10920                         {
10921                             tag: 'li',
10922                             cls: 'select2-search-field',
10923                             cn: [
10924
10925                                 buttons
10926                             ]
10927                         }
10928                     ]
10929                 }
10930             ]
10931         }
10932         
10933         var combobox = {
10934             cls: 'select2-container input-group select2-container-multi',
10935             cn: [
10936                 box
10937 //                {
10938 //                    tag: 'ul',
10939 //                    cls: 'typeahead typeahead-long dropdown-menu',
10940 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10941 //                }
10942             ]
10943         };
10944         
10945         if(this.hasFeedback && !this.allowBlank){
10946             
10947             var feedback = {
10948                 tag: 'span',
10949                 cls: 'glyphicon form-control-feedback'
10950             };
10951
10952             combobox.cn.push(feedback);
10953         }
10954         
10955         if (align ==='left' && this.fieldLabel.length) {
10956             
10957                 Roo.log("left and has label");
10958                 cfg.cn = [
10959                     
10960                     {
10961                         tag: 'label',
10962                         'for' :  id,
10963                         cls : 'control-label col-sm-' + this.labelWidth,
10964                         html : this.fieldLabel
10965                         
10966                     },
10967                     {
10968                         cls : "col-sm-" + (12 - this.labelWidth), 
10969                         cn: [
10970                             combobox
10971                         ]
10972                     }
10973                     
10974                 ];
10975         } else if ( this.fieldLabel.length) {
10976                 Roo.log(" label");
10977                  cfg.cn = [
10978                    
10979                     {
10980                         tag: 'label',
10981                         //cls : 'input-group-addon',
10982                         html : this.fieldLabel
10983                         
10984                     },
10985                     
10986                     combobox
10987                     
10988                 ];
10989
10990         } else {
10991             
10992                 Roo.log(" no label && no align");
10993                 cfg = combobox
10994                      
10995                 
10996         }
10997          
10998         var settings=this;
10999         ['xs','sm','md','lg'].map(function(size){
11000             if (settings[size]) {
11001                 cfg.cls += ' col-' + size + '-' + settings[size];
11002             }
11003         });
11004         
11005         return cfg;
11006         
11007     },
11008     
11009     // private
11010     initEvents: function()
11011     {
11012         
11013         if (!this.store) {
11014             throw "can not find store for combo";
11015         }
11016         this.store = Roo.factory(this.store, Roo.data);
11017         
11018         if(this.tickable){
11019             this.initTickableEvents();
11020             return;
11021         }
11022         
11023         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11024         
11025         if(this.hiddenName){
11026             
11027             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11028             
11029             this.hiddenField.dom.value =
11030                 this.hiddenValue !== undefined ? this.hiddenValue :
11031                 this.value !== undefined ? this.value : '';
11032
11033             // prevent input submission
11034             this.el.dom.removeAttribute('name');
11035             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11036              
11037              
11038         }
11039         //if(Roo.isGecko){
11040         //    this.el.dom.setAttribute('autocomplete', 'off');
11041         //}
11042         
11043         var cls = 'x-combo-list';
11044         
11045         //this.list = new Roo.Layer({
11046         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11047         //});
11048         
11049         var _this = this;
11050         
11051         (function(){
11052             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11053             _this.list.setWidth(lw);
11054         }).defer(100);
11055         
11056         this.list.on('mouseover', this.onViewOver, this);
11057         this.list.on('mousemove', this.onViewMove, this);
11058         
11059         this.list.on('scroll', this.onViewScroll, this);
11060         
11061         /*
11062         this.list.swallowEvent('mousewheel');
11063         this.assetHeight = 0;
11064
11065         if(this.title){
11066             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11067             this.assetHeight += this.header.getHeight();
11068         }
11069
11070         this.innerList = this.list.createChild({cls:cls+'-inner'});
11071         this.innerList.on('mouseover', this.onViewOver, this);
11072         this.innerList.on('mousemove', this.onViewMove, this);
11073         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11074         
11075         if(this.allowBlank && !this.pageSize && !this.disableClear){
11076             this.footer = this.list.createChild({cls:cls+'-ft'});
11077             this.pageTb = new Roo.Toolbar(this.footer);
11078            
11079         }
11080         if(this.pageSize){
11081             this.footer = this.list.createChild({cls:cls+'-ft'});
11082             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11083                     {pageSize: this.pageSize});
11084             
11085         }
11086         
11087         if (this.pageTb && this.allowBlank && !this.disableClear) {
11088             var _this = this;
11089             this.pageTb.add(new Roo.Toolbar.Fill(), {
11090                 cls: 'x-btn-icon x-btn-clear',
11091                 text: '&#160;',
11092                 handler: function()
11093                 {
11094                     _this.collapse();
11095                     _this.clearValue();
11096                     _this.onSelect(false, -1);
11097                 }
11098             });
11099         }
11100         if (this.footer) {
11101             this.assetHeight += this.footer.getHeight();
11102         }
11103         */
11104             
11105         if(!this.tpl){
11106             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11107         }
11108
11109         this.view = new Roo.View(this.list, this.tpl, {
11110             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11111         });
11112         //this.view.wrapEl.setDisplayed(false);
11113         this.view.on('click', this.onViewClick, this);
11114         
11115         
11116         
11117         this.store.on('beforeload', this.onBeforeLoad, this);
11118         this.store.on('load', this.onLoad, this);
11119         this.store.on('loadexception', this.onLoadException, this);
11120         /*
11121         if(this.resizable){
11122             this.resizer = new Roo.Resizable(this.list,  {
11123                pinned:true, handles:'se'
11124             });
11125             this.resizer.on('resize', function(r, w, h){
11126                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11127                 this.listWidth = w;
11128                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11129                 this.restrictHeight();
11130             }, this);
11131             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11132         }
11133         */
11134         if(!this.editable){
11135             this.editable = true;
11136             this.setEditable(false);
11137         }
11138         
11139         /*
11140         
11141         if (typeof(this.events.add.listeners) != 'undefined') {
11142             
11143             this.addicon = this.wrap.createChild(
11144                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11145        
11146             this.addicon.on('click', function(e) {
11147                 this.fireEvent('add', this);
11148             }, this);
11149         }
11150         if (typeof(this.events.edit.listeners) != 'undefined') {
11151             
11152             this.editicon = this.wrap.createChild(
11153                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11154             if (this.addicon) {
11155                 this.editicon.setStyle('margin-left', '40px');
11156             }
11157             this.editicon.on('click', function(e) {
11158                 
11159                 // we fire even  if inothing is selected..
11160                 this.fireEvent('edit', this, this.lastData );
11161                 
11162             }, this);
11163         }
11164         */
11165         
11166         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11167             "up" : function(e){
11168                 this.inKeyMode = true;
11169                 this.selectPrev();
11170             },
11171
11172             "down" : function(e){
11173                 if(!this.isExpanded()){
11174                     this.onTriggerClick();
11175                 }else{
11176                     this.inKeyMode = true;
11177                     this.selectNext();
11178                 }
11179             },
11180
11181             "enter" : function(e){
11182 //                this.onViewClick();
11183                 //return true;
11184                 this.collapse();
11185                 
11186                 if(this.fireEvent("specialkey", this, e)){
11187                     this.onViewClick(false);
11188                 }
11189                 
11190                 return true;
11191             },
11192
11193             "esc" : function(e){
11194                 this.collapse();
11195             },
11196
11197             "tab" : function(e){
11198                 this.collapse();
11199                 
11200                 if(this.fireEvent("specialkey", this, e)){
11201                     this.onViewClick(false);
11202                 }
11203                 
11204                 return true;
11205             },
11206
11207             scope : this,
11208
11209             doRelay : function(foo, bar, hname){
11210                 if(hname == 'down' || this.scope.isExpanded()){
11211                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11212                 }
11213                 return true;
11214             },
11215
11216             forceKeyDown: true
11217         });
11218         
11219         
11220         this.queryDelay = Math.max(this.queryDelay || 10,
11221                 this.mode == 'local' ? 10 : 250);
11222         
11223         
11224         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11225         
11226         if(this.typeAhead){
11227             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11228         }
11229         if(this.editable !== false){
11230             this.inputEl().on("keyup", this.onKeyUp, this);
11231         }
11232         if(this.forceSelection){
11233             this.inputEl().on('blur', this.doForce, this);
11234         }
11235         
11236         if(this.multiple){
11237             this.choices = this.el.select('ul.select2-choices', true).first();
11238             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11239         }
11240     },
11241     
11242     initTickableEvents: function()
11243     {   
11244         this.createList();
11245         
11246         if(this.hiddenName){
11247             
11248             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11249             
11250             this.hiddenField.dom.value =
11251                 this.hiddenValue !== undefined ? this.hiddenValue :
11252                 this.value !== undefined ? this.value : '';
11253
11254             // prevent input submission
11255             this.el.dom.removeAttribute('name');
11256             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11257              
11258              
11259         }
11260         
11261 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11262         
11263         this.choices = this.el.select('ul.select2-choices', true).first();
11264         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11265         if(this.triggerList){
11266             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11267         }
11268          
11269         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11270         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11271         
11272         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11273         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11274         
11275         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11276         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11277         
11278         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11279         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11280         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11281         
11282         this.okBtn.hide();
11283         this.cancelBtn.hide();
11284         
11285         var _this = this;
11286         
11287         (function(){
11288             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11289             _this.list.setWidth(lw);
11290         }).defer(100);
11291         
11292         this.list.on('mouseover', this.onViewOver, this);
11293         this.list.on('mousemove', this.onViewMove, this);
11294         
11295         this.list.on('scroll', this.onViewScroll, this);
11296         
11297         if(!this.tpl){
11298             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>';
11299         }
11300
11301         this.view = new Roo.View(this.list, this.tpl, {
11302             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11303         });
11304         
11305         //this.view.wrapEl.setDisplayed(false);
11306         this.view.on('click', this.onViewClick, this);
11307         
11308         
11309         
11310         this.store.on('beforeload', this.onBeforeLoad, this);
11311         this.store.on('load', this.onLoad, this);
11312         this.store.on('loadexception', this.onLoadException, this);
11313         
11314 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11315 //            "up" : function(e){
11316 //                this.inKeyMode = true;
11317 //                this.selectPrev();
11318 //            },
11319 //
11320 //            "down" : function(e){
11321 //                if(!this.isExpanded()){
11322 //                    this.onTriggerClick();
11323 //                }else{
11324 //                    this.inKeyMode = true;
11325 //                    this.selectNext();
11326 //                }
11327 //            },
11328 //
11329 //            "enter" : function(e){
11330 ////                this.onViewClick();
11331 //                //return true;
11332 //                this.collapse();
11333 //                
11334 //                if(this.fireEvent("specialkey", this, e)){
11335 //                    this.onViewClick(false);
11336 //                }
11337 //                
11338 //                return true;
11339 //            },
11340 //
11341 //            "esc" : function(e){
11342 //                this.collapse();
11343 //            },
11344 //
11345 //            "tab" : function(e){
11346 //                this.collapse();
11347 //                
11348 //                if(this.fireEvent("specialkey", this, e)){
11349 //                    this.onViewClick(false);
11350 //                }
11351 //                
11352 //                return true;
11353 //            },
11354 //
11355 //            scope : this,
11356 //
11357 //            doRelay : function(foo, bar, hname){
11358 //                if(hname == 'down' || this.scope.isExpanded()){
11359 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11360 //                }
11361 //                return true;
11362 //            },
11363 //
11364 //            forceKeyDown: true
11365 //        });
11366         
11367         
11368         this.queryDelay = Math.max(this.queryDelay || 10,
11369                 this.mode == 'local' ? 10 : 250);
11370         
11371         
11372         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11373         
11374         if(this.typeAhead){
11375             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11376         }
11377         
11378     },
11379
11380     onDestroy : function(){
11381         if(this.view){
11382             this.view.setStore(null);
11383             this.view.el.removeAllListeners();
11384             this.view.el.remove();
11385             this.view.purgeListeners();
11386         }
11387         if(this.list){
11388             this.list.dom.innerHTML  = '';
11389         }
11390         
11391         if(this.store){
11392             this.store.un('beforeload', this.onBeforeLoad, this);
11393             this.store.un('load', this.onLoad, this);
11394             this.store.un('loadexception', this.onLoadException, this);
11395         }
11396         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11397     },
11398
11399     // private
11400     fireKey : function(e){
11401         if(e.isNavKeyPress() && !this.list.isVisible()){
11402             this.fireEvent("specialkey", this, e);
11403         }
11404     },
11405
11406     // private
11407     onResize: function(w, h){
11408 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11409 //        
11410 //        if(typeof w != 'number'){
11411 //            // we do not handle it!?!?
11412 //            return;
11413 //        }
11414 //        var tw = this.trigger.getWidth();
11415 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11416 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11417 //        var x = w - tw;
11418 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11419 //            
11420 //        //this.trigger.setStyle('left', x+'px');
11421 //        
11422 //        if(this.list && this.listWidth === undefined){
11423 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11424 //            this.list.setWidth(lw);
11425 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11426 //        }
11427         
11428     
11429         
11430     },
11431
11432     /**
11433      * Allow or prevent the user from directly editing the field text.  If false is passed,
11434      * the user will only be able to select from the items defined in the dropdown list.  This method
11435      * is the runtime equivalent of setting the 'editable' config option at config time.
11436      * @param {Boolean} value True to allow the user to directly edit the field text
11437      */
11438     setEditable : function(value){
11439         if(value == this.editable){
11440             return;
11441         }
11442         this.editable = value;
11443         if(!value){
11444             this.inputEl().dom.setAttribute('readOnly', true);
11445             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11446             this.inputEl().addClass('x-combo-noedit');
11447         }else{
11448             this.inputEl().dom.setAttribute('readOnly', false);
11449             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11450             this.inputEl().removeClass('x-combo-noedit');
11451         }
11452     },
11453
11454     // private
11455     
11456     onBeforeLoad : function(combo,opts){
11457         if(!this.hasFocus){
11458             return;
11459         }
11460          if (!opts.add) {
11461             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11462          }
11463         this.restrictHeight();
11464         this.selectedIndex = -1;
11465     },
11466
11467     // private
11468     onLoad : function(){
11469         
11470         this.hasQuery = false;
11471         
11472         if(!this.hasFocus){
11473             return;
11474         }
11475         
11476         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11477             this.loading.hide();
11478         }
11479         
11480         if(this.store.getCount() > 0){
11481             this.expand();
11482 //            this.restrictHeight();
11483             if(this.lastQuery == this.allQuery){
11484                 if(this.editable && !this.tickable){
11485                     this.inputEl().dom.select();
11486                 }
11487                 
11488                 if(
11489                     !this.selectByValue(this.value, true) &&
11490                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11491                     this.store.lastOptions.add != true)
11492                 ){
11493                     this.select(0, true);
11494                 }
11495             }else{
11496                 if(this.autoFocus){
11497                     this.selectNext();
11498                 }
11499                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11500                     this.taTask.delay(this.typeAheadDelay);
11501                 }
11502             }
11503         }else{
11504             this.onEmptyResults();
11505         }
11506         
11507         //this.el.focus();
11508     },
11509     // private
11510     onLoadException : function()
11511     {
11512         this.hasQuery = false;
11513         
11514         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11515             this.loading.hide();
11516         }
11517         
11518         this.collapse();
11519         Roo.log(this.store.reader.jsonData);
11520         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11521             // fixme
11522             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11523         }
11524         
11525         
11526     },
11527     // private
11528     onTypeAhead : function(){
11529         if(this.store.getCount() > 0){
11530             var r = this.store.getAt(0);
11531             var newValue = r.data[this.displayField];
11532             var len = newValue.length;
11533             var selStart = this.getRawValue().length;
11534             
11535             if(selStart != len){
11536                 this.setRawValue(newValue);
11537                 this.selectText(selStart, newValue.length);
11538             }
11539         }
11540     },
11541
11542     // private
11543     onSelect : function(record, index){
11544         
11545         if(this.fireEvent('beforeselect', this, record, index) !== false){
11546         
11547             this.setFromData(index > -1 ? record.data : false);
11548             
11549             this.collapse();
11550             this.fireEvent('select', this, record, index);
11551         }
11552     },
11553
11554     /**
11555      * Returns the currently selected field value or empty string if no value is set.
11556      * @return {String} value The selected value
11557      */
11558     getValue : function(){
11559         
11560         if(this.multiple){
11561             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11562         }
11563         
11564         if(this.valueField){
11565             return typeof this.value != 'undefined' ? this.value : '';
11566         }else{
11567             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11568         }
11569     },
11570
11571     /**
11572      * Clears any text/value currently set in the field
11573      */
11574     clearValue : function(){
11575         if(this.hiddenField){
11576             this.hiddenField.dom.value = '';
11577         }
11578         this.value = '';
11579         this.setRawValue('');
11580         this.lastSelectionText = '';
11581         this.lastData = false;
11582         
11583     },
11584
11585     /**
11586      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11587      * will be displayed in the field.  If the value does not match the data value of an existing item,
11588      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11589      * Otherwise the field will be blank (although the value will still be set).
11590      * @param {String} value The value to match
11591      */
11592     setValue : function(v){
11593         if(this.multiple){
11594             this.syncValue();
11595             return;
11596         }
11597         
11598         var text = v;
11599         if(this.valueField){
11600             var r = this.findRecord(this.valueField, v);
11601             if(r){
11602                 text = r.data[this.displayField];
11603             }else if(this.valueNotFoundText !== undefined){
11604                 text = this.valueNotFoundText;
11605             }
11606         }
11607         this.lastSelectionText = text;
11608         if(this.hiddenField){
11609             this.hiddenField.dom.value = v;
11610         }
11611         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11612         this.value = v;
11613     },
11614     /**
11615      * @property {Object} the last set data for the element
11616      */
11617     
11618     lastData : false,
11619     /**
11620      * Sets the value of the field based on a object which is related to the record format for the store.
11621      * @param {Object} value the value to set as. or false on reset?
11622      */
11623     setFromData : function(o){
11624         
11625         if(this.multiple){
11626             this.addItem(o);
11627             return;
11628         }
11629             
11630         var dv = ''; // display value
11631         var vv = ''; // value value..
11632         this.lastData = o;
11633         if (this.displayField) {
11634             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11635         } else {
11636             // this is an error condition!!!
11637             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11638         }
11639         
11640         if(this.valueField){
11641             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11642         }
11643         
11644         if(this.hiddenField){
11645             this.hiddenField.dom.value = vv;
11646             
11647             this.lastSelectionText = dv;
11648             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11649             this.value = vv;
11650             return;
11651         }
11652         // no hidden field.. - we store the value in 'value', but still display
11653         // display field!!!!
11654         this.lastSelectionText = dv;
11655         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11656         this.value = vv;
11657         
11658         
11659     },
11660     // private
11661     reset : function(){
11662         // overridden so that last data is reset..
11663         this.setValue(this.originalValue);
11664         this.clearInvalid();
11665         this.lastData = false;
11666         if (this.view) {
11667             this.view.clearSelections();
11668         }
11669     },
11670     // private
11671     findRecord : function(prop, value){
11672         var record;
11673         if(this.store.getCount() > 0){
11674             this.store.each(function(r){
11675                 if(r.data[prop] == value){
11676                     record = r;
11677                     return false;
11678                 }
11679                 return true;
11680             });
11681         }
11682         return record;
11683     },
11684     
11685     getName: function()
11686     {
11687         // returns hidden if it's set..
11688         if (!this.rendered) {return ''};
11689         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11690         
11691     },
11692     // private
11693     onViewMove : function(e, t){
11694         this.inKeyMode = false;
11695     },
11696
11697     // private
11698     onViewOver : function(e, t){
11699         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11700             return;
11701         }
11702         var item = this.view.findItemFromChild(t);
11703         
11704         if(item){
11705             var index = this.view.indexOf(item);
11706             this.select(index, false);
11707         }
11708     },
11709
11710     // private
11711     onViewClick : function(view, doFocus, el, e)
11712     {
11713         var index = this.view.getSelectedIndexes()[0];
11714         
11715         var r = this.store.getAt(index);
11716         
11717         if(this.tickable){
11718             
11719             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11720                 return;
11721             }
11722             
11723             var rm = false;
11724             var _this = this;
11725             
11726             Roo.each(this.tickItems, function(v,k){
11727                 
11728                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11729                     _this.tickItems.splice(k, 1);
11730                     rm = true;
11731                     return;
11732                 }
11733             });
11734             
11735             if(rm){
11736                 return;
11737             }
11738             
11739             this.tickItems.push(r.data);
11740             return;
11741         }
11742         
11743         if(r){
11744             this.onSelect(r, index);
11745         }
11746         if(doFocus !== false && !this.blockFocus){
11747             this.inputEl().focus();
11748         }
11749     },
11750
11751     // private
11752     restrictHeight : function(){
11753         //this.innerList.dom.style.height = '';
11754         //var inner = this.innerList.dom;
11755         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11756         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11757         //this.list.beginUpdate();
11758         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11759         this.list.alignTo(this.inputEl(), this.listAlign);
11760         this.list.alignTo(this.inputEl(), this.listAlign);
11761         //this.list.endUpdate();
11762     },
11763
11764     // private
11765     onEmptyResults : function(){
11766         this.collapse();
11767     },
11768
11769     /**
11770      * Returns true if the dropdown list is expanded, else false.
11771      */
11772     isExpanded : function(){
11773         return this.list.isVisible();
11774     },
11775
11776     /**
11777      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11778      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11779      * @param {String} value The data value of the item to select
11780      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11781      * selected item if it is not currently in view (defaults to true)
11782      * @return {Boolean} True if the value matched an item in the list, else false
11783      */
11784     selectByValue : function(v, scrollIntoView){
11785         if(v !== undefined && v !== null){
11786             var r = this.findRecord(this.valueField || this.displayField, v);
11787             if(r){
11788                 this.select(this.store.indexOf(r), scrollIntoView);
11789                 return true;
11790             }
11791         }
11792         return false;
11793     },
11794
11795     /**
11796      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11797      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11798      * @param {Number} index The zero-based index of the list item to select
11799      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11800      * selected item if it is not currently in view (defaults to true)
11801      */
11802     select : function(index, scrollIntoView){
11803         this.selectedIndex = index;
11804         this.view.select(index);
11805         if(scrollIntoView !== false){
11806             var el = this.view.getNode(index);
11807             if(el && !this.multiple && !this.tickable){
11808                 this.list.scrollChildIntoView(el, false);
11809             }
11810         }
11811     },
11812
11813     // private
11814     selectNext : function(){
11815         var ct = this.store.getCount();
11816         if(ct > 0){
11817             if(this.selectedIndex == -1){
11818                 this.select(0);
11819             }else if(this.selectedIndex < ct-1){
11820                 this.select(this.selectedIndex+1);
11821             }
11822         }
11823     },
11824
11825     // private
11826     selectPrev : function(){
11827         var ct = this.store.getCount();
11828         if(ct > 0){
11829             if(this.selectedIndex == -1){
11830                 this.select(0);
11831             }else if(this.selectedIndex != 0){
11832                 this.select(this.selectedIndex-1);
11833             }
11834         }
11835     },
11836
11837     // private
11838     onKeyUp : function(e){
11839         if(this.editable !== false && !e.isSpecialKey()){
11840             this.lastKey = e.getKey();
11841             this.dqTask.delay(this.queryDelay);
11842         }
11843     },
11844
11845     // private
11846     validateBlur : function(){
11847         return !this.list || !this.list.isVisible();   
11848     },
11849
11850     // private
11851     initQuery : function(){
11852         this.doQuery(this.getRawValue());
11853     },
11854
11855     // private
11856     doForce : function(){
11857         if(this.inputEl().dom.value.length > 0){
11858             this.inputEl().dom.value =
11859                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11860              
11861         }
11862     },
11863
11864     /**
11865      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11866      * query allowing the query action to be canceled if needed.
11867      * @param {String} query The SQL query to execute
11868      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11869      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11870      * saved in the current store (defaults to false)
11871      */
11872     doQuery : function(q, forceAll){
11873         
11874         if(q === undefined || q === null){
11875             q = '';
11876         }
11877         var qe = {
11878             query: q,
11879             forceAll: forceAll,
11880             combo: this,
11881             cancel:false
11882         };
11883         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11884             return false;
11885         }
11886         q = qe.query;
11887         
11888         forceAll = qe.forceAll;
11889         if(forceAll === true || (q.length >= this.minChars)){
11890             
11891             this.hasQuery = true;
11892             
11893             if(this.lastQuery != q || this.alwaysQuery){
11894                 this.lastQuery = q;
11895                 if(this.mode == 'local'){
11896                     this.selectedIndex = -1;
11897                     if(forceAll){
11898                         this.store.clearFilter();
11899                     }else{
11900                         this.store.filter(this.displayField, q);
11901                     }
11902                     this.onLoad();
11903                 }else{
11904                     this.store.baseParams[this.queryParam] = q;
11905                     
11906                     var options = {params : this.getParams(q)};
11907                     
11908                     if(this.loadNext){
11909                         options.add = true;
11910                         options.params.start = this.page * this.pageSize;
11911                     }
11912                     
11913                     this.store.load(options);
11914                     /*
11915                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11916                      *  we should expand the list on onLoad
11917                      *  so command out it
11918                      */
11919 //                    this.expand();
11920                 }
11921             }else{
11922                 this.selectedIndex = -1;
11923                 this.onLoad();   
11924             }
11925         }
11926         
11927         this.loadNext = false;
11928     },
11929
11930     // private
11931     getParams : function(q){
11932         var p = {};
11933         //p[this.queryParam] = q;
11934         
11935         if(this.pageSize){
11936             p.start = 0;
11937             p.limit = this.pageSize;
11938         }
11939         return p;
11940     },
11941
11942     /**
11943      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11944      */
11945     collapse : function(){
11946         if(!this.isExpanded()){
11947             return;
11948         }
11949         
11950         this.list.hide();
11951         
11952         if(this.tickable){
11953             this.hasFocus = false;
11954             this.okBtn.hide();
11955             this.cancelBtn.hide();
11956             this.trigger.show();
11957         }
11958         
11959         Roo.get(document).un('mousedown', this.collapseIf, this);
11960         Roo.get(document).un('mousewheel', this.collapseIf, this);
11961         if (!this.editable) {
11962             Roo.get(document).un('keydown', this.listKeyPress, this);
11963         }
11964         this.fireEvent('collapse', this);
11965     },
11966
11967     // private
11968     collapseIf : function(e){
11969         var in_combo  = e.within(this.el);
11970         var in_list =  e.within(this.list);
11971         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11972         
11973         if (in_combo || in_list || is_list) {
11974             //e.stopPropagation();
11975             return;
11976         }
11977         
11978         if(this.tickable){
11979             this.onTickableFooterButtonClick(e, false, false);
11980         }
11981
11982         this.collapse();
11983         
11984     },
11985
11986     /**
11987      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11988      */
11989     expand : function(){
11990        
11991         if(this.isExpanded() || !this.hasFocus){
11992             return;
11993         }
11994         
11995         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11996         this.list.setWidth(lw);
11997         
11998         
11999          Roo.log('expand');
12000         
12001         this.list.show();
12002         
12003         this.restrictHeight();
12004         
12005         if(this.tickable){
12006             
12007             this.tickItems = Roo.apply([], this.item);
12008             
12009             this.okBtn.show();
12010             this.cancelBtn.show();
12011             this.trigger.hide();
12012             
12013         }
12014         
12015         Roo.get(document).on('mousedown', this.collapseIf, this);
12016         Roo.get(document).on('mousewheel', this.collapseIf, this);
12017         if (!this.editable) {
12018             Roo.get(document).on('keydown', this.listKeyPress, this);
12019         }
12020         
12021         this.fireEvent('expand', this);
12022     },
12023
12024     // private
12025     // Implements the default empty TriggerField.onTriggerClick function
12026     onTriggerClick : function(e)
12027     {
12028         Roo.log('trigger click');
12029         
12030         if(this.disabled || !this.triggerList){
12031             return;
12032         }
12033         
12034         this.page = 0;
12035         this.loadNext = false;
12036         
12037         if(this.isExpanded()){
12038             this.collapse();
12039             if (!this.blockFocus) {
12040                 this.inputEl().focus();
12041             }
12042             
12043         }else {
12044             this.hasFocus = true;
12045             if(this.triggerAction == 'all') {
12046                 this.doQuery(this.allQuery, true);
12047             } else {
12048                 this.doQuery(this.getRawValue());
12049             }
12050             if (!this.blockFocus) {
12051                 this.inputEl().focus();
12052             }
12053         }
12054     },
12055     
12056     onTickableTriggerClick : function(e)
12057     {
12058         if(this.disabled){
12059             return;
12060         }
12061         
12062         this.page = 0;
12063         this.loadNext = false;
12064         this.hasFocus = true;
12065         
12066         if(this.triggerAction == 'all') {
12067             this.doQuery(this.allQuery, true);
12068         } else {
12069             this.doQuery(this.getRawValue());
12070         }
12071     },
12072     
12073     onSearchFieldClick : function(e)
12074     {
12075         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12076             this.onTickableFooterButtonClick(e, false, false);
12077             return;
12078         }
12079         
12080         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12081             return;
12082         }
12083         
12084         this.page = 0;
12085         this.loadNext = false;
12086         this.hasFocus = true;
12087         
12088         if(this.triggerAction == 'all') {
12089             this.doQuery(this.allQuery, true);
12090         } else {
12091             this.doQuery(this.getRawValue());
12092         }
12093     },
12094     
12095     listKeyPress : function(e)
12096     {
12097         //Roo.log('listkeypress');
12098         // scroll to first matching element based on key pres..
12099         if (e.isSpecialKey()) {
12100             return false;
12101         }
12102         var k = String.fromCharCode(e.getKey()).toUpperCase();
12103         //Roo.log(k);
12104         var match  = false;
12105         var csel = this.view.getSelectedNodes();
12106         var cselitem = false;
12107         if (csel.length) {
12108             var ix = this.view.indexOf(csel[0]);
12109             cselitem  = this.store.getAt(ix);
12110             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12111                 cselitem = false;
12112             }
12113             
12114         }
12115         
12116         this.store.each(function(v) { 
12117             if (cselitem) {
12118                 // start at existing selection.
12119                 if (cselitem.id == v.id) {
12120                     cselitem = false;
12121                 }
12122                 return true;
12123             }
12124                 
12125             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12126                 match = this.store.indexOf(v);
12127                 return false;
12128             }
12129             return true;
12130         }, this);
12131         
12132         if (match === false) {
12133             return true; // no more action?
12134         }
12135         // scroll to?
12136         this.view.select(match);
12137         var sn = Roo.get(this.view.getSelectedNodes()[0])
12138         sn.scrollIntoView(sn.dom.parentNode, false);
12139     },
12140     
12141     onViewScroll : function(e, t){
12142         
12143         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){
12144             return;
12145         }
12146         
12147         this.hasQuery = true;
12148         
12149         this.loading = this.list.select('.loading', true).first();
12150         
12151         if(this.loading === null){
12152             this.list.createChild({
12153                 tag: 'div',
12154                 cls: 'loading select2-more-results select2-active',
12155                 html: 'Loading more results...'
12156             })
12157             
12158             this.loading = this.list.select('.loading', true).first();
12159             
12160             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12161             
12162             this.loading.hide();
12163         }
12164         
12165         this.loading.show();
12166         
12167         var _combo = this;
12168         
12169         this.page++;
12170         this.loadNext = true;
12171         
12172         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12173         
12174         return;
12175     },
12176     
12177     addItem : function(o)
12178     {   
12179         var dv = ''; // display value
12180         
12181         if (this.displayField) {
12182             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12183         } else {
12184             // this is an error condition!!!
12185             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12186         }
12187         
12188         if(!dv.length){
12189             return;
12190         }
12191         
12192         var choice = this.choices.createChild({
12193             tag: 'li',
12194             cls: 'select2-search-choice',
12195             cn: [
12196                 {
12197                     tag: 'div',
12198                     html: dv
12199                 },
12200                 {
12201                     tag: 'a',
12202                     href: '#',
12203                     cls: 'select2-search-choice-close',
12204                     tabindex: '-1'
12205                 }
12206             ]
12207             
12208         }, this.searchField);
12209         
12210         var close = choice.select('a.select2-search-choice-close', true).first()
12211         
12212         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12213         
12214         this.item.push(o);
12215         
12216         this.lastData = o;
12217         
12218         this.syncValue();
12219         
12220         this.inputEl().dom.value = '';
12221         
12222         this.validate();
12223     },
12224     
12225     onRemoveItem : function(e, _self, o)
12226     {
12227         e.preventDefault();
12228         
12229         this.lastItem = Roo.apply([], this.item);
12230         
12231         var index = this.item.indexOf(o.data) * 1;
12232         
12233         if( index < 0){
12234             Roo.log('not this item?!');
12235             return;
12236         }
12237         
12238         this.item.splice(index, 1);
12239         o.item.remove();
12240         
12241         this.syncValue();
12242         
12243         this.fireEvent('remove', this, e);
12244         
12245         this.validate();
12246         
12247     },
12248     
12249     syncValue : function()
12250     {
12251         if(!this.item.length){
12252             this.clearValue();
12253             return;
12254         }
12255             
12256         var value = [];
12257         var _this = this;
12258         Roo.each(this.item, function(i){
12259             if(_this.valueField){
12260                 value.push(i[_this.valueField]);
12261                 return;
12262             }
12263
12264             value.push(i);
12265         });
12266
12267         this.value = value.join(',');
12268
12269         if(this.hiddenField){
12270             this.hiddenField.dom.value = this.value;
12271         }
12272     },
12273     
12274     clearItem : function()
12275     {
12276         if(!this.multiple){
12277             return;
12278         }
12279         
12280         this.item = [];
12281         
12282         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12283            c.remove();
12284         });
12285         
12286         this.syncValue();
12287         
12288         this.validate();
12289     },
12290     
12291     inputEl: function ()
12292     {
12293         if(this.tickable){
12294             return this.searchField;
12295         }
12296         return this.el.select('input.form-control',true).first();
12297     },
12298     
12299     
12300     onTickableFooterButtonClick : function(e, btn, el)
12301     {
12302         e.preventDefault();
12303         
12304         this.lastItem = Roo.apply([], this.item);
12305         
12306         if(btn && btn.name == 'cancel'){
12307             this.tickItems = Roo.apply([], this.item);
12308             this.collapse();
12309             return;
12310         }
12311         
12312         this.clearItem();
12313         
12314         var _this = this;
12315         
12316         Roo.each(this.tickItems, function(o){
12317             _this.addItem(o);
12318         });
12319         
12320         this.collapse();
12321         
12322     },
12323     
12324     validate : function()
12325     {
12326         var v = this.getRawValue();
12327         
12328         if(this.multiple){
12329             v = this.getValue();
12330         }
12331         
12332         if(this.disabled || this.allowBlank || v.length){
12333             this.markValid();
12334             return true;
12335         }
12336         
12337         this.markInvalid();
12338         return false;
12339     }
12340     
12341     
12342
12343     /** 
12344     * @cfg {Boolean} grow 
12345     * @hide 
12346     */
12347     /** 
12348     * @cfg {Number} growMin 
12349     * @hide 
12350     */
12351     /** 
12352     * @cfg {Number} growMax 
12353     * @hide 
12354     */
12355     /**
12356      * @hide
12357      * @method autoSize
12358      */
12359 });
12360 /*
12361  * Based on:
12362  * Ext JS Library 1.1.1
12363  * Copyright(c) 2006-2007, Ext JS, LLC.
12364  *
12365  * Originally Released Under LGPL - original licence link has changed is not relivant.
12366  *
12367  * Fork - LGPL
12368  * <script type="text/javascript">
12369  */
12370
12371 /**
12372  * @class Roo.View
12373  * @extends Roo.util.Observable
12374  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12375  * This class also supports single and multi selection modes. <br>
12376  * Create a data model bound view:
12377  <pre><code>
12378  var store = new Roo.data.Store(...);
12379
12380  var view = new Roo.View({
12381     el : "my-element",
12382     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12383  
12384     singleSelect: true,
12385     selectedClass: "ydataview-selected",
12386     store: store
12387  });
12388
12389  // listen for node click?
12390  view.on("click", function(vw, index, node, e){
12391  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12392  });
12393
12394  // load XML data
12395  dataModel.load("foobar.xml");
12396  </code></pre>
12397  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12398  * <br><br>
12399  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12400  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12401  * 
12402  * Note: old style constructor is still suported (container, template, config)
12403  * 
12404  * @constructor
12405  * Create a new View
12406  * @param {Object} config The config object
12407  * 
12408  */
12409 Roo.View = function(config, depreciated_tpl, depreciated_config){
12410     
12411     this.parent = false;
12412     
12413     if (typeof(depreciated_tpl) == 'undefined') {
12414         // new way.. - universal constructor.
12415         Roo.apply(this, config);
12416         this.el  = Roo.get(this.el);
12417     } else {
12418         // old format..
12419         this.el  = Roo.get(config);
12420         this.tpl = depreciated_tpl;
12421         Roo.apply(this, depreciated_config);
12422     }
12423     this.wrapEl  = this.el.wrap().wrap();
12424     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12425     
12426     
12427     if(typeof(this.tpl) == "string"){
12428         this.tpl = new Roo.Template(this.tpl);
12429     } else {
12430         // support xtype ctors..
12431         this.tpl = new Roo.factory(this.tpl, Roo);
12432     }
12433     
12434     
12435     this.tpl.compile();
12436     
12437     /** @private */
12438     this.addEvents({
12439         /**
12440          * @event beforeclick
12441          * Fires before a click is processed. Returns false to cancel the default action.
12442          * @param {Roo.View} this
12443          * @param {Number} index The index of the target node
12444          * @param {HTMLElement} node The target node
12445          * @param {Roo.EventObject} e The raw event object
12446          */
12447             "beforeclick" : true,
12448         /**
12449          * @event click
12450          * Fires when a template node is clicked.
12451          * @param {Roo.View} this
12452          * @param {Number} index The index of the target node
12453          * @param {HTMLElement} node The target node
12454          * @param {Roo.EventObject} e The raw event object
12455          */
12456             "click" : true,
12457         /**
12458          * @event dblclick
12459          * Fires when a template node is double clicked.
12460          * @param {Roo.View} this
12461          * @param {Number} index The index of the target node
12462          * @param {HTMLElement} node The target node
12463          * @param {Roo.EventObject} e The raw event object
12464          */
12465             "dblclick" : true,
12466         /**
12467          * @event contextmenu
12468          * Fires when a template node is right clicked.
12469          * @param {Roo.View} this
12470          * @param {Number} index The index of the target node
12471          * @param {HTMLElement} node The target node
12472          * @param {Roo.EventObject} e The raw event object
12473          */
12474             "contextmenu" : true,
12475         /**
12476          * @event selectionchange
12477          * Fires when the selected nodes change.
12478          * @param {Roo.View} this
12479          * @param {Array} selections Array of the selected nodes
12480          */
12481             "selectionchange" : true,
12482     
12483         /**
12484          * @event beforeselect
12485          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12486          * @param {Roo.View} this
12487          * @param {HTMLElement} node The node to be selected
12488          * @param {Array} selections Array of currently selected nodes
12489          */
12490             "beforeselect" : true,
12491         /**
12492          * @event preparedata
12493          * Fires on every row to render, to allow you to change the data.
12494          * @param {Roo.View} this
12495          * @param {Object} data to be rendered (change this)
12496          */
12497           "preparedata" : true
12498           
12499           
12500         });
12501
12502
12503
12504     this.el.on({
12505         "click": this.onClick,
12506         "dblclick": this.onDblClick,
12507         "contextmenu": this.onContextMenu,
12508         scope:this
12509     });
12510
12511     this.selections = [];
12512     this.nodes = [];
12513     this.cmp = new Roo.CompositeElementLite([]);
12514     if(this.store){
12515         this.store = Roo.factory(this.store, Roo.data);
12516         this.setStore(this.store, true);
12517     }
12518     
12519     if ( this.footer && this.footer.xtype) {
12520            
12521          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12522         
12523         this.footer.dataSource = this.store
12524         this.footer.container = fctr;
12525         this.footer = Roo.factory(this.footer, Roo);
12526         fctr.insertFirst(this.el);
12527         
12528         // this is a bit insane - as the paging toolbar seems to detach the el..
12529 //        dom.parentNode.parentNode.parentNode
12530          // they get detached?
12531     }
12532     
12533     
12534     Roo.View.superclass.constructor.call(this);
12535     
12536     
12537 };
12538
12539 Roo.extend(Roo.View, Roo.util.Observable, {
12540     
12541      /**
12542      * @cfg {Roo.data.Store} store Data store to load data from.
12543      */
12544     store : false,
12545     
12546     /**
12547      * @cfg {String|Roo.Element} el The container element.
12548      */
12549     el : '',
12550     
12551     /**
12552      * @cfg {String|Roo.Template} tpl The template used by this View 
12553      */
12554     tpl : false,
12555     /**
12556      * @cfg {String} dataName the named area of the template to use as the data area
12557      *                          Works with domtemplates roo-name="name"
12558      */
12559     dataName: false,
12560     /**
12561      * @cfg {String} selectedClass The css class to add to selected nodes
12562      */
12563     selectedClass : "x-view-selected",
12564      /**
12565      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12566      */
12567     emptyText : "",
12568     
12569     /**
12570      * @cfg {String} text to display on mask (default Loading)
12571      */
12572     mask : false,
12573     /**
12574      * @cfg {Boolean} multiSelect Allow multiple selection
12575      */
12576     multiSelect : false,
12577     /**
12578      * @cfg {Boolean} singleSelect Allow single selection
12579      */
12580     singleSelect:  false,
12581     
12582     /**
12583      * @cfg {Boolean} toggleSelect - selecting 
12584      */
12585     toggleSelect : false,
12586     
12587     /**
12588      * @cfg {Boolean} tickable - selecting 
12589      */
12590     tickable : false,
12591     
12592     /**
12593      * Returns the element this view is bound to.
12594      * @return {Roo.Element}
12595      */
12596     getEl : function(){
12597         return this.wrapEl;
12598     },
12599     
12600     
12601
12602     /**
12603      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12604      */
12605     refresh : function(){
12606         //Roo.log('refresh');
12607         var t = this.tpl;
12608         
12609         // if we are using something like 'domtemplate', then
12610         // the what gets used is:
12611         // t.applySubtemplate(NAME, data, wrapping data..)
12612         // the outer template then get' applied with
12613         //     the store 'extra data'
12614         // and the body get's added to the
12615         //      roo-name="data" node?
12616         //      <span class='roo-tpl-{name}'></span> ?????
12617         
12618         
12619         
12620         this.clearSelections();
12621         this.el.update("");
12622         var html = [];
12623         var records = this.store.getRange();
12624         if(records.length < 1) {
12625             
12626             // is this valid??  = should it render a template??
12627             
12628             this.el.update(this.emptyText);
12629             return;
12630         }
12631         var el = this.el;
12632         if (this.dataName) {
12633             this.el.update(t.apply(this.store.meta)); //????
12634             el = this.el.child('.roo-tpl-' + this.dataName);
12635         }
12636         
12637         for(var i = 0, len = records.length; i < len; i++){
12638             var data = this.prepareData(records[i].data, i, records[i]);
12639             this.fireEvent("preparedata", this, data, i, records[i]);
12640             
12641             var d = Roo.apply({}, data);
12642             
12643             if(this.tickable){
12644                 Roo.apply(d, {'roo-id' : Roo.id()});
12645                 
12646                 var _this = this;
12647             
12648                 Roo.each(this.parent.item, function(item){
12649                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12650                         return;
12651                     }
12652                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12653                 });
12654             }
12655             
12656             html[html.length] = Roo.util.Format.trim(
12657                 this.dataName ?
12658                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12659                     t.apply(d)
12660             );
12661         }
12662         
12663         
12664         
12665         el.update(html.join(""));
12666         this.nodes = el.dom.childNodes;
12667         this.updateIndexes(0);
12668     },
12669     
12670
12671     /**
12672      * Function to override to reformat the data that is sent to
12673      * the template for each node.
12674      * DEPRICATED - use the preparedata event handler.
12675      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12676      * a JSON object for an UpdateManager bound view).
12677      */
12678     prepareData : function(data, index, record)
12679     {
12680         this.fireEvent("preparedata", this, data, index, record);
12681         return data;
12682     },
12683
12684     onUpdate : function(ds, record){
12685         // Roo.log('on update');   
12686         this.clearSelections();
12687         var index = this.store.indexOf(record);
12688         var n = this.nodes[index];
12689         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12690         n.parentNode.removeChild(n);
12691         this.updateIndexes(index, index);
12692     },
12693
12694     
12695     
12696 // --------- FIXME     
12697     onAdd : function(ds, records, index)
12698     {
12699         //Roo.log(['on Add', ds, records, index] );        
12700         this.clearSelections();
12701         if(this.nodes.length == 0){
12702             this.refresh();
12703             return;
12704         }
12705         var n = this.nodes[index];
12706         for(var i = 0, len = records.length; i < len; i++){
12707             var d = this.prepareData(records[i].data, i, records[i]);
12708             if(n){
12709                 this.tpl.insertBefore(n, d);
12710             }else{
12711                 
12712                 this.tpl.append(this.el, d);
12713             }
12714         }
12715         this.updateIndexes(index);
12716     },
12717
12718     onRemove : function(ds, record, index){
12719        // Roo.log('onRemove');
12720         this.clearSelections();
12721         var el = this.dataName  ?
12722             this.el.child('.roo-tpl-' + this.dataName) :
12723             this.el; 
12724         
12725         el.dom.removeChild(this.nodes[index]);
12726         this.updateIndexes(index);
12727     },
12728
12729     /**
12730      * Refresh an individual node.
12731      * @param {Number} index
12732      */
12733     refreshNode : function(index){
12734         this.onUpdate(this.store, this.store.getAt(index));
12735     },
12736
12737     updateIndexes : function(startIndex, endIndex){
12738         var ns = this.nodes;
12739         startIndex = startIndex || 0;
12740         endIndex = endIndex || ns.length - 1;
12741         for(var i = startIndex; i <= endIndex; i++){
12742             ns[i].nodeIndex = i;
12743         }
12744     },
12745
12746     /**
12747      * Changes the data store this view uses and refresh the view.
12748      * @param {Store} store
12749      */
12750     setStore : function(store, initial){
12751         if(!initial && this.store){
12752             this.store.un("datachanged", this.refresh);
12753             this.store.un("add", this.onAdd);
12754             this.store.un("remove", this.onRemove);
12755             this.store.un("update", this.onUpdate);
12756             this.store.un("clear", this.refresh);
12757             this.store.un("beforeload", this.onBeforeLoad);
12758             this.store.un("load", this.onLoad);
12759             this.store.un("loadexception", this.onLoad);
12760         }
12761         if(store){
12762           
12763             store.on("datachanged", this.refresh, this);
12764             store.on("add", this.onAdd, this);
12765             store.on("remove", this.onRemove, this);
12766             store.on("update", this.onUpdate, this);
12767             store.on("clear", this.refresh, this);
12768             store.on("beforeload", this.onBeforeLoad, this);
12769             store.on("load", this.onLoad, this);
12770             store.on("loadexception", this.onLoad, this);
12771         }
12772         
12773         if(store){
12774             this.refresh();
12775         }
12776     },
12777     /**
12778      * onbeforeLoad - masks the loading area.
12779      *
12780      */
12781     onBeforeLoad : function(store,opts)
12782     {
12783          //Roo.log('onBeforeLoad');   
12784         if (!opts.add) {
12785             this.el.update("");
12786         }
12787         this.el.mask(this.mask ? this.mask : "Loading" ); 
12788     },
12789     onLoad : function ()
12790     {
12791         this.el.unmask();
12792     },
12793     
12794
12795     /**
12796      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12797      * @param {HTMLElement} node
12798      * @return {HTMLElement} The template node
12799      */
12800     findItemFromChild : function(node){
12801         var el = this.dataName  ?
12802             this.el.child('.roo-tpl-' + this.dataName,true) :
12803             this.el.dom; 
12804         
12805         if(!node || node.parentNode == el){
12806                     return node;
12807             }
12808             var p = node.parentNode;
12809             while(p && p != el){
12810             if(p.parentNode == el){
12811                 return p;
12812             }
12813             p = p.parentNode;
12814         }
12815             return null;
12816     },
12817
12818     /** @ignore */
12819     onClick : function(e){
12820         var item = this.findItemFromChild(e.getTarget());
12821         if(item){
12822             var index = this.indexOf(item);
12823             if(this.onItemClick(item, index, e) !== false){
12824                 this.fireEvent("click", this, index, item, e);
12825             }
12826         }else{
12827             this.clearSelections();
12828         }
12829     },
12830
12831     /** @ignore */
12832     onContextMenu : function(e){
12833         var item = this.findItemFromChild(e.getTarget());
12834         if(item){
12835             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12836         }
12837     },
12838
12839     /** @ignore */
12840     onDblClick : function(e){
12841         var item = this.findItemFromChild(e.getTarget());
12842         if(item){
12843             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12844         }
12845     },
12846
12847     onItemClick : function(item, index, e)
12848     {
12849         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12850             return false;
12851         }
12852         if (this.toggleSelect) {
12853             var m = this.isSelected(item) ? 'unselect' : 'select';
12854             //Roo.log(m);
12855             var _t = this;
12856             _t[m](item, true, false);
12857             return true;
12858         }
12859         if(this.multiSelect || this.singleSelect){
12860             if(this.multiSelect && e.shiftKey && this.lastSelection){
12861                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12862             }else{
12863                 this.select(item, this.multiSelect && e.ctrlKey);
12864                 this.lastSelection = item;
12865             }
12866             
12867             if(!this.tickable){
12868                 e.preventDefault();
12869             }
12870             
12871         }
12872         return true;
12873     },
12874
12875     /**
12876      * Get the number of selected nodes.
12877      * @return {Number}
12878      */
12879     getSelectionCount : function(){
12880         return this.selections.length;
12881     },
12882
12883     /**
12884      * Get the currently selected nodes.
12885      * @return {Array} An array of HTMLElements
12886      */
12887     getSelectedNodes : function(){
12888         return this.selections;
12889     },
12890
12891     /**
12892      * Get the indexes of the selected nodes.
12893      * @return {Array}
12894      */
12895     getSelectedIndexes : function(){
12896         var indexes = [], s = this.selections;
12897         for(var i = 0, len = s.length; i < len; i++){
12898             indexes.push(s[i].nodeIndex);
12899         }
12900         return indexes;
12901     },
12902
12903     /**
12904      * Clear all selections
12905      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12906      */
12907     clearSelections : function(suppressEvent){
12908         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12909             this.cmp.elements = this.selections;
12910             this.cmp.removeClass(this.selectedClass);
12911             this.selections = [];
12912             if(!suppressEvent){
12913                 this.fireEvent("selectionchange", this, this.selections);
12914             }
12915         }
12916     },
12917
12918     /**
12919      * Returns true if the passed node is selected
12920      * @param {HTMLElement/Number} node The node or node index
12921      * @return {Boolean}
12922      */
12923     isSelected : function(node){
12924         var s = this.selections;
12925         if(s.length < 1){
12926             return false;
12927         }
12928         node = this.getNode(node);
12929         return s.indexOf(node) !== -1;
12930     },
12931
12932     /**
12933      * Selects nodes.
12934      * @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
12935      * @param {Boolean} keepExisting (optional) true to keep existing selections
12936      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12937      */
12938     select : function(nodeInfo, keepExisting, suppressEvent){
12939         if(nodeInfo instanceof Array){
12940             if(!keepExisting){
12941                 this.clearSelections(true);
12942             }
12943             for(var i = 0, len = nodeInfo.length; i < len; i++){
12944                 this.select(nodeInfo[i], true, true);
12945             }
12946             return;
12947         } 
12948         var node = this.getNode(nodeInfo);
12949         if(!node || this.isSelected(node)){
12950             return; // already selected.
12951         }
12952         if(!keepExisting){
12953             this.clearSelections(true);
12954         }
12955         
12956         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12957             Roo.fly(node).addClass(this.selectedClass);
12958             this.selections.push(node);
12959             if(!suppressEvent){
12960                 this.fireEvent("selectionchange", this, this.selections);
12961             }
12962         }
12963         
12964         
12965     },
12966       /**
12967      * Unselects nodes.
12968      * @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
12969      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12970      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12971      */
12972     unselect : function(nodeInfo, keepExisting, suppressEvent)
12973     {
12974         if(nodeInfo instanceof Array){
12975             Roo.each(this.selections, function(s) {
12976                 this.unselect(s, nodeInfo);
12977             }, this);
12978             return;
12979         }
12980         var node = this.getNode(nodeInfo);
12981         if(!node || !this.isSelected(node)){
12982             //Roo.log("not selected");
12983             return; // not selected.
12984         }
12985         // fireevent???
12986         var ns = [];
12987         Roo.each(this.selections, function(s) {
12988             if (s == node ) {
12989                 Roo.fly(node).removeClass(this.selectedClass);
12990
12991                 return;
12992             }
12993             ns.push(s);
12994         },this);
12995         
12996         this.selections= ns;
12997         this.fireEvent("selectionchange", this, this.selections);
12998     },
12999
13000     /**
13001      * Gets a template node.
13002      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13003      * @return {HTMLElement} The node or null if it wasn't found
13004      */
13005     getNode : function(nodeInfo){
13006         if(typeof nodeInfo == "string"){
13007             return document.getElementById(nodeInfo);
13008         }else if(typeof nodeInfo == "number"){
13009             return this.nodes[nodeInfo];
13010         }
13011         return nodeInfo;
13012     },
13013
13014     /**
13015      * Gets a range template nodes.
13016      * @param {Number} startIndex
13017      * @param {Number} endIndex
13018      * @return {Array} An array of nodes
13019      */
13020     getNodes : function(start, end){
13021         var ns = this.nodes;
13022         start = start || 0;
13023         end = typeof end == "undefined" ? ns.length - 1 : end;
13024         var nodes = [];
13025         if(start <= end){
13026             for(var i = start; i <= end; i++){
13027                 nodes.push(ns[i]);
13028             }
13029         } else{
13030             for(var i = start; i >= end; i--){
13031                 nodes.push(ns[i]);
13032             }
13033         }
13034         return nodes;
13035     },
13036
13037     /**
13038      * Finds the index of the passed node
13039      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13040      * @return {Number} The index of the node or -1
13041      */
13042     indexOf : function(node){
13043         node = this.getNode(node);
13044         if(typeof node.nodeIndex == "number"){
13045             return node.nodeIndex;
13046         }
13047         var ns = this.nodes;
13048         for(var i = 0, len = ns.length; i < len; i++){
13049             if(ns[i] == node){
13050                 return i;
13051             }
13052         }
13053         return -1;
13054     }
13055 });
13056 /*
13057  * - LGPL
13058  *
13059  * based on jquery fullcalendar
13060  * 
13061  */
13062
13063 Roo.bootstrap = Roo.bootstrap || {};
13064 /**
13065  * @class Roo.bootstrap.Calendar
13066  * @extends Roo.bootstrap.Component
13067  * Bootstrap Calendar class
13068  * @cfg {Boolean} loadMask (true|false) default false
13069  * @cfg {Object} header generate the user specific header of the calendar, default false
13070
13071  * @constructor
13072  * Create a new Container
13073  * @param {Object} config The config object
13074  */
13075
13076
13077
13078 Roo.bootstrap.Calendar = function(config){
13079     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13080      this.addEvents({
13081         /**
13082              * @event select
13083              * Fires when a date is selected
13084              * @param {DatePicker} this
13085              * @param {Date} date The selected date
13086              */
13087         'select': true,
13088         /**
13089              * @event monthchange
13090              * Fires when the displayed month changes 
13091              * @param {DatePicker} this
13092              * @param {Date} date The selected month
13093              */
13094         'monthchange': true,
13095         /**
13096              * @event evententer
13097              * Fires when mouse over an event
13098              * @param {Calendar} this
13099              * @param {event} Event
13100              */
13101         'evententer': true,
13102         /**
13103              * @event eventleave
13104              * Fires when the mouse leaves an
13105              * @param {Calendar} this
13106              * @param {event}
13107              */
13108         'eventleave': true,
13109         /**
13110              * @event eventclick
13111              * Fires when the mouse click an
13112              * @param {Calendar} this
13113              * @param {event}
13114              */
13115         'eventclick': true
13116         
13117     });
13118
13119 };
13120
13121 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13122     
13123      /**
13124      * @cfg {Number} startDay
13125      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13126      */
13127     startDay : 0,
13128     
13129     loadMask : false,
13130     
13131     header : false,
13132       
13133     getAutoCreate : function(){
13134         
13135         
13136         var fc_button = function(name, corner, style, content ) {
13137             return Roo.apply({},{
13138                 tag : 'span',
13139                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13140                          (corner.length ?
13141                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13142                             ''
13143                         ),
13144                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13145                 unselectable: 'on'
13146             });
13147         };
13148         
13149         var header = {};
13150         
13151         if(!this.header){
13152             header = {
13153                 tag : 'table',
13154                 cls : 'fc-header',
13155                 style : 'width:100%',
13156                 cn : [
13157                     {
13158                         tag: 'tr',
13159                         cn : [
13160                             {
13161                                 tag : 'td',
13162                                 cls : 'fc-header-left',
13163                                 cn : [
13164                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13165                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13166                                     { tag: 'span', cls: 'fc-header-space' },
13167                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13168
13169
13170                                 ]
13171                             },
13172
13173                             {
13174                                 tag : 'td',
13175                                 cls : 'fc-header-center',
13176                                 cn : [
13177                                     {
13178                                         tag: 'span',
13179                                         cls: 'fc-header-title',
13180                                         cn : {
13181                                             tag: 'H2',
13182                                             html : 'month / year'
13183                                         }
13184                                     }
13185
13186                                 ]
13187                             },
13188                             {
13189                                 tag : 'td',
13190                                 cls : 'fc-header-right',
13191                                 cn : [
13192                               /*      fc_button('month', 'left', '', 'month' ),
13193                                     fc_button('week', '', '', 'week' ),
13194                                     fc_button('day', 'right', '', 'day' )
13195                                 */    
13196
13197                                 ]
13198                             }
13199
13200                         ]
13201                     }
13202                 ]
13203             };
13204         }
13205         
13206         header = this.header;
13207         
13208        
13209         var cal_heads = function() {
13210             var ret = [];
13211             // fixme - handle this.
13212             
13213             for (var i =0; i < Date.dayNames.length; i++) {
13214                 var d = Date.dayNames[i];
13215                 ret.push({
13216                     tag: 'th',
13217                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13218                     html : d.substring(0,3)
13219                 });
13220                 
13221             }
13222             ret[0].cls += ' fc-first';
13223             ret[6].cls += ' fc-last';
13224             return ret;
13225         };
13226         var cal_cell = function(n) {
13227             return  {
13228                 tag: 'td',
13229                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13230                 cn : [
13231                     {
13232                         cn : [
13233                             {
13234                                 cls: 'fc-day-number',
13235                                 html: 'D'
13236                             },
13237                             {
13238                                 cls: 'fc-day-content',
13239                              
13240                                 cn : [
13241                                      {
13242                                         style: 'position: relative;' // height: 17px;
13243                                     }
13244                                 ]
13245                             }
13246                             
13247                             
13248                         ]
13249                     }
13250                 ]
13251                 
13252             }
13253         };
13254         var cal_rows = function() {
13255             
13256             var ret = [];
13257             for (var r = 0; r < 6; r++) {
13258                 var row= {
13259                     tag : 'tr',
13260                     cls : 'fc-week',
13261                     cn : []
13262                 };
13263                 
13264                 for (var i =0; i < Date.dayNames.length; i++) {
13265                     var d = Date.dayNames[i];
13266                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13267
13268                 }
13269                 row.cn[0].cls+=' fc-first';
13270                 row.cn[0].cn[0].style = 'min-height:90px';
13271                 row.cn[6].cls+=' fc-last';
13272                 ret.push(row);
13273                 
13274             }
13275             ret[0].cls += ' fc-first';
13276             ret[4].cls += ' fc-prev-last';
13277             ret[5].cls += ' fc-last';
13278             return ret;
13279             
13280         };
13281         
13282         var cal_table = {
13283             tag: 'table',
13284             cls: 'fc-border-separate',
13285             style : 'width:100%',
13286             cellspacing  : 0,
13287             cn : [
13288                 { 
13289                     tag: 'thead',
13290                     cn : [
13291                         { 
13292                             tag: 'tr',
13293                             cls : 'fc-first fc-last',
13294                             cn : cal_heads()
13295                         }
13296                     ]
13297                 },
13298                 { 
13299                     tag: 'tbody',
13300                     cn : cal_rows()
13301                 }
13302                   
13303             ]
13304         };
13305          
13306          var cfg = {
13307             cls : 'fc fc-ltr',
13308             cn : [
13309                 header,
13310                 {
13311                     cls : 'fc-content',
13312                     style : "position: relative;",
13313                     cn : [
13314                         {
13315                             cls : 'fc-view fc-view-month fc-grid',
13316                             style : 'position: relative',
13317                             unselectable : 'on',
13318                             cn : [
13319                                 {
13320                                     cls : 'fc-event-container',
13321                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13322                                 },
13323                                 cal_table
13324                             ]
13325                         }
13326                     ]
13327     
13328                 }
13329            ] 
13330             
13331         };
13332         
13333          
13334         
13335         return cfg;
13336     },
13337     
13338     
13339     initEvents : function()
13340     {
13341         if(!this.store){
13342             throw "can not find store for calendar";
13343         }
13344         
13345         var mark = {
13346             tag: "div",
13347             cls:"x-dlg-mask",
13348             style: "text-align:center",
13349             cn: [
13350                 {
13351                     tag: "div",
13352                     style: "background-color:white;width:50%;margin:250 auto",
13353                     cn: [
13354                         {
13355                             tag: "img",
13356                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13357                         },
13358                         {
13359                             tag: "span",
13360                             html: "Loading"
13361                         }
13362                         
13363                     ]
13364                 }
13365             ]
13366         }
13367         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13368         
13369         var size = this.el.select('.fc-content', true).first().getSize();
13370         this.maskEl.setSize(size.width, size.height);
13371         this.maskEl.enableDisplayMode("block");
13372         if(!this.loadMask){
13373             this.maskEl.hide();
13374         }
13375         
13376         this.store = Roo.factory(this.store, Roo.data);
13377         this.store.on('load', this.onLoad, this);
13378         this.store.on('beforeload', this.onBeforeLoad, this);
13379         
13380         this.resize();
13381         
13382         this.cells = this.el.select('.fc-day',true);
13383         //Roo.log(this.cells);
13384         this.textNodes = this.el.query('.fc-day-number');
13385         this.cells.addClassOnOver('fc-state-hover');
13386         
13387         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13388         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13389         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13390         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13391         
13392         this.on('monthchange', this.onMonthChange, this);
13393         
13394         this.update(new Date().clearTime());
13395     },
13396     
13397     resize : function() {
13398         var sz  = this.el.getSize();
13399         
13400         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13401         this.el.select('.fc-day-content div',true).setHeight(34);
13402     },
13403     
13404     
13405     // private
13406     showPrevMonth : function(e){
13407         this.update(this.activeDate.add("mo", -1));
13408     },
13409     showToday : function(e){
13410         this.update(new Date().clearTime());
13411     },
13412     // private
13413     showNextMonth : function(e){
13414         this.update(this.activeDate.add("mo", 1));
13415     },
13416
13417     // private
13418     showPrevYear : function(){
13419         this.update(this.activeDate.add("y", -1));
13420     },
13421
13422     // private
13423     showNextYear : function(){
13424         this.update(this.activeDate.add("y", 1));
13425     },
13426
13427     
13428    // private
13429     update : function(date)
13430     {
13431         var vd = this.activeDate;
13432         this.activeDate = date;
13433 //        if(vd && this.el){
13434 //            var t = date.getTime();
13435 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13436 //                Roo.log('using add remove');
13437 //                
13438 //                this.fireEvent('monthchange', this, date);
13439 //                
13440 //                this.cells.removeClass("fc-state-highlight");
13441 //                this.cells.each(function(c){
13442 //                   if(c.dateValue == t){
13443 //                       c.addClass("fc-state-highlight");
13444 //                       setTimeout(function(){
13445 //                            try{c.dom.firstChild.focus();}catch(e){}
13446 //                       }, 50);
13447 //                       return false;
13448 //                   }
13449 //                   return true;
13450 //                });
13451 //                return;
13452 //            }
13453 //        }
13454         
13455         var days = date.getDaysInMonth();
13456         
13457         var firstOfMonth = date.getFirstDateOfMonth();
13458         var startingPos = firstOfMonth.getDay()-this.startDay;
13459         
13460         if(startingPos < this.startDay){
13461             startingPos += 7;
13462         }
13463         
13464         var pm = date.add(Date.MONTH, -1);
13465         var prevStart = pm.getDaysInMonth()-startingPos;
13466 //        
13467         this.cells = this.el.select('.fc-day',true);
13468         this.textNodes = this.el.query('.fc-day-number');
13469         this.cells.addClassOnOver('fc-state-hover');
13470         
13471         var cells = this.cells.elements;
13472         var textEls = this.textNodes;
13473         
13474         Roo.each(cells, function(cell){
13475             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13476         });
13477         
13478         days += startingPos;
13479
13480         // convert everything to numbers so it's fast
13481         var day = 86400000;
13482         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13483         //Roo.log(d);
13484         //Roo.log(pm);
13485         //Roo.log(prevStart);
13486         
13487         var today = new Date().clearTime().getTime();
13488         var sel = date.clearTime().getTime();
13489         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13490         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13491         var ddMatch = this.disabledDatesRE;
13492         var ddText = this.disabledDatesText;
13493         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13494         var ddaysText = this.disabledDaysText;
13495         var format = this.format;
13496         
13497         var setCellClass = function(cal, cell){
13498             cell.row = 0;
13499             cell.events = [];
13500             cell.more = [];
13501             //Roo.log('set Cell Class');
13502             cell.title = "";
13503             var t = d.getTime();
13504             
13505             //Roo.log(d);
13506             
13507             cell.dateValue = t;
13508             if(t == today){
13509                 cell.className += " fc-today";
13510                 cell.className += " fc-state-highlight";
13511                 cell.title = cal.todayText;
13512             }
13513             if(t == sel){
13514                 // disable highlight in other month..
13515                 //cell.className += " fc-state-highlight";
13516                 
13517             }
13518             // disabling
13519             if(t < min) {
13520                 cell.className = " fc-state-disabled";
13521                 cell.title = cal.minText;
13522                 return;
13523             }
13524             if(t > max) {
13525                 cell.className = " fc-state-disabled";
13526                 cell.title = cal.maxText;
13527                 return;
13528             }
13529             if(ddays){
13530                 if(ddays.indexOf(d.getDay()) != -1){
13531                     cell.title = ddaysText;
13532                     cell.className = " fc-state-disabled";
13533                 }
13534             }
13535             if(ddMatch && format){
13536                 var fvalue = d.dateFormat(format);
13537                 if(ddMatch.test(fvalue)){
13538                     cell.title = ddText.replace("%0", fvalue);
13539                     cell.className = " fc-state-disabled";
13540                 }
13541             }
13542             
13543             if (!cell.initialClassName) {
13544                 cell.initialClassName = cell.dom.className;
13545             }
13546             
13547             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13548         };
13549
13550         var i = 0;
13551         
13552         for(; i < startingPos; i++) {
13553             textEls[i].innerHTML = (++prevStart);
13554             d.setDate(d.getDate()+1);
13555             
13556             cells[i].className = "fc-past fc-other-month";
13557             setCellClass(this, cells[i]);
13558         }
13559         
13560         var intDay = 0;
13561         
13562         for(; i < days; i++){
13563             intDay = i - startingPos + 1;
13564             textEls[i].innerHTML = (intDay);
13565             d.setDate(d.getDate()+1);
13566             
13567             cells[i].className = ''; // "x-date-active";
13568             setCellClass(this, cells[i]);
13569         }
13570         var extraDays = 0;
13571         
13572         for(; i < 42; i++) {
13573             textEls[i].innerHTML = (++extraDays);
13574             d.setDate(d.getDate()+1);
13575             
13576             cells[i].className = "fc-future fc-other-month";
13577             setCellClass(this, cells[i]);
13578         }
13579         
13580         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13581         
13582         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13583         
13584         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13585         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13586         
13587         if(totalRows != 6){
13588             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13589             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13590         }
13591         
13592         this.fireEvent('monthchange', this, date);
13593         
13594         
13595         /*
13596         if(!this.internalRender){
13597             var main = this.el.dom.firstChild;
13598             var w = main.offsetWidth;
13599             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13600             Roo.fly(main).setWidth(w);
13601             this.internalRender = true;
13602             // opera does not respect the auto grow header center column
13603             // then, after it gets a width opera refuses to recalculate
13604             // without a second pass
13605             if(Roo.isOpera && !this.secondPass){
13606                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13607                 this.secondPass = true;
13608                 this.update.defer(10, this, [date]);
13609             }
13610         }
13611         */
13612         
13613     },
13614     
13615     findCell : function(dt) {
13616         dt = dt.clearTime().getTime();
13617         var ret = false;
13618         this.cells.each(function(c){
13619             //Roo.log("check " +c.dateValue + '?=' + dt);
13620             if(c.dateValue == dt){
13621                 ret = c;
13622                 return false;
13623             }
13624             return true;
13625         });
13626         
13627         return ret;
13628     },
13629     
13630     findCells : function(ev) {
13631         var s = ev.start.clone().clearTime().getTime();
13632        // Roo.log(s);
13633         var e= ev.end.clone().clearTime().getTime();
13634        // Roo.log(e);
13635         var ret = [];
13636         this.cells.each(function(c){
13637              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13638             
13639             if(c.dateValue > e){
13640                 return ;
13641             }
13642             if(c.dateValue < s){
13643                 return ;
13644             }
13645             ret.push(c);
13646         });
13647         
13648         return ret;    
13649     },
13650     
13651 //    findBestRow: function(cells)
13652 //    {
13653 //        var ret = 0;
13654 //        
13655 //        for (var i =0 ; i < cells.length;i++) {
13656 //            ret  = Math.max(cells[i].rows || 0,ret);
13657 //        }
13658 //        return ret;
13659 //        
13660 //    },
13661     
13662     
13663     addItem : function(ev)
13664     {
13665         // look for vertical location slot in
13666         var cells = this.findCells(ev);
13667         
13668 //        ev.row = this.findBestRow(cells);
13669         
13670         // work out the location.
13671         
13672         var crow = false;
13673         var rows = [];
13674         for(var i =0; i < cells.length; i++) {
13675             
13676             cells[i].row = cells[0].row;
13677             
13678             if(i == 0){
13679                 cells[i].row = cells[i].row + 1;
13680             }
13681             
13682             if (!crow) {
13683                 crow = {
13684                     start : cells[i],
13685                     end :  cells[i]
13686                 };
13687                 continue;
13688             }
13689             if (crow.start.getY() == cells[i].getY()) {
13690                 // on same row.
13691                 crow.end = cells[i];
13692                 continue;
13693             }
13694             // different row.
13695             rows.push(crow);
13696             crow = {
13697                 start: cells[i],
13698                 end : cells[i]
13699             };
13700             
13701         }
13702         
13703         rows.push(crow);
13704         ev.els = [];
13705         ev.rows = rows;
13706         ev.cells = cells;
13707         
13708         cells[0].events.push(ev);
13709         
13710         this.calevents.push(ev);
13711     },
13712     
13713     clearEvents: function() {
13714         
13715         if(!this.calevents){
13716             return;
13717         }
13718         
13719         Roo.each(this.cells.elements, function(c){
13720             c.row = 0;
13721             c.events = [];
13722             c.more = [];
13723         });
13724         
13725         Roo.each(this.calevents, function(e) {
13726             Roo.each(e.els, function(el) {
13727                 el.un('mouseenter' ,this.onEventEnter, this);
13728                 el.un('mouseleave' ,this.onEventLeave, this);
13729                 el.remove();
13730             },this);
13731         },this);
13732         
13733         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13734             e.remove();
13735         });
13736         
13737     },
13738     
13739     renderEvents: function()
13740     {   
13741         var _this = this;
13742         
13743         this.cells.each(function(c) {
13744             
13745             if(c.row < 5){
13746                 return;
13747             }
13748             
13749             var ev = c.events;
13750             
13751             var r = 4;
13752             if(c.row != c.events.length){
13753                 r = 4 - (4 - (c.row - c.events.length));
13754             }
13755             
13756             c.events = ev.slice(0, r);
13757             c.more = ev.slice(r);
13758             
13759             if(c.more.length && c.more.length == 1){
13760                 c.events.push(c.more.pop());
13761             }
13762             
13763             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13764             
13765         });
13766             
13767         this.cells.each(function(c) {
13768             
13769             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13770             
13771             
13772             for (var e = 0; e < c.events.length; e++){
13773                 var ev = c.events[e];
13774                 var rows = ev.rows;
13775                 
13776                 for(var i = 0; i < rows.length; i++) {
13777                 
13778                     // how many rows should it span..
13779
13780                     var  cfg = {
13781                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13782                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13783
13784                         unselectable : "on",
13785                         cn : [
13786                             {
13787                                 cls: 'fc-event-inner',
13788                                 cn : [
13789     //                                {
13790     //                                  tag:'span',
13791     //                                  cls: 'fc-event-time',
13792     //                                  html : cells.length > 1 ? '' : ev.time
13793     //                                },
13794                                     {
13795                                       tag:'span',
13796                                       cls: 'fc-event-title',
13797                                       html : String.format('{0}', ev.title)
13798                                     }
13799
13800
13801                                 ]
13802                             },
13803                             {
13804                                 cls: 'ui-resizable-handle ui-resizable-e',
13805                                 html : '&nbsp;&nbsp;&nbsp'
13806                             }
13807
13808                         ]
13809                     };
13810
13811                     if (i == 0) {
13812                         cfg.cls += ' fc-event-start';
13813                     }
13814                     if ((i+1) == rows.length) {
13815                         cfg.cls += ' fc-event-end';
13816                     }
13817
13818                     var ctr = _this.el.select('.fc-event-container',true).first();
13819                     var cg = ctr.createChild(cfg);
13820
13821                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13822                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13823
13824                     var r = (c.more.length) ? 1 : 0;
13825                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13826                     cg.setWidth(ebox.right - sbox.x -2);
13827
13828                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13829                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13830                     cg.on('click', _this.onEventClick, _this, ev);
13831
13832                     ev.els.push(cg);
13833                     
13834                 }
13835                 
13836             }
13837             
13838             
13839             if(c.more.length){
13840                 var  cfg = {
13841                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13842                     style : 'position: absolute',
13843                     unselectable : "on",
13844                     cn : [
13845                         {
13846                             cls: 'fc-event-inner',
13847                             cn : [
13848                                 {
13849                                   tag:'span',
13850                                   cls: 'fc-event-title',
13851                                   html : 'More'
13852                                 }
13853
13854
13855                             ]
13856                         },
13857                         {
13858                             cls: 'ui-resizable-handle ui-resizable-e',
13859                             html : '&nbsp;&nbsp;&nbsp'
13860                         }
13861
13862                     ]
13863                 };
13864
13865                 var ctr = _this.el.select('.fc-event-container',true).first();
13866                 var cg = ctr.createChild(cfg);
13867
13868                 var sbox = c.select('.fc-day-content',true).first().getBox();
13869                 var ebox = c.select('.fc-day-content',true).first().getBox();
13870                 //Roo.log(cg);
13871                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13872                 cg.setWidth(ebox.right - sbox.x -2);
13873
13874                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13875                 
13876             }
13877             
13878         });
13879         
13880         
13881         
13882     },
13883     
13884     onEventEnter: function (e, el,event,d) {
13885         this.fireEvent('evententer', this, el, event);
13886     },
13887     
13888     onEventLeave: function (e, el,event,d) {
13889         this.fireEvent('eventleave', this, el, event);
13890     },
13891     
13892     onEventClick: function (e, el,event,d) {
13893         this.fireEvent('eventclick', this, el, event);
13894     },
13895     
13896     onMonthChange: function () {
13897         this.store.load();
13898     },
13899     
13900     onMoreEventClick: function(e, el, more)
13901     {
13902         var _this = this;
13903         
13904         this.calpopover.placement = 'right';
13905         this.calpopover.setTitle('More');
13906         
13907         this.calpopover.setContent('');
13908         
13909         var ctr = this.calpopover.el.select('.popover-content', true).first();
13910         
13911         Roo.each(more, function(m){
13912             var cfg = {
13913                 cls : 'fc-event-hori fc-event-draggable',
13914                 html : m.title
13915             }
13916             var cg = ctr.createChild(cfg);
13917             
13918             cg.on('click', _this.onEventClick, _this, m);
13919         });
13920         
13921         this.calpopover.show(el);
13922         
13923         
13924     },
13925     
13926     onLoad: function () 
13927     {   
13928         this.calevents = [];
13929         var cal = this;
13930         
13931         if(this.store.getCount() > 0){
13932             this.store.data.each(function(d){
13933                cal.addItem({
13934                     id : d.data.id,
13935                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13936                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13937                     time : d.data.start_time,
13938                     title : d.data.title,
13939                     description : d.data.description,
13940                     venue : d.data.venue
13941                 });
13942             });
13943         }
13944         
13945         this.renderEvents();
13946         
13947         if(this.calevents.length && this.loadMask){
13948             this.maskEl.hide();
13949         }
13950     },
13951     
13952     onBeforeLoad: function()
13953     {
13954         this.clearEvents();
13955         if(this.loadMask){
13956             this.maskEl.show();
13957         }
13958     }
13959 });
13960
13961  
13962  /*
13963  * - LGPL
13964  *
13965  * element
13966  * 
13967  */
13968
13969 /**
13970  * @class Roo.bootstrap.Popover
13971  * @extends Roo.bootstrap.Component
13972  * Bootstrap Popover class
13973  * @cfg {String} html contents of the popover   (or false to use children..)
13974  * @cfg {String} title of popover (or false to hide)
13975  * @cfg {String} placement how it is placed
13976  * @cfg {String} trigger click || hover (or false to trigger manually)
13977  * @cfg {String} over what (parent or false to trigger manually.)
13978  * @cfg {Number} delay - delay before showing
13979  
13980  * @constructor
13981  * Create a new Popover
13982  * @param {Object} config The config object
13983  */
13984
13985 Roo.bootstrap.Popover = function(config){
13986     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13987 };
13988
13989 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13990     
13991     title: 'Fill in a title',
13992     html: false,
13993     
13994     placement : 'right',
13995     trigger : 'hover', // hover
13996     
13997     delay : 0,
13998     
13999     over: 'parent',
14000     
14001     can_build_overlaid : false,
14002     
14003     getChildContainer : function()
14004     {
14005         return this.el.select('.popover-content',true).first();
14006     },
14007     
14008     getAutoCreate : function(){
14009          Roo.log('make popover?');
14010         var cfg = {
14011            cls : 'popover roo-dynamic',
14012            style: 'display:block',
14013            cn : [
14014                 {
14015                     cls : 'arrow'
14016                 },
14017                 {
14018                     cls : 'popover-inner',
14019                     cn : [
14020                         {
14021                             tag: 'h3',
14022                             cls: 'popover-title',
14023                             html : this.title
14024                         },
14025                         {
14026                             cls : 'popover-content',
14027                             html : this.html
14028                         }
14029                     ]
14030                     
14031                 }
14032            ]
14033         };
14034         
14035         return cfg;
14036     },
14037     setTitle: function(str)
14038     {
14039         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14040     },
14041     setContent: function(str)
14042     {
14043         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14044     },
14045     // as it get's added to the bottom of the page.
14046     onRender : function(ct, position)
14047     {
14048         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14049         if(!this.el){
14050             var cfg = Roo.apply({},  this.getAutoCreate());
14051             cfg.id = Roo.id();
14052             
14053             if (this.cls) {
14054                 cfg.cls += ' ' + this.cls;
14055             }
14056             if (this.style) {
14057                 cfg.style = this.style;
14058             }
14059             Roo.log("adding to ")
14060             this.el = Roo.get(document.body).createChild(cfg, position);
14061             Roo.log(this.el);
14062         }
14063         this.initEvents();
14064     },
14065     
14066     initEvents : function()
14067     {
14068         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14069         this.el.enableDisplayMode('block');
14070         this.el.hide();
14071         if (this.over === false) {
14072             return; 
14073         }
14074         if (this.triggers === false) {
14075             return;
14076         }
14077         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14078         var triggers = this.trigger ? this.trigger.split(' ') : [];
14079         Roo.each(triggers, function(trigger) {
14080         
14081             if (trigger == 'click') {
14082                 on_el.on('click', this.toggle, this);
14083             } else if (trigger != 'manual') {
14084                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14085                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14086       
14087                 on_el.on(eventIn  ,this.enter, this);
14088                 on_el.on(eventOut, this.leave, this);
14089             }
14090         }, this);
14091         
14092     },
14093     
14094     
14095     // private
14096     timeout : null,
14097     hoverState : null,
14098     
14099     toggle : function () {
14100         this.hoverState == 'in' ? this.leave() : this.enter();
14101     },
14102     
14103     enter : function () {
14104        
14105     
14106         clearTimeout(this.timeout);
14107     
14108         this.hoverState = 'in';
14109     
14110         if (!this.delay || !this.delay.show) {
14111             this.show();
14112             return;
14113         }
14114         var _t = this;
14115         this.timeout = setTimeout(function () {
14116             if (_t.hoverState == 'in') {
14117                 _t.show();
14118             }
14119         }, this.delay.show)
14120     },
14121     leave : function() {
14122         clearTimeout(this.timeout);
14123     
14124         this.hoverState = 'out';
14125     
14126         if (!this.delay || !this.delay.hide) {
14127             this.hide();
14128             return;
14129         }
14130         var _t = this;
14131         this.timeout = setTimeout(function () {
14132             if (_t.hoverState == 'out') {
14133                 _t.hide();
14134             }
14135         }, this.delay.hide)
14136     },
14137     
14138     show : function (on_el)
14139     {
14140         if (!on_el) {
14141             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14142         }
14143         // set content.
14144         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14145         if (this.html !== false) {
14146             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14147         }
14148         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14149         if (!this.title.length) {
14150             this.el.select('.popover-title',true).hide();
14151         }
14152         
14153         var placement = typeof this.placement == 'function' ?
14154             this.placement.call(this, this.el, on_el) :
14155             this.placement;
14156             
14157         var autoToken = /\s?auto?\s?/i;
14158         var autoPlace = autoToken.test(placement);
14159         if (autoPlace) {
14160             placement = placement.replace(autoToken, '') || 'top';
14161         }
14162         
14163         //this.el.detach()
14164         //this.el.setXY([0,0]);
14165         this.el.show();
14166         this.el.dom.style.display='block';
14167         this.el.addClass(placement);
14168         
14169         //this.el.appendTo(on_el);
14170         
14171         var p = this.getPosition();
14172         var box = this.el.getBox();
14173         
14174         if (autoPlace) {
14175             // fixme..
14176         }
14177         var align = Roo.bootstrap.Popover.alignment[placement];
14178         this.el.alignTo(on_el, align[0],align[1]);
14179         //var arrow = this.el.select('.arrow',true).first();
14180         //arrow.set(align[2], 
14181         
14182         this.el.addClass('in');
14183         this.hoverState = null;
14184         
14185         if (this.el.hasClass('fade')) {
14186             // fade it?
14187         }
14188         
14189     },
14190     hide : function()
14191     {
14192         this.el.setXY([0,0]);
14193         this.el.removeClass('in');
14194         this.el.hide();
14195         
14196     }
14197     
14198 });
14199
14200 Roo.bootstrap.Popover.alignment = {
14201     'left' : ['r-l', [-10,0], 'right'],
14202     'right' : ['l-r', [10,0], 'left'],
14203     'bottom' : ['t-b', [0,10], 'top'],
14204     'top' : [ 'b-t', [0,-10], 'bottom']
14205 };
14206
14207  /*
14208  * - LGPL
14209  *
14210  * Progress
14211  * 
14212  */
14213
14214 /**
14215  * @class Roo.bootstrap.Progress
14216  * @extends Roo.bootstrap.Component
14217  * Bootstrap Progress class
14218  * @cfg {Boolean} striped striped of the progress bar
14219  * @cfg {Boolean} active animated of the progress bar
14220  * 
14221  * 
14222  * @constructor
14223  * Create a new Progress
14224  * @param {Object} config The config object
14225  */
14226
14227 Roo.bootstrap.Progress = function(config){
14228     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14229 };
14230
14231 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14232     
14233     striped : false,
14234     active: false,
14235     
14236     getAutoCreate : function(){
14237         var cfg = {
14238             tag: 'div',
14239             cls: 'progress'
14240         };
14241         
14242         
14243         if(this.striped){
14244             cfg.cls += ' progress-striped';
14245         }
14246       
14247         if(this.active){
14248             cfg.cls += ' active';
14249         }
14250         
14251         
14252         return cfg;
14253     }
14254    
14255 });
14256
14257  
14258
14259  /*
14260  * - LGPL
14261  *
14262  * ProgressBar
14263  * 
14264  */
14265
14266 /**
14267  * @class Roo.bootstrap.ProgressBar
14268  * @extends Roo.bootstrap.Component
14269  * Bootstrap ProgressBar class
14270  * @cfg {Number} aria_valuenow aria-value now
14271  * @cfg {Number} aria_valuemin aria-value min
14272  * @cfg {Number} aria_valuemax aria-value max
14273  * @cfg {String} label label for the progress bar
14274  * @cfg {String} panel (success | info | warning | danger )
14275  * @cfg {String} role role of the progress bar
14276  * @cfg {String} sr_only text
14277  * 
14278  * 
14279  * @constructor
14280  * Create a new ProgressBar
14281  * @param {Object} config The config object
14282  */
14283
14284 Roo.bootstrap.ProgressBar = function(config){
14285     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14286 };
14287
14288 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14289     
14290     aria_valuenow : 0,
14291     aria_valuemin : 0,
14292     aria_valuemax : 100,
14293     label : false,
14294     panel : false,
14295     role : false,
14296     sr_only: false,
14297     
14298     getAutoCreate : function()
14299     {
14300         
14301         var cfg = {
14302             tag: 'div',
14303             cls: 'progress-bar',
14304             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14305         };
14306         
14307         if(this.sr_only){
14308             cfg.cn = {
14309                 tag: 'span',
14310                 cls: 'sr-only',
14311                 html: this.sr_only
14312             }
14313         }
14314         
14315         if(this.role){
14316             cfg.role = this.role;
14317         }
14318         
14319         if(this.aria_valuenow){
14320             cfg['aria-valuenow'] = this.aria_valuenow;
14321         }
14322         
14323         if(this.aria_valuemin){
14324             cfg['aria-valuemin'] = this.aria_valuemin;
14325         }
14326         
14327         if(this.aria_valuemax){
14328             cfg['aria-valuemax'] = this.aria_valuemax;
14329         }
14330         
14331         if(this.label && !this.sr_only){
14332             cfg.html = this.label;
14333         }
14334         
14335         if(this.panel){
14336             cfg.cls += ' progress-bar-' + this.panel;
14337         }
14338         
14339         return cfg;
14340     },
14341     
14342     update : function(aria_valuenow)
14343     {
14344         this.aria_valuenow = aria_valuenow;
14345         
14346         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14347     }
14348    
14349 });
14350
14351  
14352
14353  /*
14354  * - LGPL
14355  *
14356  * column
14357  * 
14358  */
14359
14360 /**
14361  * @class Roo.bootstrap.TabGroup
14362  * @extends Roo.bootstrap.Column
14363  * Bootstrap Column class
14364  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14365  * @cfg {Boolean} carousel true to make the group behave like a carousel
14366  * 
14367  * @constructor
14368  * Create a new TabGroup
14369  * @param {Object} config The config object
14370  */
14371
14372 Roo.bootstrap.TabGroup = function(config){
14373     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14374     if (!this.navId) {
14375         this.navId = Roo.id();
14376     }
14377     this.tabs = [];
14378     Roo.bootstrap.TabGroup.register(this);
14379     
14380 };
14381
14382 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14383     
14384     carousel : false,
14385     transition : false,
14386      
14387     getAutoCreate : function()
14388     {
14389         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14390         
14391         cfg.cls += ' tab-content';
14392         
14393         if (this.carousel) {
14394             cfg.cls += ' carousel slide';
14395             cfg.cn = [{
14396                cls : 'carousel-inner'
14397             }]
14398         }
14399         
14400         
14401         return cfg;
14402     },
14403     getChildContainer : function()
14404     {
14405         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14406     },
14407     
14408     /**
14409     * register a Navigation item
14410     * @param {Roo.bootstrap.NavItem} the navitem to add
14411     */
14412     register : function(item)
14413     {
14414         this.tabs.push( item);
14415         item.navId = this.navId; // not really needed..
14416     
14417     },
14418     
14419     getActivePanel : function()
14420     {
14421         var r = false;
14422         Roo.each(this.tabs, function(t) {
14423             if (t.active) {
14424                 r = t;
14425                 return false;
14426             }
14427             return null;
14428         });
14429         return r;
14430         
14431     },
14432     getPanelByName : function(n)
14433     {
14434         var r = false;
14435         Roo.each(this.tabs, function(t) {
14436             if (t.tabId == n) {
14437                 r = t;
14438                 return false;
14439             }
14440             return null;
14441         });
14442         return r;
14443     },
14444     indexOfPanel : function(p)
14445     {
14446         var r = false;
14447         Roo.each(this.tabs, function(t,i) {
14448             if (t.tabId == p.tabId) {
14449                 r = i;
14450                 return false;
14451             }
14452             return null;
14453         });
14454         return r;
14455     },
14456     /**
14457      * show a specific panel
14458      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14459      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14460      */
14461     showPanel : function (pan)
14462     {
14463         
14464         if (typeof(pan) == 'number') {
14465             pan = this.tabs[pan];
14466         }
14467         if (typeof(pan) == 'string') {
14468             pan = this.getPanelByName(pan);
14469         }
14470         if (pan.tabId == this.getActivePanel().tabId) {
14471             return true;
14472         }
14473         var cur = this.getActivePanel();
14474         
14475         if (false === cur.fireEvent('beforedeactivate')) {
14476             return false;
14477         }
14478         
14479         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14480             
14481             this.transition = true;
14482             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14483             var lr = dir == 'next' ? 'left' : 'right';
14484             pan.el.addClass(dir); // or prev
14485             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14486             cur.el.addClass(lr); // or right
14487             pan.el.addClass(lr);
14488             
14489             var _this = this;
14490             cur.el.on('transitionend', function() {
14491                 Roo.log("trans end?");
14492                 
14493                 pan.el.removeClass([lr,dir]);
14494                 pan.setActive(true);
14495                 
14496                 cur.el.removeClass([lr]);
14497                 cur.setActive(false);
14498                 
14499                 _this.transition = false;
14500                 
14501             }, this, { single:  true } );
14502             return true;
14503         }
14504         
14505         cur.setActive(false);
14506         pan.setActive(true);
14507         return true;
14508         
14509     },
14510     showPanelNext : function()
14511     {
14512         var i = this.indexOfPanel(this.getActivePanel());
14513         if (i > this.tabs.length) {
14514             return;
14515         }
14516         this.showPanel(this.tabs[i+1]);
14517     },
14518     showPanelPrev : function()
14519     {
14520         var i = this.indexOfPanel(this.getActivePanel());
14521         if (i  < 1) {
14522             return;
14523         }
14524         this.showPanel(this.tabs[i-1]);
14525     }
14526     
14527     
14528   
14529 });
14530
14531  
14532
14533  
14534  
14535 Roo.apply(Roo.bootstrap.TabGroup, {
14536     
14537     groups: {},
14538      /**
14539     * register a Navigation Group
14540     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14541     */
14542     register : function(navgrp)
14543     {
14544         this.groups[navgrp.navId] = navgrp;
14545         
14546     },
14547     /**
14548     * fetch a Navigation Group based on the navigation ID
14549     * if one does not exist , it will get created.
14550     * @param {string} the navgroup to add
14551     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14552     */
14553     get: function(navId) {
14554         if (typeof(this.groups[navId]) == 'undefined') {
14555             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14556         }
14557         return this.groups[navId] ;
14558     }
14559     
14560     
14561     
14562 });
14563
14564  /*
14565  * - LGPL
14566  *
14567  * TabPanel
14568  * 
14569  */
14570
14571 /**
14572  * @class Roo.bootstrap.TabPanel
14573  * @extends Roo.bootstrap.Component
14574  * Bootstrap TabPanel class
14575  * @cfg {Boolean} active panel active
14576  * @cfg {String} html panel content
14577  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14578  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14579  * 
14580  * 
14581  * @constructor
14582  * Create a new TabPanel
14583  * @param {Object} config The config object
14584  */
14585
14586 Roo.bootstrap.TabPanel = function(config){
14587     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14588     this.addEvents({
14589         /**
14590              * @event changed
14591              * Fires when the active status changes
14592              * @param {Roo.bootstrap.TabPanel} this
14593              * @param {Boolean} state the new state
14594             
14595          */
14596         'changed': true,
14597         /**
14598              * @event beforedeactivate
14599              * Fires before a tab is de-activated - can be used to do validation on a form.
14600              * @param {Roo.bootstrap.TabPanel} this
14601              * @return {Boolean} false if there is an error
14602             
14603          */
14604         'beforedeactivate': true
14605      });
14606     
14607     this.tabId = this.tabId || Roo.id();
14608   
14609 };
14610
14611 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14612     
14613     active: false,
14614     html: false,
14615     tabId: false,
14616     navId : false,
14617     
14618     getAutoCreate : function(){
14619         var cfg = {
14620             tag: 'div',
14621             // item is needed for carousel - not sure if it has any effect otherwise
14622             cls: 'tab-pane item',
14623             html: this.html || ''
14624         };
14625         
14626         if(this.active){
14627             cfg.cls += ' active';
14628         }
14629         
14630         if(this.tabId){
14631             cfg.tabId = this.tabId;
14632         }
14633         
14634         
14635         return cfg;
14636     },
14637     
14638     initEvents:  function()
14639     {
14640         Roo.log('-------- init events on tab panel ---------');
14641         
14642         var p = this.parent();
14643         this.navId = this.navId || p.navId;
14644         
14645         if (typeof(this.navId) != 'undefined') {
14646             // not really needed.. but just in case.. parent should be a NavGroup.
14647             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14648             Roo.log(['register', tg, this]);
14649             tg.register(this);
14650         }
14651     },
14652     
14653     
14654     onRender : function(ct, position)
14655     {
14656        // Roo.log("Call onRender: " + this.xtype);
14657         
14658         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14659         
14660         
14661         
14662         
14663         
14664     },
14665     
14666     setActive: function(state)
14667     {
14668         Roo.log("panel - set active " + this.tabId + "=" + state);
14669         
14670         this.active = state;
14671         if (!state) {
14672             this.el.removeClass('active');
14673             
14674         } else  if (!this.el.hasClass('active')) {
14675             this.el.addClass('active');
14676         }
14677         this.fireEvent('changed', this, state);
14678     }
14679     
14680     
14681 });
14682  
14683
14684  
14685
14686  /*
14687  * - LGPL
14688  *
14689  * DateField
14690  * 
14691  */
14692
14693 /**
14694  * @class Roo.bootstrap.DateField
14695  * @extends Roo.bootstrap.Input
14696  * Bootstrap DateField class
14697  * @cfg {Number} weekStart default 0
14698  * @cfg {String} viewMode default empty, (months|years)
14699  * @cfg {String} minViewMode default empty, (months|years)
14700  * @cfg {Number} startDate default -Infinity
14701  * @cfg {Number} endDate default Infinity
14702  * @cfg {Boolean} todayHighlight default false
14703  * @cfg {Boolean} todayBtn default false
14704  * @cfg {Boolean} calendarWeeks default false
14705  * @cfg {Object} daysOfWeekDisabled default empty
14706  * @cfg {Boolean} singleMode default false (true | false)
14707  * 
14708  * @cfg {Boolean} keyboardNavigation default true
14709  * @cfg {String} language default en
14710  * 
14711  * @constructor
14712  * Create a new DateField
14713  * @param {Object} config The config object
14714  */
14715
14716 Roo.bootstrap.DateField = function(config){
14717     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14718      this.addEvents({
14719             /**
14720              * @event show
14721              * Fires when this field show.
14722              * @param {Roo.bootstrap.DateField} this
14723              * @param {Mixed} date The date value
14724              */
14725             show : true,
14726             /**
14727              * @event show
14728              * Fires when this field hide.
14729              * @param {Roo.bootstrap.DateField} this
14730              * @param {Mixed} date The date value
14731              */
14732             hide : true,
14733             /**
14734              * @event select
14735              * Fires when select a date.
14736              * @param {Roo.bootstrap.DateField} this
14737              * @param {Mixed} date The date value
14738              */
14739             select : true
14740         });
14741 };
14742
14743 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14744     
14745     /**
14746      * @cfg {String} format
14747      * The default date format string which can be overriden for localization support.  The format must be
14748      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14749      */
14750     format : "m/d/y",
14751     /**
14752      * @cfg {String} altFormats
14753      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14754      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14755      */
14756     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14757     
14758     weekStart : 0,
14759     
14760     viewMode : '',
14761     
14762     minViewMode : '',
14763     
14764     todayHighlight : false,
14765     
14766     todayBtn: false,
14767     
14768     language: 'en',
14769     
14770     keyboardNavigation: true,
14771     
14772     calendarWeeks: false,
14773     
14774     startDate: -Infinity,
14775     
14776     endDate: Infinity,
14777     
14778     daysOfWeekDisabled: [],
14779     
14780     _events: [],
14781     
14782     singleMode : false,
14783     
14784     UTCDate: function()
14785     {
14786         return new Date(Date.UTC.apply(Date, arguments));
14787     },
14788     
14789     UTCToday: function()
14790     {
14791         var today = new Date();
14792         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14793     },
14794     
14795     getDate: function() {
14796             var d = this.getUTCDate();
14797             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14798     },
14799     
14800     getUTCDate: function() {
14801             return this.date;
14802     },
14803     
14804     setDate: function(d) {
14805             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14806     },
14807     
14808     setUTCDate: function(d) {
14809             this.date = d;
14810             this.setValue(this.formatDate(this.date));
14811     },
14812         
14813     onRender: function(ct, position)
14814     {
14815         
14816         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14817         
14818         this.language = this.language || 'en';
14819         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14820         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14821         
14822         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14823         this.format = this.format || 'm/d/y';
14824         this.isInline = false;
14825         this.isInput = true;
14826         this.component = this.el.select('.add-on', true).first() || false;
14827         this.component = (this.component && this.component.length === 0) ? false : this.component;
14828         this.hasInput = this.component && this.inputEL().length;
14829         
14830         if (typeof(this.minViewMode === 'string')) {
14831             switch (this.minViewMode) {
14832                 case 'months':
14833                     this.minViewMode = 1;
14834                     break;
14835                 case 'years':
14836                     this.minViewMode = 2;
14837                     break;
14838                 default:
14839                     this.minViewMode = 0;
14840                     break;
14841             }
14842         }
14843         
14844         if (typeof(this.viewMode === 'string')) {
14845             switch (this.viewMode) {
14846                 case 'months':
14847                     this.viewMode = 1;
14848                     break;
14849                 case 'years':
14850                     this.viewMode = 2;
14851                     break;
14852                 default:
14853                     this.viewMode = 0;
14854                     break;
14855             }
14856         }
14857                 
14858         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14859         
14860 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14861         
14862         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14863         
14864         this.picker().on('mousedown', this.onMousedown, this);
14865         this.picker().on('click', this.onClick, this);
14866         
14867         this.picker().addClass('datepicker-dropdown');
14868         
14869         this.startViewMode = this.viewMode;
14870         
14871         if(this.singleMode){
14872             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
14873                 v.setVisibilityMode(Roo.Element.DISPLAY)
14874                 v.hide();
14875             });
14876             
14877             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
14878                 v.setStyle('width', '189px');
14879             });
14880         }
14881         
14882         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14883             if(!this.calendarWeeks){
14884                 v.remove();
14885                 return;
14886             }
14887             
14888             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14889             v.attr('colspan', function(i, val){
14890                 return parseInt(val) + 1;
14891             });
14892         })
14893                         
14894         
14895         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14896         
14897         this.setStartDate(this.startDate);
14898         this.setEndDate(this.endDate);
14899         
14900         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14901         
14902         this.fillDow();
14903         this.fillMonths();
14904         this.update();
14905         this.showMode();
14906         
14907         if(this.isInline) {
14908             this.show();
14909         }
14910     },
14911     
14912     picker : function()
14913     {
14914         return this.pickerEl;
14915 //        return this.el.select('.datepicker', true).first();
14916     },
14917     
14918     fillDow: function()
14919     {
14920         var dowCnt = this.weekStart;
14921         
14922         var dow = {
14923             tag: 'tr',
14924             cn: [
14925                 
14926             ]
14927         };
14928         
14929         if(this.calendarWeeks){
14930             dow.cn.push({
14931                 tag: 'th',
14932                 cls: 'cw',
14933                 html: '&nbsp;'
14934             })
14935         }
14936         
14937         while (dowCnt < this.weekStart + 7) {
14938             dow.cn.push({
14939                 tag: 'th',
14940                 cls: 'dow',
14941                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14942             });
14943         }
14944         
14945         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14946     },
14947     
14948     fillMonths: function()
14949     {    
14950         var i = 0;
14951         var months = this.picker().select('>.datepicker-months td', true).first();
14952         
14953         months.dom.innerHTML = '';
14954         
14955         while (i < 12) {
14956             var month = {
14957                 tag: 'span',
14958                 cls: 'month',
14959                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14960             }
14961             
14962             months.createChild(month);
14963         }
14964         
14965     },
14966     
14967     update: function()
14968     {
14969         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;
14970         
14971         if (this.date < this.startDate) {
14972             this.viewDate = new Date(this.startDate);
14973         } else if (this.date > this.endDate) {
14974             this.viewDate = new Date(this.endDate);
14975         } else {
14976             this.viewDate = new Date(this.date);
14977         }
14978         
14979         this.fill();
14980     },
14981     
14982     fill: function() 
14983     {
14984         var d = new Date(this.viewDate),
14985                 year = d.getUTCFullYear(),
14986                 month = d.getUTCMonth(),
14987                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14988                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14989                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14990                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14991                 currentDate = this.date && this.date.valueOf(),
14992                 today = this.UTCToday();
14993         
14994         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14995         
14996 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14997         
14998 //        this.picker.select('>tfoot th.today').
14999 //                                              .text(dates[this.language].today)
15000 //                                              .toggle(this.todayBtn !== false);
15001     
15002         this.updateNavArrows();
15003         this.fillMonths();
15004                                                 
15005         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15006         
15007         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15008          
15009         prevMonth.setUTCDate(day);
15010         
15011         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15012         
15013         var nextMonth = new Date(prevMonth);
15014         
15015         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15016         
15017         nextMonth = nextMonth.valueOf();
15018         
15019         var fillMonths = false;
15020         
15021         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15022         
15023         while(prevMonth.valueOf() < nextMonth) {
15024             var clsName = '';
15025             
15026             if (prevMonth.getUTCDay() === this.weekStart) {
15027                 if(fillMonths){
15028                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15029                 }
15030                     
15031                 fillMonths = {
15032                     tag: 'tr',
15033                     cn: []
15034                 };
15035                 
15036                 if(this.calendarWeeks){
15037                     // ISO 8601: First week contains first thursday.
15038                     // ISO also states week starts on Monday, but we can be more abstract here.
15039                     var
15040                     // Start of current week: based on weekstart/current date
15041                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15042                     // Thursday of this week
15043                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15044                     // First Thursday of year, year from thursday
15045                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15046                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15047                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15048                     
15049                     fillMonths.cn.push({
15050                         tag: 'td',
15051                         cls: 'cw',
15052                         html: calWeek
15053                     });
15054                 }
15055             }
15056             
15057             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15058                 clsName += ' old';
15059             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15060                 clsName += ' new';
15061             }
15062             if (this.todayHighlight &&
15063                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15064                 prevMonth.getUTCMonth() == today.getMonth() &&
15065                 prevMonth.getUTCDate() == today.getDate()) {
15066                 clsName += ' today';
15067             }
15068             
15069             if (currentDate && prevMonth.valueOf() === currentDate) {
15070                 clsName += ' active';
15071             }
15072             
15073             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15074                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15075                     clsName += ' disabled';
15076             }
15077             
15078             fillMonths.cn.push({
15079                 tag: 'td',
15080                 cls: 'day ' + clsName,
15081                 html: prevMonth.getDate()
15082             })
15083             
15084             prevMonth.setDate(prevMonth.getDate()+1);
15085         }
15086           
15087         var currentYear = this.date && this.date.getUTCFullYear();
15088         var currentMonth = this.date && this.date.getUTCMonth();
15089         
15090         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15091         
15092         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15093             v.removeClass('active');
15094             
15095             if(currentYear === year && k === currentMonth){
15096                 v.addClass('active');
15097             }
15098             
15099             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15100                 v.addClass('disabled');
15101             }
15102             
15103         });
15104         
15105         
15106         year = parseInt(year/10, 10) * 10;
15107         
15108         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15109         
15110         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15111         
15112         year -= 1;
15113         for (var i = -1; i < 11; i++) {
15114             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15115                 tag: 'span',
15116                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15117                 html: year
15118             })
15119             
15120             year += 1;
15121         }
15122     },
15123     
15124     showMode: function(dir) 
15125     {
15126         if (dir) {
15127             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15128         }
15129         
15130         Roo.each(this.picker().select('>div',true).elements, function(v){
15131             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15132             v.hide();
15133         });
15134         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15135     },
15136     
15137     place: function()
15138     {
15139         if(this.isInline) return;
15140         
15141         this.picker().removeClass(['bottom', 'top']);
15142         
15143         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15144             /*
15145              * place to the top of element!
15146              *
15147              */
15148             
15149             this.picker().addClass('top');
15150             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15151             
15152             return;
15153         }
15154         
15155         this.picker().addClass('bottom');
15156         
15157         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15158     },
15159     
15160     parseDate : function(value)
15161     {
15162         if(!value || value instanceof Date){
15163             return value;
15164         }
15165         var v = Date.parseDate(value, this.format);
15166         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15167             v = Date.parseDate(value, 'Y-m-d');
15168         }
15169         if(!v && this.altFormats){
15170             if(!this.altFormatsArray){
15171                 this.altFormatsArray = this.altFormats.split("|");
15172             }
15173             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15174                 v = Date.parseDate(value, this.altFormatsArray[i]);
15175             }
15176         }
15177         return v;
15178     },
15179     
15180     formatDate : function(date, fmt)
15181     {   
15182         return (!date || !(date instanceof Date)) ?
15183         date : date.dateFormat(fmt || this.format);
15184     },
15185     
15186     onFocus : function()
15187     {
15188         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15189         this.show();
15190     },
15191     
15192     onBlur : function()
15193     {
15194         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15195         
15196         var d = this.inputEl().getValue();
15197         
15198         this.setValue(d);
15199                 
15200         this.hide();
15201     },
15202     
15203     show : function()
15204     {
15205         this.picker().show();
15206         this.update();
15207         this.place();
15208         
15209         this.fireEvent('show', this, this.date);
15210     },
15211     
15212     hide : function()
15213     {
15214         if(this.isInline) return;
15215         this.picker().hide();
15216         this.viewMode = this.startViewMode;
15217         this.showMode();
15218         
15219         this.fireEvent('hide', this, this.date);
15220         
15221     },
15222     
15223     onMousedown: function(e)
15224     {
15225         e.stopPropagation();
15226         e.preventDefault();
15227     },
15228     
15229     keyup: function(e)
15230     {
15231         Roo.bootstrap.DateField.superclass.keyup.call(this);
15232         this.update();
15233     },
15234
15235     setValue: function(v)
15236     {
15237         
15238         // v can be a string or a date..
15239         
15240         
15241         var d = new Date(this.parseDate(v) ).clearTime();
15242         
15243         if(isNaN(d.getTime())){
15244             this.date = this.viewDate = '';
15245             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15246             return;
15247         }
15248         
15249         v = this.formatDate(d);
15250         
15251         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15252         
15253         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15254      
15255         this.update();
15256
15257         this.fireEvent('select', this, this.date);
15258         
15259     },
15260     
15261     getValue: function()
15262     {
15263         return this.formatDate(this.date);
15264     },
15265     
15266     fireKey: function(e)
15267     {
15268         if (!this.picker().isVisible()){
15269             if (e.keyCode == 27) // allow escape to hide and re-show picker
15270                 this.show();
15271             return;
15272         }
15273         
15274         var dateChanged = false,
15275         dir, day, month,
15276         newDate, newViewDate;
15277         
15278         switch(e.keyCode){
15279             case 27: // escape
15280                 this.hide();
15281                 e.preventDefault();
15282                 break;
15283             case 37: // left
15284             case 39: // right
15285                 if (!this.keyboardNavigation) break;
15286                 dir = e.keyCode == 37 ? -1 : 1;
15287                 
15288                 if (e.ctrlKey){
15289                     newDate = this.moveYear(this.date, dir);
15290                     newViewDate = this.moveYear(this.viewDate, dir);
15291                 } else if (e.shiftKey){
15292                     newDate = this.moveMonth(this.date, dir);
15293                     newViewDate = this.moveMonth(this.viewDate, dir);
15294                 } else {
15295                     newDate = new Date(this.date);
15296                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15297                     newViewDate = new Date(this.viewDate);
15298                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15299                 }
15300                 if (this.dateWithinRange(newDate)){
15301                     this.date = newDate;
15302                     this.viewDate = newViewDate;
15303                     this.setValue(this.formatDate(this.date));
15304 //                    this.update();
15305                     e.preventDefault();
15306                     dateChanged = true;
15307                 }
15308                 break;
15309             case 38: // up
15310             case 40: // down
15311                 if (!this.keyboardNavigation) break;
15312                 dir = e.keyCode == 38 ? -1 : 1;
15313                 if (e.ctrlKey){
15314                     newDate = this.moveYear(this.date, dir);
15315                     newViewDate = this.moveYear(this.viewDate, dir);
15316                 } else if (e.shiftKey){
15317                     newDate = this.moveMonth(this.date, dir);
15318                     newViewDate = this.moveMonth(this.viewDate, dir);
15319                 } else {
15320                     newDate = new Date(this.date);
15321                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15322                     newViewDate = new Date(this.viewDate);
15323                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15324                 }
15325                 if (this.dateWithinRange(newDate)){
15326                     this.date = newDate;
15327                     this.viewDate = newViewDate;
15328                     this.setValue(this.formatDate(this.date));
15329 //                    this.update();
15330                     e.preventDefault();
15331                     dateChanged = true;
15332                 }
15333                 break;
15334             case 13: // enter
15335                 this.setValue(this.formatDate(this.date));
15336                 this.hide();
15337                 e.preventDefault();
15338                 break;
15339             case 9: // tab
15340                 this.setValue(this.formatDate(this.date));
15341                 this.hide();
15342                 break;
15343             case 16: // shift
15344             case 17: // ctrl
15345             case 18: // alt
15346                 break;
15347             default :
15348                 this.hide();
15349                 
15350         }
15351     },
15352     
15353     
15354     onClick: function(e) 
15355     {
15356         e.stopPropagation();
15357         e.preventDefault();
15358         
15359         var target = e.getTarget();
15360         
15361         if(target.nodeName.toLowerCase() === 'i'){
15362             target = Roo.get(target).dom.parentNode;
15363         }
15364         
15365         var nodeName = target.nodeName;
15366         var className = target.className;
15367         var html = target.innerHTML;
15368         //Roo.log(nodeName);
15369         
15370         switch(nodeName.toLowerCase()) {
15371             case 'th':
15372                 switch(className) {
15373                     case 'switch':
15374                         this.showMode(1);
15375                         break;
15376                     case 'prev':
15377                     case 'next':
15378                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15379                         switch(this.viewMode){
15380                                 case 0:
15381                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15382                                         break;
15383                                 case 1:
15384                                 case 2:
15385                                         this.viewDate = this.moveYear(this.viewDate, dir);
15386                                         break;
15387                         }
15388                         this.fill();
15389                         break;
15390                     case 'today':
15391                         var date = new Date();
15392                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15393 //                        this.fill()
15394                         this.setValue(this.formatDate(this.date));
15395                         
15396                         this.hide();
15397                         break;
15398                 }
15399                 break;
15400             case 'span':
15401                 if (className.indexOf('disabled') < 0) {
15402                     this.viewDate.setUTCDate(1);
15403                     if (className.indexOf('month') > -1) {
15404                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15405                     } else {
15406                         var year = parseInt(html, 10) || 0;
15407                         this.viewDate.setUTCFullYear(year);
15408                         
15409                     }
15410                     
15411                     if(this.singleMode){
15412                         this.setValue(this.formatDate(this.viewDate));
15413                         this.hide();
15414                         return;
15415                     }
15416                     
15417                     this.showMode(-1);
15418                     this.fill();
15419                 }
15420                 break;
15421                 
15422             case 'td':
15423                 //Roo.log(className);
15424                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15425                     var day = parseInt(html, 10) || 1;
15426                     var year = this.viewDate.getUTCFullYear(),
15427                         month = this.viewDate.getUTCMonth();
15428
15429                     if (className.indexOf('old') > -1) {
15430                         if(month === 0 ){
15431                             month = 11;
15432                             year -= 1;
15433                         }else{
15434                             month -= 1;
15435                         }
15436                     } else if (className.indexOf('new') > -1) {
15437                         if (month == 11) {
15438                             month = 0;
15439                             year += 1;
15440                         } else {
15441                             month += 1;
15442                         }
15443                     }
15444                     //Roo.log([year,month,day]);
15445                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15446                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15447 //                    this.fill();
15448                     //Roo.log(this.formatDate(this.date));
15449                     this.setValue(this.formatDate(this.date));
15450                     this.hide();
15451                 }
15452                 break;
15453         }
15454     },
15455     
15456     setStartDate: function(startDate)
15457     {
15458         this.startDate = startDate || -Infinity;
15459         if (this.startDate !== -Infinity) {
15460             this.startDate = this.parseDate(this.startDate);
15461         }
15462         this.update();
15463         this.updateNavArrows();
15464     },
15465
15466     setEndDate: function(endDate)
15467     {
15468         this.endDate = endDate || Infinity;
15469         if (this.endDate !== Infinity) {
15470             this.endDate = this.parseDate(this.endDate);
15471         }
15472         this.update();
15473         this.updateNavArrows();
15474     },
15475     
15476     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15477     {
15478         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15479         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15480             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15481         }
15482         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15483             return parseInt(d, 10);
15484         });
15485         this.update();
15486         this.updateNavArrows();
15487     },
15488     
15489     updateNavArrows: function() 
15490     {
15491         if(this.singleMode){
15492             return;
15493         }
15494         
15495         var d = new Date(this.viewDate),
15496         year = d.getUTCFullYear(),
15497         month = d.getUTCMonth();
15498         
15499         Roo.each(this.picker().select('.prev', true).elements, function(v){
15500             v.show();
15501             switch (this.viewMode) {
15502                 case 0:
15503
15504                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15505                         v.hide();
15506                     }
15507                     break;
15508                 case 1:
15509                 case 2:
15510                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15511                         v.hide();
15512                     }
15513                     break;
15514             }
15515         });
15516         
15517         Roo.each(this.picker().select('.next', true).elements, function(v){
15518             v.show();
15519             switch (this.viewMode) {
15520                 case 0:
15521
15522                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15523                         v.hide();
15524                     }
15525                     break;
15526                 case 1:
15527                 case 2:
15528                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15529                         v.hide();
15530                     }
15531                     break;
15532             }
15533         })
15534     },
15535     
15536     moveMonth: function(date, dir)
15537     {
15538         if (!dir) return date;
15539         var new_date = new Date(date.valueOf()),
15540         day = new_date.getUTCDate(),
15541         month = new_date.getUTCMonth(),
15542         mag = Math.abs(dir),
15543         new_month, test;
15544         dir = dir > 0 ? 1 : -1;
15545         if (mag == 1){
15546             test = dir == -1
15547             // If going back one month, make sure month is not current month
15548             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15549             ? function(){
15550                 return new_date.getUTCMonth() == month;
15551             }
15552             // If going forward one month, make sure month is as expected
15553             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15554             : function(){
15555                 return new_date.getUTCMonth() != new_month;
15556             };
15557             new_month = month + dir;
15558             new_date.setUTCMonth(new_month);
15559             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15560             if (new_month < 0 || new_month > 11)
15561                 new_month = (new_month + 12) % 12;
15562         } else {
15563             // For magnitudes >1, move one month at a time...
15564             for (var i=0; i<mag; i++)
15565                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15566                 new_date = this.moveMonth(new_date, dir);
15567             // ...then reset the day, keeping it in the new month
15568             new_month = new_date.getUTCMonth();
15569             new_date.setUTCDate(day);
15570             test = function(){
15571                 return new_month != new_date.getUTCMonth();
15572             };
15573         }
15574         // Common date-resetting loop -- if date is beyond end of month, make it
15575         // end of month
15576         while (test()){
15577             new_date.setUTCDate(--day);
15578             new_date.setUTCMonth(new_month);
15579         }
15580         return new_date;
15581     },
15582
15583     moveYear: function(date, dir)
15584     {
15585         return this.moveMonth(date, dir*12);
15586     },
15587
15588     dateWithinRange: function(date)
15589     {
15590         return date >= this.startDate && date <= this.endDate;
15591     },
15592
15593     
15594     remove: function() 
15595     {
15596         this.picker().remove();
15597     }
15598    
15599 });
15600
15601 Roo.apply(Roo.bootstrap.DateField,  {
15602     
15603     head : {
15604         tag: 'thead',
15605         cn: [
15606         {
15607             tag: 'tr',
15608             cn: [
15609             {
15610                 tag: 'th',
15611                 cls: 'prev',
15612                 html: '<i class="fa fa-arrow-left"/>'
15613             },
15614             {
15615                 tag: 'th',
15616                 cls: 'switch',
15617                 colspan: '5'
15618             },
15619             {
15620                 tag: 'th',
15621                 cls: 'next',
15622                 html: '<i class="fa fa-arrow-right"/>'
15623             }
15624
15625             ]
15626         }
15627         ]
15628     },
15629     
15630     content : {
15631         tag: 'tbody',
15632         cn: [
15633         {
15634             tag: 'tr',
15635             cn: [
15636             {
15637                 tag: 'td',
15638                 colspan: '7'
15639             }
15640             ]
15641         }
15642         ]
15643     },
15644     
15645     footer : {
15646         tag: 'tfoot',
15647         cn: [
15648         {
15649             tag: 'tr',
15650             cn: [
15651             {
15652                 tag: 'th',
15653                 colspan: '7',
15654                 cls: 'today'
15655             }
15656                     
15657             ]
15658         }
15659         ]
15660     },
15661     
15662     dates:{
15663         en: {
15664             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15665             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15666             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15667             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15668             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15669             today: "Today"
15670         }
15671     },
15672     
15673     modes: [
15674     {
15675         clsName: 'days',
15676         navFnc: 'Month',
15677         navStep: 1
15678     },
15679     {
15680         clsName: 'months',
15681         navFnc: 'FullYear',
15682         navStep: 1
15683     },
15684     {
15685         clsName: 'years',
15686         navFnc: 'FullYear',
15687         navStep: 10
15688     }]
15689 });
15690
15691 Roo.apply(Roo.bootstrap.DateField,  {
15692   
15693     template : {
15694         tag: 'div',
15695         cls: 'datepicker dropdown-menu roo-dynamic',
15696         cn: [
15697         {
15698             tag: 'div',
15699             cls: 'datepicker-days',
15700             cn: [
15701             {
15702                 tag: 'table',
15703                 cls: 'table-condensed',
15704                 cn:[
15705                 Roo.bootstrap.DateField.head,
15706                 {
15707                     tag: 'tbody'
15708                 },
15709                 Roo.bootstrap.DateField.footer
15710                 ]
15711             }
15712             ]
15713         },
15714         {
15715             tag: 'div',
15716             cls: 'datepicker-months',
15717             cn: [
15718             {
15719                 tag: 'table',
15720                 cls: 'table-condensed',
15721                 cn:[
15722                 Roo.bootstrap.DateField.head,
15723                 Roo.bootstrap.DateField.content,
15724                 Roo.bootstrap.DateField.footer
15725                 ]
15726             }
15727             ]
15728         },
15729         {
15730             tag: 'div',
15731             cls: 'datepicker-years',
15732             cn: [
15733             {
15734                 tag: 'table',
15735                 cls: 'table-condensed',
15736                 cn:[
15737                 Roo.bootstrap.DateField.head,
15738                 Roo.bootstrap.DateField.content,
15739                 Roo.bootstrap.DateField.footer
15740                 ]
15741             }
15742             ]
15743         }
15744         ]
15745     }
15746 });
15747
15748  
15749
15750  /*
15751  * - LGPL
15752  *
15753  * TimeField
15754  * 
15755  */
15756
15757 /**
15758  * @class Roo.bootstrap.TimeField
15759  * @extends Roo.bootstrap.Input
15760  * Bootstrap DateField class
15761  * 
15762  * 
15763  * @constructor
15764  * Create a new TimeField
15765  * @param {Object} config The config object
15766  */
15767
15768 Roo.bootstrap.TimeField = function(config){
15769     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15770     this.addEvents({
15771             /**
15772              * @event show
15773              * Fires when this field show.
15774              * @param {Roo.bootstrap.DateField} thisthis
15775              * @param {Mixed} date The date value
15776              */
15777             show : true,
15778             /**
15779              * @event show
15780              * Fires when this field hide.
15781              * @param {Roo.bootstrap.DateField} this
15782              * @param {Mixed} date The date value
15783              */
15784             hide : true,
15785             /**
15786              * @event select
15787              * Fires when select a date.
15788              * @param {Roo.bootstrap.DateField} this
15789              * @param {Mixed} date The date value
15790              */
15791             select : true
15792         });
15793 };
15794
15795 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15796     
15797     /**
15798      * @cfg {String} format
15799      * The default time format string which can be overriden for localization support.  The format must be
15800      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15801      */
15802     format : "H:i",
15803        
15804     onRender: function(ct, position)
15805     {
15806         
15807         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15808                 
15809         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15810         
15811         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15812         
15813         this.pop = this.picker().select('>.datepicker-time',true).first();
15814         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15815         
15816         this.picker().on('mousedown', this.onMousedown, this);
15817         this.picker().on('click', this.onClick, this);
15818         
15819         this.picker().addClass('datepicker-dropdown');
15820     
15821         this.fillTime();
15822         this.update();
15823             
15824         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15825         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15826         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15827         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15828         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15829         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15830
15831     },
15832     
15833     fireKey: function(e){
15834         if (!this.picker().isVisible()){
15835             if (e.keyCode == 27) { // allow escape to hide and re-show picker
15836                 this.show();
15837             }
15838             return;
15839         }
15840
15841         e.preventDefault();
15842         
15843         switch(e.keyCode){
15844             case 27: // escape
15845                 this.hide();
15846                 break;
15847             case 37: // left
15848             case 39: // right
15849                 this.onTogglePeriod();
15850                 break;
15851             case 38: // up
15852                 this.onIncrementMinutes();
15853                 break;
15854             case 40: // down
15855                 this.onDecrementMinutes();
15856                 break;
15857             case 13: // enter
15858             case 9: // tab
15859                 this.setTime();
15860                 break;
15861         }
15862     },
15863     
15864     onClick: function(e) {
15865         e.stopPropagation();
15866         e.preventDefault();
15867     },
15868     
15869     picker : function()
15870     {
15871         return this.el.select('.datepicker', true).first();
15872     },
15873     
15874     fillTime: function()
15875     {    
15876         var time = this.pop.select('tbody', true).first();
15877         
15878         time.dom.innerHTML = '';
15879         
15880         time.createChild({
15881             tag: 'tr',
15882             cn: [
15883                 {
15884                     tag: 'td',
15885                     cn: [
15886                         {
15887                             tag: 'a',
15888                             href: '#',
15889                             cls: 'btn',
15890                             cn: [
15891                                 {
15892                                     tag: 'span',
15893                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15894                                 }
15895                             ]
15896                         } 
15897                     ]
15898                 },
15899                 {
15900                     tag: 'td',
15901                     cls: 'separator'
15902                 },
15903                 {
15904                     tag: 'td',
15905                     cn: [
15906                         {
15907                             tag: 'a',
15908                             href: '#',
15909                             cls: 'btn',
15910                             cn: [
15911                                 {
15912                                     tag: 'span',
15913                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15914                                 }
15915                             ]
15916                         }
15917                     ]
15918                 },
15919                 {
15920                     tag: 'td',
15921                     cls: 'separator'
15922                 }
15923             ]
15924         });
15925         
15926         time.createChild({
15927             tag: 'tr',
15928             cn: [
15929                 {
15930                     tag: 'td',
15931                     cn: [
15932                         {
15933                             tag: 'span',
15934                             cls: 'timepicker-hour',
15935                             html: '00'
15936                         }  
15937                     ]
15938                 },
15939                 {
15940                     tag: 'td',
15941                     cls: 'separator',
15942                     html: ':'
15943                 },
15944                 {
15945                     tag: 'td',
15946                     cn: [
15947                         {
15948                             tag: 'span',
15949                             cls: 'timepicker-minute',
15950                             html: '00'
15951                         }  
15952                     ]
15953                 },
15954                 {
15955                     tag: 'td',
15956                     cls: 'separator'
15957                 },
15958                 {
15959                     tag: 'td',
15960                     cn: [
15961                         {
15962                             tag: 'button',
15963                             type: 'button',
15964                             cls: 'btn btn-primary period',
15965                             html: 'AM'
15966                             
15967                         }
15968                     ]
15969                 }
15970             ]
15971         });
15972         
15973         time.createChild({
15974             tag: 'tr',
15975             cn: [
15976                 {
15977                     tag: 'td',
15978                     cn: [
15979                         {
15980                             tag: 'a',
15981                             href: '#',
15982                             cls: 'btn',
15983                             cn: [
15984                                 {
15985                                     tag: 'span',
15986                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15987                                 }
15988                             ]
15989                         }
15990                     ]
15991                 },
15992                 {
15993                     tag: 'td',
15994                     cls: 'separator'
15995                 },
15996                 {
15997                     tag: 'td',
15998                     cn: [
15999                         {
16000                             tag: 'a',
16001                             href: '#',
16002                             cls: 'btn',
16003                             cn: [
16004                                 {
16005                                     tag: 'span',
16006                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16007                                 }
16008                             ]
16009                         }
16010                     ]
16011                 },
16012                 {
16013                     tag: 'td',
16014                     cls: 'separator'
16015                 }
16016             ]
16017         });
16018         
16019     },
16020     
16021     update: function()
16022     {
16023         
16024         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16025         
16026         this.fill();
16027     },
16028     
16029     fill: function() 
16030     {
16031         var hours = this.time.getHours();
16032         var minutes = this.time.getMinutes();
16033         var period = 'AM';
16034         
16035         if(hours > 11){
16036             period = 'PM';
16037         }
16038         
16039         if(hours == 0){
16040             hours = 12;
16041         }
16042         
16043         
16044         if(hours > 12){
16045             hours = hours - 12;
16046         }
16047         
16048         if(hours < 10){
16049             hours = '0' + hours;
16050         }
16051         
16052         if(minutes < 10){
16053             minutes = '0' + minutes;
16054         }
16055         
16056         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16057         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16058         this.pop.select('button', true).first().dom.innerHTML = period;
16059         
16060     },
16061     
16062     place: function()
16063     {   
16064         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16065         
16066         var cls = ['bottom'];
16067         
16068         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16069             cls.pop();
16070             cls.push('top');
16071         }
16072         
16073         cls.push('right');
16074         
16075         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16076             cls.pop();
16077             cls.push('left');
16078         }
16079         
16080         this.picker().addClass(cls.join('-'));
16081         
16082         var _this = this;
16083         
16084         Roo.each(cls, function(c){
16085             if(c == 'bottom'){
16086                 _this.picker().setTop(_this.inputEl().getHeight());
16087                 return;
16088             }
16089             if(c == 'top'){
16090                 _this.picker().setTop(0 - _this.picker().getHeight());
16091                 return;
16092             }
16093             
16094             if(c == 'left'){
16095                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16096                 return;
16097             }
16098             if(c == 'right'){
16099                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16100                 return;
16101             }
16102         });
16103         
16104     },
16105   
16106     onFocus : function()
16107     {
16108         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16109         this.show();
16110     },
16111     
16112     onBlur : function()
16113     {
16114         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16115         this.hide();
16116     },
16117     
16118     show : function()
16119     {
16120         this.picker().show();
16121         this.pop.show();
16122         this.update();
16123         this.place();
16124         
16125         this.fireEvent('show', this, this.date);
16126     },
16127     
16128     hide : function()
16129     {
16130         this.picker().hide();
16131         this.pop.hide();
16132         
16133         this.fireEvent('hide', this, this.date);
16134     },
16135     
16136     setTime : function()
16137     {
16138         this.hide();
16139         this.setValue(this.time.format(this.format));
16140         
16141         this.fireEvent('select', this, this.date);
16142         
16143         
16144     },
16145     
16146     onMousedown: function(e){
16147         e.stopPropagation();
16148         e.preventDefault();
16149     },
16150     
16151     onIncrementHours: function()
16152     {
16153         Roo.log('onIncrementHours');
16154         this.time = this.time.add(Date.HOUR, 1);
16155         this.update();
16156         
16157     },
16158     
16159     onDecrementHours: function()
16160     {
16161         Roo.log('onDecrementHours');
16162         this.time = this.time.add(Date.HOUR, -1);
16163         this.update();
16164     },
16165     
16166     onIncrementMinutes: function()
16167     {
16168         Roo.log('onIncrementMinutes');
16169         this.time = this.time.add(Date.MINUTE, 1);
16170         this.update();
16171     },
16172     
16173     onDecrementMinutes: function()
16174     {
16175         Roo.log('onDecrementMinutes');
16176         this.time = this.time.add(Date.MINUTE, -1);
16177         this.update();
16178     },
16179     
16180     onTogglePeriod: function()
16181     {
16182         Roo.log('onTogglePeriod');
16183         this.time = this.time.add(Date.HOUR, 12);
16184         this.update();
16185     }
16186     
16187    
16188 });
16189
16190 Roo.apply(Roo.bootstrap.TimeField,  {
16191     
16192     content : {
16193         tag: 'tbody',
16194         cn: [
16195             {
16196                 tag: 'tr',
16197                 cn: [
16198                 {
16199                     tag: 'td',
16200                     colspan: '7'
16201                 }
16202                 ]
16203             }
16204         ]
16205     },
16206     
16207     footer : {
16208         tag: 'tfoot',
16209         cn: [
16210             {
16211                 tag: 'tr',
16212                 cn: [
16213                 {
16214                     tag: 'th',
16215                     colspan: '7',
16216                     cls: '',
16217                     cn: [
16218                         {
16219                             tag: 'button',
16220                             cls: 'btn btn-info ok',
16221                             html: 'OK'
16222                         }
16223                     ]
16224                 }
16225
16226                 ]
16227             }
16228         ]
16229     }
16230 });
16231
16232 Roo.apply(Roo.bootstrap.TimeField,  {
16233   
16234     template : {
16235         tag: 'div',
16236         cls: 'datepicker dropdown-menu',
16237         cn: [
16238             {
16239                 tag: 'div',
16240                 cls: 'datepicker-time',
16241                 cn: [
16242                 {
16243                     tag: 'table',
16244                     cls: 'table-condensed',
16245                     cn:[
16246                     Roo.bootstrap.TimeField.content,
16247                     Roo.bootstrap.TimeField.footer
16248                     ]
16249                 }
16250                 ]
16251             }
16252         ]
16253     }
16254 });
16255
16256  
16257
16258  /*
16259  * - LGPL
16260  *
16261  * MonthField
16262  * 
16263  */
16264
16265 /**
16266  * @class Roo.bootstrap.MonthField
16267  * @extends Roo.bootstrap.Input
16268  * Bootstrap MonthField class
16269  * 
16270  * @cfg {String} language default en
16271  * 
16272  * @constructor
16273  * Create a new MonthField
16274  * @param {Object} config The config object
16275  */
16276
16277 Roo.bootstrap.MonthField = function(config){
16278     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16279     
16280     this.addEvents({
16281         /**
16282          * @event show
16283          * Fires when this field show.
16284          * @param {Roo.bootstrap.MonthField} this
16285          * @param {Mixed} date The date value
16286          */
16287         show : true,
16288         /**
16289          * @event show
16290          * Fires when this field hide.
16291          * @param {Roo.bootstrap.MonthField} this
16292          * @param {Mixed} date The date value
16293          */
16294         hide : true,
16295         /**
16296          * @event select
16297          * Fires when select a date.
16298          * @param {Roo.bootstrap.MonthField} this
16299          * @param {String} oldvalue The old value
16300          * @param {String} newvalue The new value
16301          */
16302         select : true
16303     });
16304 };
16305
16306 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16307     
16308     onRender: function(ct, position)
16309     {
16310         
16311         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16312         
16313         this.language = this.language || 'en';
16314         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16315         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16316         
16317         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16318         this.isInline = false;
16319         this.isInput = true;
16320         this.component = this.el.select('.add-on', true).first() || false;
16321         this.component = (this.component && this.component.length === 0) ? false : this.component;
16322         this.hasInput = this.component && this.inputEL().length;
16323         
16324         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16325         
16326         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16327         
16328         this.picker().on('mousedown', this.onMousedown, this);
16329         this.picker().on('click', this.onClick, this);
16330         
16331         this.picker().addClass('datepicker-dropdown');
16332         
16333         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16334             v.setStyle('width', '189px');
16335         });
16336         
16337         this.fillMonths();
16338         
16339         this.update();
16340         
16341         if(this.isInline) {
16342             this.show();
16343         }
16344         
16345     },
16346     
16347     setValue: function(v, suppressEvent)
16348     {   
16349         var o = this.getValue();
16350         
16351         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16352         
16353         this.update();
16354
16355         if(suppressEvent !== true){
16356             this.fireEvent('select', this, o, v);
16357         }
16358         
16359     },
16360     
16361     getValue: function()
16362     {
16363         return this.value;
16364     },
16365     
16366     onClick: function(e) 
16367     {
16368         e.stopPropagation();
16369         e.preventDefault();
16370         
16371         var target = e.getTarget();
16372         
16373         if(target.nodeName.toLowerCase() === 'i'){
16374             target = Roo.get(target).dom.parentNode;
16375         }
16376         
16377         var nodeName = target.nodeName;
16378         var className = target.className;
16379         var html = target.innerHTML;
16380         
16381         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16382             return;
16383         }
16384         
16385         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16386         
16387         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16388         
16389         this.hide();
16390                         
16391     },
16392     
16393     picker : function()
16394     {
16395         return this.pickerEl;
16396     },
16397     
16398     fillMonths: function()
16399     {    
16400         var i = 0;
16401         var months = this.picker().select('>.datepicker-months td', true).first();
16402         
16403         months.dom.innerHTML = '';
16404         
16405         while (i < 12) {
16406             var month = {
16407                 tag: 'span',
16408                 cls: 'month',
16409                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16410             }
16411             
16412             months.createChild(month);
16413         }
16414         
16415     },
16416     
16417     update: function()
16418     {
16419         var _this = this;
16420         
16421         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16422             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16423         }
16424         
16425         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16426             e.removeClass('active');
16427             
16428             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16429                 e.addClass('active');
16430             }
16431         })
16432     },
16433     
16434     place: function()
16435     {
16436         if(this.isInline) return;
16437         
16438         this.picker().removeClass(['bottom', 'top']);
16439         
16440         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16441             /*
16442              * place to the top of element!
16443              *
16444              */
16445             
16446             this.picker().addClass('top');
16447             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16448             
16449             return;
16450         }
16451         
16452         this.picker().addClass('bottom');
16453         
16454         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16455     },
16456     
16457     onFocus : function()
16458     {
16459         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
16460         this.show();
16461     },
16462     
16463     onBlur : function()
16464     {
16465         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
16466         
16467         var d = this.inputEl().getValue();
16468         
16469         this.setValue(d);
16470                 
16471         this.hide();
16472     },
16473     
16474     show : function()
16475     {
16476         this.picker().show();
16477         this.picker().select('>.datepicker-months', true).first().show();
16478         this.update();
16479         this.place();
16480         
16481         this.fireEvent('show', this, this.date);
16482     },
16483     
16484     hide : function()
16485     {
16486         if(this.isInline) return;
16487         this.picker().hide();
16488         this.fireEvent('hide', this, this.date);
16489         
16490     },
16491     
16492     onMousedown: function(e)
16493     {
16494         e.stopPropagation();
16495         e.preventDefault();
16496     },
16497     
16498     keyup: function(e)
16499     {
16500         Roo.bootstrap.MonthField.superclass.keyup.call(this);
16501         this.update();
16502     },
16503
16504     fireKey: function(e)
16505     {
16506         if (!this.picker().isVisible()){
16507             if (e.keyCode == 27) // allow escape to hide and re-show picker
16508                 this.show();
16509             return;
16510         }
16511         
16512         var dir;
16513         
16514         switch(e.keyCode){
16515             case 27: // escape
16516                 this.hide();
16517                 e.preventDefault();
16518                 break;
16519             case 37: // left
16520             case 39: // right
16521                 dir = e.keyCode == 37 ? -1 : 1;
16522                 
16523                 this.vIndex = this.vIndex + dir;
16524                 
16525                 if(this.vIndex < 0){
16526                     this.vIndex = 0;
16527                 }
16528                 
16529                 if(this.vIndex > 11){
16530                     this.vIndex = 11;
16531                 }
16532                 
16533                 if(isNaN(this.vIndex)){
16534                     this.vIndex = 0;
16535                 }
16536                 
16537                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16538                 
16539                 break;
16540             case 38: // up
16541             case 40: // down
16542                 
16543                 dir = e.keyCode == 38 ? -1 : 1;
16544                 
16545                 this.vIndex = this.vIndex + dir * 4;
16546                 
16547                 if(this.vIndex < 0){
16548                     this.vIndex = 0;
16549                 }
16550                 
16551                 if(this.vIndex > 11){
16552                     this.vIndex = 11;
16553                 }
16554                 
16555                 if(isNaN(this.vIndex)){
16556                     this.vIndex = 0;
16557                 }
16558                 
16559                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16560                 break;
16561                 
16562             case 13: // enter
16563                 
16564                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16565                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16566                 }
16567                 
16568                 this.hide();
16569                 e.preventDefault();
16570                 break;
16571             case 9: // tab
16572                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16573                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16574                 }
16575                 this.hide();
16576                 break;
16577             case 16: // shift
16578             case 17: // ctrl
16579             case 18: // alt
16580                 break;
16581             default :
16582                 this.hide();
16583                 
16584         }
16585     },
16586     
16587     remove: function() 
16588     {
16589         this.picker().remove();
16590     }
16591    
16592 });
16593
16594 Roo.apply(Roo.bootstrap.MonthField,  {
16595     
16596     content : {
16597         tag: 'tbody',
16598         cn: [
16599         {
16600             tag: 'tr',
16601             cn: [
16602             {
16603                 tag: 'td',
16604                 colspan: '7'
16605             }
16606             ]
16607         }
16608         ]
16609     },
16610     
16611     dates:{
16612         en: {
16613             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16614             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
16615         }
16616     }
16617 });
16618
16619 Roo.apply(Roo.bootstrap.MonthField,  {
16620   
16621     template : {
16622         tag: 'div',
16623         cls: 'datepicker dropdown-menu roo-dynamic',
16624         cn: [
16625             {
16626                 tag: 'div',
16627                 cls: 'datepicker-months',
16628                 cn: [
16629                 {
16630                     tag: 'table',
16631                     cls: 'table-condensed',
16632                     cn:[
16633                         Roo.bootstrap.DateField.content
16634                     ]
16635                 }
16636                 ]
16637             }
16638         ]
16639     }
16640 });
16641
16642  
16643
16644  
16645  /*
16646  * - LGPL
16647  *
16648  * CheckBox
16649  * 
16650  */
16651
16652 /**
16653  * @class Roo.bootstrap.CheckBox
16654  * @extends Roo.bootstrap.Input
16655  * Bootstrap CheckBox class
16656  * 
16657  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
16658  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
16659  * @cfg {String} boxLabel The text that appears beside the checkbox
16660  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
16661  * @cfg {Boolean} checked initnal the element
16662  * @cfg {Boolean} inline inline the element (default false)
16663  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
16664  * 
16665  * @constructor
16666  * Create a new CheckBox
16667  * @param {Object} config The config object
16668  */
16669
16670 Roo.bootstrap.CheckBox = function(config){
16671     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
16672    
16673     this.addEvents({
16674         /**
16675         * @event check
16676         * Fires when the element is checked or unchecked.
16677         * @param {Roo.bootstrap.CheckBox} this This input
16678         * @param {Boolean} checked The new checked value
16679         */
16680        check : true
16681     });
16682     
16683 };
16684
16685 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
16686   
16687     inputType: 'checkbox',
16688     inputValue: 1,
16689     valueOff: 0,
16690     boxLabel: false,
16691     checked: false,
16692     weight : false,
16693     inline: false,
16694     
16695     getAutoCreate : function()
16696     {
16697         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16698         
16699         var id = Roo.id();
16700         
16701         var cfg = {};
16702         
16703         cfg.cls = 'form-group ' + this.inputType; //input-group
16704         
16705         if(this.inline){
16706             cfg.cls += ' ' + this.inputType + '-inline';
16707         }
16708         
16709         var input =  {
16710             tag: 'input',
16711             id : id,
16712             type : this.inputType,
16713             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
16714             cls : 'roo-' + this.inputType, //'form-box',
16715             placeholder : this.placeholder || ''
16716             
16717         };
16718         
16719         if (this.weight) { // Validity check?
16720             cfg.cls += " " + this.inputType + "-" + this.weight;
16721         }
16722         
16723         if (this.disabled) {
16724             input.disabled=true;
16725         }
16726         
16727         if(this.checked){
16728             input.checked = this.checked;
16729         }
16730         
16731         if (this.name) {
16732             input.name = this.name;
16733         }
16734         
16735         if (this.size) {
16736             input.cls += ' input-' + this.size;
16737         }
16738         
16739         var settings=this;
16740         
16741         ['xs','sm','md','lg'].map(function(size){
16742             if (settings[size]) {
16743                 cfg.cls += ' col-' + size + '-' + settings[size];
16744             }
16745         });
16746         
16747         var inputblock = input;
16748          
16749         if (this.before || this.after) {
16750             
16751             inputblock = {
16752                 cls : 'input-group',
16753                 cn :  [] 
16754             };
16755             
16756             if (this.before) {
16757                 inputblock.cn.push({
16758                     tag :'span',
16759                     cls : 'input-group-addon',
16760                     html : this.before
16761                 });
16762             }
16763             
16764             inputblock.cn.push(input);
16765             
16766             if (this.after) {
16767                 inputblock.cn.push({
16768                     tag :'span',
16769                     cls : 'input-group-addon',
16770                     html : this.after
16771                 });
16772             }
16773             
16774         }
16775         
16776         if (align ==='left' && this.fieldLabel.length) {
16777                 Roo.log("left and has label");
16778                 cfg.cn = [
16779                     
16780                     {
16781                         tag: 'label',
16782                         'for' :  id,
16783                         cls : 'control-label col-md-' + this.labelWidth,
16784                         html : this.fieldLabel
16785                         
16786                     },
16787                     {
16788                         cls : "col-md-" + (12 - this.labelWidth), 
16789                         cn: [
16790                             inputblock
16791                         ]
16792                     }
16793                     
16794                 ];
16795         } else if ( this.fieldLabel.length) {
16796                 Roo.log(" label");
16797                 cfg.cn = [
16798                    
16799                     {
16800                         tag: this.boxLabel ? 'span' : 'label',
16801                         'for': id,
16802                         cls: 'control-label box-input-label',
16803                         //cls : 'input-group-addon',
16804                         html : this.fieldLabel
16805                         
16806                     },
16807                     
16808                     inputblock
16809                     
16810                 ];
16811
16812         } else {
16813             
16814                 Roo.log(" no label && no align");
16815                 cfg.cn = [  inputblock ] ;
16816                 
16817                 
16818         }
16819         if(this.boxLabel){
16820              var boxLabelCfg = {
16821                 tag: 'label',
16822                 //'for': id, // box label is handled by onclick - so no for...
16823                 cls: 'box-label',
16824                 html: this.boxLabel
16825             }
16826             
16827             if(this.tooltip){
16828                 boxLabelCfg.tooltip = this.tooltip;
16829             }
16830              
16831             cfg.cn.push(boxLabelCfg);
16832         }
16833         
16834         
16835        
16836         return cfg;
16837         
16838     },
16839     
16840     /**
16841      * return the real input element.
16842      */
16843     inputEl: function ()
16844     {
16845         return this.el.select('input.roo-' + this.inputType,true).first();
16846     },
16847     
16848     labelEl: function()
16849     {
16850         return this.el.select('label.control-label',true).first();
16851     },
16852     /* depricated... */
16853     
16854     label: function()
16855     {
16856         return this.labelEl();
16857     },
16858     
16859     initEvents : function()
16860     {
16861 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16862         
16863         this.inputEl().on('click', this.onClick,  this);
16864         
16865         if (this.boxLabel) { 
16866             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
16867         }
16868         
16869         this.startValue = this.getValue();
16870         
16871         if(this.groupId){
16872             Roo.bootstrap.CheckBox.register(this);
16873         }
16874     },
16875     
16876     onClick : function()
16877     {   
16878         this.setChecked(!this.checked);
16879     },
16880     
16881     setChecked : function(state,suppressEvent)
16882     {
16883         this.startValue = this.getValue();
16884         
16885         if(this.inputType == 'radio'){
16886             
16887             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
16888                 e.dom.checked = false;
16889             });
16890             
16891             this.inputEl().dom.checked = true;
16892             
16893             this.inputEl().dom.value = this.inputValue;
16894             
16895             if(suppressEvent !== true){
16896                 this.fireEvent('check', this, true);
16897             }
16898             
16899             this.validate();
16900             
16901             return;
16902         }
16903         
16904         this.checked = state;
16905         
16906         this.inputEl().dom.checked = state;
16907         
16908         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16909         
16910         if(suppressEvent !== true){
16911             this.fireEvent('check', this, state);
16912         }
16913         
16914         this.validate();
16915     },
16916     
16917     getValue : function()
16918     {
16919         if(this.inputType == 'radio'){
16920             return this.getGroupValue();
16921         }
16922         
16923         return this.inputEl().getValue();
16924         
16925     },
16926     
16927     getGroupValue : function()
16928     {
16929         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
16930             return '';
16931         }
16932         
16933         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
16934     },
16935     
16936     setValue : function(v,suppressEvent)
16937     {
16938         if(this.inputType == 'radio'){
16939             this.setGroupValue(v, suppressEvent);
16940             return;
16941         }
16942         
16943         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16944         
16945         this.validate();
16946     },
16947     
16948     setGroupValue : function(v, suppressEvent)
16949     {
16950         this.startValue = this.getValue();
16951         
16952         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
16953             e.dom.checked = false;
16954             
16955             if(e.dom.value == v){
16956                 e.dom.checked = true;
16957             }
16958         });
16959         
16960         if(suppressEvent !== true){
16961             this.fireEvent('check', this, true);
16962         }
16963
16964         this.validate();
16965         
16966         return;
16967     },
16968     
16969     validate : function()
16970     {
16971         if(
16972                 this.disabled || 
16973                 (this.inputType == 'radio' && this.getValue().length) ||
16974                 (this.inputType == 'checkbox' && this.validateGroup())
16975         ){
16976             this.markValid();
16977             return true;
16978         }
16979         
16980         this.markInvalid();
16981         return false;
16982     },
16983     
16984     validateGroup : function()
16985     {
16986         if(!this.groupId){
16987             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
16988         }
16989         
16990         var group = Roo.bootstrap.CheckBox.get(this.groupId);
16991         
16992         if(!group){
16993             return false;
16994         }
16995         
16996         var r = false;
16997         
16998         for(var i in group){
16999             if(r){
17000                 break;
17001             }
17002             
17003             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17004         }
17005         
17006         return r;
17007     },
17008     
17009     /**
17010      * Mark this field as valid
17011      */
17012     markValid : function()
17013     {
17014         var _this = this;
17015         
17016         this.fireEvent('valid', this);
17017         
17018         if(this.inputType == 'radio'){
17019             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17020                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17021                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17022             });
17023             
17024             return;
17025         }
17026         
17027         if(!this.groupId){
17028             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17029             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17030             return;
17031         }
17032         
17033         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17034             
17035         if(!group){
17036             return;
17037         }
17038         
17039         for(var i in group){
17040             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17041             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17042         }
17043     },
17044     
17045      /**
17046      * Mark this field as invalid
17047      * @param {String} msg The validation message
17048      */
17049     markInvalid : function(msg)
17050     {
17051         var _this = this;
17052         
17053         this.fireEvent('invalid', this, msg);
17054         
17055         if(this.inputType == 'radio'){
17056             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17057                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17058                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17059             });
17060             
17061             return;
17062         }
17063         
17064         if(!this.groupId){
17065             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17066             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17067             return;
17068         }
17069         
17070         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17071             
17072         if(!group){
17073             return;
17074         }
17075         
17076         for(var i in group){
17077             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17078             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17079         }
17080         
17081     }
17082     
17083 });
17084
17085 Roo.apply(Roo.bootstrap.CheckBox, {
17086     
17087     groups: {},
17088     
17089      /**
17090     * register a CheckBox Group
17091     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17092     */
17093     register : function(checkbox)
17094     {
17095         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17096             this.groups[checkbox.groupId] = {};
17097         }
17098         
17099         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17100             return;
17101         }
17102         
17103         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17104         
17105     },
17106     /**
17107     * fetch a CheckBox Group based on the group ID
17108     * @param {string} the group ID
17109     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17110     */
17111     get: function(groupId) {
17112         if (typeof(this.groups[groupId]) == 'undefined') {
17113             return false;
17114         }
17115         
17116         return this.groups[groupId] ;
17117     }
17118     
17119     
17120 });
17121 /*
17122  * - LGPL
17123  *
17124  * Radio
17125  *
17126  *
17127  * not inline
17128  *<div class="radio">
17129   <label>
17130     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17131     Option one is this and that&mdash;be sure to include why it's great
17132   </label>
17133 </div>
17134  *
17135  *
17136  *inline
17137  *<span>
17138  *<label class="radio-inline">fieldLabel</label>
17139  *<label class="radio-inline">
17140   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17141 </label>
17142 <span>
17143  * 
17144  * 
17145  */
17146
17147 /**
17148  * @class Roo.bootstrap.Radio
17149  * @extends Roo.bootstrap.CheckBox
17150  * Bootstrap Radio class
17151
17152  * @constructor
17153  * Create a new Radio
17154  * @param {Object} config The config object
17155  */
17156
17157 Roo.bootstrap.Radio = function(config){
17158     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17159    
17160 };
17161
17162 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17163     
17164     inputType: 'radio',
17165     inputValue: '',
17166     valueOff: '',
17167     
17168     getAutoCreate : function()
17169     {
17170         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17171         align = align || 'left'; // default...
17172         
17173         
17174         
17175         var id = Roo.id();
17176         
17177         var cfg = {
17178                 tag : this.inline ? 'span' : 'div',
17179                 cls : '',
17180                 cn : []
17181         };
17182         
17183         var inline = this.inline ? ' radio-inline' : '';
17184         
17185         var lbl = {
17186                 tag: 'label' ,
17187                 // does not need for, as we wrap the input with it..
17188                 'for' : id,
17189                 cls : 'control-label box-label' + inline,
17190                 cn : []
17191         };
17192         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17193         
17194         var fieldLabel = {
17195             tag: 'label' ,
17196             //cls : 'control-label' + inline,
17197             html : this.fieldLabel,
17198             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17199         };
17200         
17201  
17202         
17203         
17204         var input =  {
17205             tag: 'input',
17206             id : id,
17207             type : this.inputType,
17208             //value : (!this.checked) ? this.valueOff : this.inputValue,
17209             value : this.inputValue,
17210             cls : 'roo-radio',
17211             placeholder : this.placeholder || '' // ?? needed????
17212             
17213         };
17214         if (this.weight) { // Validity check?
17215             input.cls += " radio-" + this.weight;
17216         }
17217         if (this.disabled) {
17218             input.disabled=true;
17219         }
17220         
17221         if(this.checked){
17222             input.checked = this.checked;
17223         }
17224         
17225         if (this.name) {
17226             input.name = this.name;
17227         }
17228         
17229         if (this.size) {
17230             input.cls += ' input-' + this.size;
17231         }
17232         
17233         //?? can span's inline have a width??
17234         
17235         var settings=this;
17236         ['xs','sm','md','lg'].map(function(size){
17237             if (settings[size]) {
17238                 cfg.cls += ' col-' + size + '-' + settings[size];
17239             }
17240         });
17241         
17242         var inputblock = input;
17243         
17244         if (this.before || this.after) {
17245             
17246             inputblock = {
17247                 cls : 'input-group',
17248                 tag : 'span',
17249                 cn :  [] 
17250             };
17251             if (this.before) {
17252                 inputblock.cn.push({
17253                     tag :'span',
17254                     cls : 'input-group-addon',
17255                     html : this.before
17256                 });
17257             }
17258             inputblock.cn.push(input);
17259             if (this.after) {
17260                 inputblock.cn.push({
17261                     tag :'span',
17262                     cls : 'input-group-addon',
17263                     html : this.after
17264                 });
17265             }
17266             
17267         };
17268         
17269         
17270         if (this.fieldLabel && this.fieldLabel.length) {
17271             cfg.cn.push(fieldLabel);
17272         }
17273        
17274         // normal bootstrap puts the input inside the label.
17275         // however with our styled version - it has to go after the input.
17276        
17277         //lbl.cn.push(inputblock);
17278         
17279         var lblwrap =  {
17280             tag: 'span',
17281             cls: 'radio' + inline,
17282             cn: [
17283                 inputblock,
17284                 lbl
17285             ]
17286         };
17287         
17288         cfg.cn.push( lblwrap);
17289         
17290         if(this.boxLabel){
17291             lbl.cn.push({
17292                 tag: 'span',
17293                 html: this.boxLabel
17294             })
17295         }
17296          
17297         
17298         return cfg;
17299         
17300     },
17301     
17302     initEvents : function()
17303     {
17304 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17305         
17306         this.inputEl().on('click', this.onClick,  this);
17307         if (this.boxLabel) {
17308             Roo.log('find label')
17309             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17310         }
17311         
17312     },
17313     
17314     inputEl: function ()
17315     {
17316         return this.el.select('input.roo-radio',true).first();
17317     },
17318     onClick : function()
17319     {   
17320         Roo.log("click");
17321         this.setChecked(true);
17322     },
17323     
17324     setChecked : function(state,suppressEvent)
17325     {
17326         if(state){
17327             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17328                 v.dom.checked = false;
17329             });
17330         }
17331         Roo.log(this.inputEl().dom);
17332         this.checked = state;
17333         this.inputEl().dom.checked = state;
17334         
17335         if(suppressEvent !== true){
17336             this.fireEvent('check', this, state);
17337         }
17338         
17339         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17340         
17341     },
17342     
17343     getGroupValue : function()
17344     {
17345         var value = '';
17346         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17347             if(v.dom.checked == true){
17348                 value = v.dom.value;
17349             }
17350         });
17351         
17352         return value;
17353     },
17354     
17355     /**
17356      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17357      * @return {Mixed} value The field value
17358      */
17359     getValue : function(){
17360         return this.getGroupValue();
17361     }
17362     
17363 });
17364
17365  
17366 //<script type="text/javascript">
17367
17368 /*
17369  * Based  Ext JS Library 1.1.1
17370  * Copyright(c) 2006-2007, Ext JS, LLC.
17371  * LGPL
17372  *
17373  */
17374  
17375 /**
17376  * @class Roo.HtmlEditorCore
17377  * @extends Roo.Component
17378  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17379  *
17380  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17381  */
17382
17383 Roo.HtmlEditorCore = function(config){
17384     
17385     
17386     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17387     
17388     
17389     this.addEvents({
17390         /**
17391          * @event initialize
17392          * Fires when the editor is fully initialized (including the iframe)
17393          * @param {Roo.HtmlEditorCore} this
17394          */
17395         initialize: true,
17396         /**
17397          * @event activate
17398          * Fires when the editor is first receives the focus. Any insertion must wait
17399          * until after this event.
17400          * @param {Roo.HtmlEditorCore} this
17401          */
17402         activate: true,
17403          /**
17404          * @event beforesync
17405          * Fires before the textarea is updated with content from the editor iframe. Return false
17406          * to cancel the sync.
17407          * @param {Roo.HtmlEditorCore} this
17408          * @param {String} html
17409          */
17410         beforesync: true,
17411          /**
17412          * @event beforepush
17413          * Fires before the iframe editor is updated with content from the textarea. Return false
17414          * to cancel the push.
17415          * @param {Roo.HtmlEditorCore} this
17416          * @param {String} html
17417          */
17418         beforepush: true,
17419          /**
17420          * @event sync
17421          * Fires when the textarea is updated with content from the editor iframe.
17422          * @param {Roo.HtmlEditorCore} this
17423          * @param {String} html
17424          */
17425         sync: true,
17426          /**
17427          * @event push
17428          * Fires when the iframe editor is updated with content from the textarea.
17429          * @param {Roo.HtmlEditorCore} this
17430          * @param {String} html
17431          */
17432         push: true,
17433         
17434         /**
17435          * @event editorevent
17436          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17437          * @param {Roo.HtmlEditorCore} this
17438          */
17439         editorevent: true
17440         
17441     });
17442     
17443     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
17444     
17445     // defaults : white / black...
17446     this.applyBlacklists();
17447     
17448     
17449     
17450 };
17451
17452
17453 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
17454
17455
17456      /**
17457      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
17458      */
17459     
17460     owner : false,
17461     
17462      /**
17463      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17464      *                        Roo.resizable.
17465      */
17466     resizable : false,
17467      /**
17468      * @cfg {Number} height (in pixels)
17469      */   
17470     height: 300,
17471    /**
17472      * @cfg {Number} width (in pixels)
17473      */   
17474     width: 500,
17475     
17476     /**
17477      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17478      * 
17479      */
17480     stylesheets: false,
17481     
17482     // id of frame..
17483     frameId: false,
17484     
17485     // private properties
17486     validationEvent : false,
17487     deferHeight: true,
17488     initialized : false,
17489     activated : false,
17490     sourceEditMode : false,
17491     onFocus : Roo.emptyFn,
17492     iframePad:3,
17493     hideMode:'offsets',
17494     
17495     clearUp: true,
17496     
17497     // blacklist + whitelisted elements..
17498     black: false,
17499     white: false,
17500      
17501     
17502
17503     /**
17504      * Protected method that will not generally be called directly. It
17505      * is called when the editor initializes the iframe with HTML contents. Override this method if you
17506      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
17507      */
17508     getDocMarkup : function(){
17509         // body styles..
17510         var st = '';
17511         
17512         // inherit styels from page...?? 
17513         if (this.stylesheets === false) {
17514             
17515             Roo.get(document.head).select('style').each(function(node) {
17516                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17517             });
17518             
17519             Roo.get(document.head).select('link').each(function(node) { 
17520                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17521             });
17522             
17523         } else if (!this.stylesheets.length) {
17524                 // simple..
17525                 st = '<style type="text/css">' +
17526                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17527                    '</style>';
17528         } else { 
17529             
17530         }
17531         
17532         st +=  '<style type="text/css">' +
17533             'IMG { cursor: pointer } ' +
17534         '</style>';
17535
17536         
17537         return '<html><head>' + st  +
17538             //<style type="text/css">' +
17539             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17540             //'</style>' +
17541             ' </head><body class="roo-htmleditor-body"></body></html>';
17542     },
17543
17544     // private
17545     onRender : function(ct, position)
17546     {
17547         var _t = this;
17548         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
17549         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
17550         
17551         
17552         this.el.dom.style.border = '0 none';
17553         this.el.dom.setAttribute('tabIndex', -1);
17554         this.el.addClass('x-hidden hide');
17555         
17556         
17557         
17558         if(Roo.isIE){ // fix IE 1px bogus margin
17559             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
17560         }
17561        
17562         
17563         this.frameId = Roo.id();
17564         
17565          
17566         
17567         var iframe = this.owner.wrap.createChild({
17568             tag: 'iframe',
17569             cls: 'form-control', // bootstrap..
17570             id: this.frameId,
17571             name: this.frameId,
17572             frameBorder : 'no',
17573             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
17574         }, this.el
17575         );
17576         
17577         
17578         this.iframe = iframe.dom;
17579
17580          this.assignDocWin();
17581         
17582         this.doc.designMode = 'on';
17583        
17584         this.doc.open();
17585         this.doc.write(this.getDocMarkup());
17586         this.doc.close();
17587
17588         
17589         var task = { // must defer to wait for browser to be ready
17590             run : function(){
17591                 //console.log("run task?" + this.doc.readyState);
17592                 this.assignDocWin();
17593                 if(this.doc.body || this.doc.readyState == 'complete'){
17594                     try {
17595                         this.doc.designMode="on";
17596                     } catch (e) {
17597                         return;
17598                     }
17599                     Roo.TaskMgr.stop(task);
17600                     this.initEditor.defer(10, this);
17601                 }
17602             },
17603             interval : 10,
17604             duration: 10000,
17605             scope: this
17606         };
17607         Roo.TaskMgr.start(task);
17608
17609     },
17610
17611     // private
17612     onResize : function(w, h)
17613     {
17614          Roo.log('resize: ' +w + ',' + h );
17615         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
17616         if(!this.iframe){
17617             return;
17618         }
17619         if(typeof w == 'number'){
17620             
17621             this.iframe.style.width = w + 'px';
17622         }
17623         if(typeof h == 'number'){
17624             
17625             this.iframe.style.height = h + 'px';
17626             if(this.doc){
17627                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
17628             }
17629         }
17630         
17631     },
17632
17633     /**
17634      * Toggles the editor between standard and source edit mode.
17635      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17636      */
17637     toggleSourceEdit : function(sourceEditMode){
17638         
17639         this.sourceEditMode = sourceEditMode === true;
17640         
17641         if(this.sourceEditMode){
17642  
17643             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
17644             
17645         }else{
17646             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
17647             //this.iframe.className = '';
17648             this.deferFocus();
17649         }
17650         //this.setSize(this.owner.wrap.getSize());
17651         //this.fireEvent('editmodechange', this, this.sourceEditMode);
17652     },
17653
17654     
17655   
17656
17657     /**
17658      * Protected method that will not generally be called directly. If you need/want
17659      * custom HTML cleanup, this is the method you should override.
17660      * @param {String} html The HTML to be cleaned
17661      * return {String} The cleaned HTML
17662      */
17663     cleanHtml : function(html){
17664         html = String(html);
17665         if(html.length > 5){
17666             if(Roo.isSafari){ // strip safari nonsense
17667                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
17668             }
17669         }
17670         if(html == '&nbsp;'){
17671             html = '';
17672         }
17673         return html;
17674     },
17675
17676     /**
17677      * HTML Editor -> Textarea
17678      * Protected method that will not generally be called directly. Syncs the contents
17679      * of the editor iframe with the textarea.
17680      */
17681     syncValue : function(){
17682         if(this.initialized){
17683             var bd = (this.doc.body || this.doc.documentElement);
17684             //this.cleanUpPaste(); -- this is done else where and causes havoc..
17685             var html = bd.innerHTML;
17686             if(Roo.isSafari){
17687                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
17688                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
17689                 if(m && m[1]){
17690                     html = '<div style="'+m[0]+'">' + html + '</div>';
17691                 }
17692             }
17693             html = this.cleanHtml(html);
17694             // fix up the special chars.. normaly like back quotes in word...
17695             // however we do not want to do this with chinese..
17696             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
17697                 var cc = b.charCodeAt();
17698                 if (
17699                     (cc >= 0x4E00 && cc < 0xA000 ) ||
17700                     (cc >= 0x3400 && cc < 0x4E00 ) ||
17701                     (cc >= 0xf900 && cc < 0xfb00 )
17702                 ) {
17703                         return b;
17704                 }
17705                 return "&#"+cc+";" 
17706             });
17707             if(this.owner.fireEvent('beforesync', this, html) !== false){
17708                 this.el.dom.value = html;
17709                 this.owner.fireEvent('sync', this, html);
17710             }
17711         }
17712     },
17713
17714     /**
17715      * Protected method that will not generally be called directly. Pushes the value of the textarea
17716      * into the iframe editor.
17717      */
17718     pushValue : function(){
17719         if(this.initialized){
17720             var v = this.el.dom.value.trim();
17721             
17722 //            if(v.length < 1){
17723 //                v = '&#160;';
17724 //            }
17725             
17726             if(this.owner.fireEvent('beforepush', this, v) !== false){
17727                 var d = (this.doc.body || this.doc.documentElement);
17728                 d.innerHTML = v;
17729                 this.cleanUpPaste();
17730                 this.el.dom.value = d.innerHTML;
17731                 this.owner.fireEvent('push', this, v);
17732             }
17733         }
17734     },
17735
17736     // private
17737     deferFocus : function(){
17738         this.focus.defer(10, this);
17739     },
17740
17741     // doc'ed in Field
17742     focus : function(){
17743         if(this.win && !this.sourceEditMode){
17744             this.win.focus();
17745         }else{
17746             this.el.focus();
17747         }
17748     },
17749     
17750     assignDocWin: function()
17751     {
17752         var iframe = this.iframe;
17753         
17754          if(Roo.isIE){
17755             this.doc = iframe.contentWindow.document;
17756             this.win = iframe.contentWindow;
17757         } else {
17758 //            if (!Roo.get(this.frameId)) {
17759 //                return;
17760 //            }
17761 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
17762 //            this.win = Roo.get(this.frameId).dom.contentWindow;
17763             
17764             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
17765                 return;
17766             }
17767             
17768             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
17769             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
17770         }
17771     },
17772     
17773     // private
17774     initEditor : function(){
17775         //console.log("INIT EDITOR");
17776         this.assignDocWin();
17777         
17778         
17779         
17780         this.doc.designMode="on";
17781         this.doc.open();
17782         this.doc.write(this.getDocMarkup());
17783         this.doc.close();
17784         
17785         var dbody = (this.doc.body || this.doc.documentElement);
17786         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
17787         // this copies styles from the containing element into thsi one..
17788         // not sure why we need all of this..
17789         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
17790         
17791         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
17792         //ss['background-attachment'] = 'fixed'; // w3c
17793         dbody.bgProperties = 'fixed'; // ie
17794         //Roo.DomHelper.applyStyles(dbody, ss);
17795         Roo.EventManager.on(this.doc, {
17796             //'mousedown': this.onEditorEvent,
17797             'mouseup': this.onEditorEvent,
17798             'dblclick': this.onEditorEvent,
17799             'click': this.onEditorEvent,
17800             'keyup': this.onEditorEvent,
17801             buffer:100,
17802             scope: this
17803         });
17804         if(Roo.isGecko){
17805             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
17806         }
17807         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
17808             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
17809         }
17810         this.initialized = true;
17811
17812         this.owner.fireEvent('initialize', this);
17813         this.pushValue();
17814     },
17815
17816     // private
17817     onDestroy : function(){
17818         
17819         
17820         
17821         if(this.rendered){
17822             
17823             //for (var i =0; i < this.toolbars.length;i++) {
17824             //    // fixme - ask toolbars for heights?
17825             //    this.toolbars[i].onDestroy();
17826            // }
17827             
17828             //this.wrap.dom.innerHTML = '';
17829             //this.wrap.remove();
17830         }
17831     },
17832
17833     // private
17834     onFirstFocus : function(){
17835         
17836         this.assignDocWin();
17837         
17838         
17839         this.activated = true;
17840          
17841     
17842         if(Roo.isGecko){ // prevent silly gecko errors
17843             this.win.focus();
17844             var s = this.win.getSelection();
17845             if(!s.focusNode || s.focusNode.nodeType != 3){
17846                 var r = s.getRangeAt(0);
17847                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
17848                 r.collapse(true);
17849                 this.deferFocus();
17850             }
17851             try{
17852                 this.execCmd('useCSS', true);
17853                 this.execCmd('styleWithCSS', false);
17854             }catch(e){}
17855         }
17856         this.owner.fireEvent('activate', this);
17857     },
17858
17859     // private
17860     adjustFont: function(btn){
17861         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
17862         //if(Roo.isSafari){ // safari
17863         //    adjust *= 2;
17864        // }
17865         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
17866         if(Roo.isSafari){ // safari
17867             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
17868             v =  (v < 10) ? 10 : v;
17869             v =  (v > 48) ? 48 : v;
17870             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
17871             
17872         }
17873         
17874         
17875         v = Math.max(1, v+adjust);
17876         
17877         this.execCmd('FontSize', v  );
17878     },
17879
17880     onEditorEvent : function(e){
17881         this.owner.fireEvent('editorevent', this, e);
17882       //  this.updateToolbar();
17883         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
17884     },
17885
17886     insertTag : function(tg)
17887     {
17888         // could be a bit smarter... -> wrap the current selected tRoo..
17889         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
17890             
17891             range = this.createRange(this.getSelection());
17892             var wrappingNode = this.doc.createElement(tg.toLowerCase());
17893             wrappingNode.appendChild(range.extractContents());
17894             range.insertNode(wrappingNode);
17895
17896             return;
17897             
17898             
17899             
17900         }
17901         this.execCmd("formatblock",   tg);
17902         
17903     },
17904     
17905     insertText : function(txt)
17906     {
17907         
17908         
17909         var range = this.createRange();
17910         range.deleteContents();
17911                //alert(Sender.getAttribute('label'));
17912                
17913         range.insertNode(this.doc.createTextNode(txt));
17914     } ,
17915     
17916      
17917
17918     /**
17919      * Executes a Midas editor command on the editor document and performs necessary focus and
17920      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
17921      * @param {String} cmd The Midas command
17922      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17923      */
17924     relayCmd : function(cmd, value){
17925         this.win.focus();
17926         this.execCmd(cmd, value);
17927         this.owner.fireEvent('editorevent', this);
17928         //this.updateToolbar();
17929         this.owner.deferFocus();
17930     },
17931
17932     /**
17933      * Executes a Midas editor command directly on the editor document.
17934      * For visual commands, you should use {@link #relayCmd} instead.
17935      * <b>This should only be called after the editor is initialized.</b>
17936      * @param {String} cmd The Midas command
17937      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17938      */
17939     execCmd : function(cmd, value){
17940         this.doc.execCommand(cmd, false, value === undefined ? null : value);
17941         this.syncValue();
17942     },
17943  
17944  
17945    
17946     /**
17947      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
17948      * to insert tRoo.
17949      * @param {String} text | dom node.. 
17950      */
17951     insertAtCursor : function(text)
17952     {
17953         
17954         
17955         
17956         if(!this.activated){
17957             return;
17958         }
17959         /*
17960         if(Roo.isIE){
17961             this.win.focus();
17962             var r = this.doc.selection.createRange();
17963             if(r){
17964                 r.collapse(true);
17965                 r.pasteHTML(text);
17966                 this.syncValue();
17967                 this.deferFocus();
17968             
17969             }
17970             return;
17971         }
17972         */
17973         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
17974             this.win.focus();
17975             
17976             
17977             // from jquery ui (MIT licenced)
17978             var range, node;
17979             var win = this.win;
17980             
17981             if (win.getSelection && win.getSelection().getRangeAt) {
17982                 range = win.getSelection().getRangeAt(0);
17983                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
17984                 range.insertNode(node);
17985             } else if (win.document.selection && win.document.selection.createRange) {
17986                 // no firefox support
17987                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17988                 win.document.selection.createRange().pasteHTML(txt);
17989             } else {
17990                 // no firefox support
17991                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17992                 this.execCmd('InsertHTML', txt);
17993             } 
17994             
17995             this.syncValue();
17996             
17997             this.deferFocus();
17998         }
17999     },
18000  // private
18001     mozKeyPress : function(e){
18002         if(e.ctrlKey){
18003             var c = e.getCharCode(), cmd;
18004           
18005             if(c > 0){
18006                 c = String.fromCharCode(c).toLowerCase();
18007                 switch(c){
18008                     case 'b':
18009                         cmd = 'bold';
18010                         break;
18011                     case 'i':
18012                         cmd = 'italic';
18013                         break;
18014                     
18015                     case 'u':
18016                         cmd = 'underline';
18017                         break;
18018                     
18019                     case 'v':
18020                         this.cleanUpPaste.defer(100, this);
18021                         return;
18022                         
18023                 }
18024                 if(cmd){
18025                     this.win.focus();
18026                     this.execCmd(cmd);
18027                     this.deferFocus();
18028                     e.preventDefault();
18029                 }
18030                 
18031             }
18032         }
18033     },
18034
18035     // private
18036     fixKeys : function(){ // load time branching for fastest keydown performance
18037         if(Roo.isIE){
18038             return function(e){
18039                 var k = e.getKey(), r;
18040                 if(k == e.TAB){
18041                     e.stopEvent();
18042                     r = this.doc.selection.createRange();
18043                     if(r){
18044                         r.collapse(true);
18045                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18046                         this.deferFocus();
18047                     }
18048                     return;
18049                 }
18050                 
18051                 if(k == e.ENTER){
18052                     r = this.doc.selection.createRange();
18053                     if(r){
18054                         var target = r.parentElement();
18055                         if(!target || target.tagName.toLowerCase() != 'li'){
18056                             e.stopEvent();
18057                             r.pasteHTML('<br />');
18058                             r.collapse(false);
18059                             r.select();
18060                         }
18061                     }
18062                 }
18063                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18064                     this.cleanUpPaste.defer(100, this);
18065                     return;
18066                 }
18067                 
18068                 
18069             };
18070         }else if(Roo.isOpera){
18071             return function(e){
18072                 var k = e.getKey();
18073                 if(k == e.TAB){
18074                     e.stopEvent();
18075                     this.win.focus();
18076                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18077                     this.deferFocus();
18078                 }
18079                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18080                     this.cleanUpPaste.defer(100, this);
18081                     return;
18082                 }
18083                 
18084             };
18085         }else if(Roo.isSafari){
18086             return function(e){
18087                 var k = e.getKey();
18088                 
18089                 if(k == e.TAB){
18090                     e.stopEvent();
18091                     this.execCmd('InsertText','\t');
18092                     this.deferFocus();
18093                     return;
18094                 }
18095                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18096                     this.cleanUpPaste.defer(100, this);
18097                     return;
18098                 }
18099                 
18100              };
18101         }
18102     }(),
18103     
18104     getAllAncestors: function()
18105     {
18106         var p = this.getSelectedNode();
18107         var a = [];
18108         if (!p) {
18109             a.push(p); // push blank onto stack..
18110             p = this.getParentElement();
18111         }
18112         
18113         
18114         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18115             a.push(p);
18116             p = p.parentNode;
18117         }
18118         a.push(this.doc.body);
18119         return a;
18120     },
18121     lastSel : false,
18122     lastSelNode : false,
18123     
18124     
18125     getSelection : function() 
18126     {
18127         this.assignDocWin();
18128         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18129     },
18130     
18131     getSelectedNode: function() 
18132     {
18133         // this may only work on Gecko!!!
18134         
18135         // should we cache this!!!!
18136         
18137         
18138         
18139          
18140         var range = this.createRange(this.getSelection()).cloneRange();
18141         
18142         if (Roo.isIE) {
18143             var parent = range.parentElement();
18144             while (true) {
18145                 var testRange = range.duplicate();
18146                 testRange.moveToElementText(parent);
18147                 if (testRange.inRange(range)) {
18148                     break;
18149                 }
18150                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18151                     break;
18152                 }
18153                 parent = parent.parentElement;
18154             }
18155             return parent;
18156         }
18157         
18158         // is ancestor a text element.
18159         var ac =  range.commonAncestorContainer;
18160         if (ac.nodeType == 3) {
18161             ac = ac.parentNode;
18162         }
18163         
18164         var ar = ac.childNodes;
18165          
18166         var nodes = [];
18167         var other_nodes = [];
18168         var has_other_nodes = false;
18169         for (var i=0;i<ar.length;i++) {
18170             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18171                 continue;
18172             }
18173             // fullly contained node.
18174             
18175             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18176                 nodes.push(ar[i]);
18177                 continue;
18178             }
18179             
18180             // probably selected..
18181             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18182                 other_nodes.push(ar[i]);
18183                 continue;
18184             }
18185             // outer..
18186             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18187                 continue;
18188             }
18189             
18190             
18191             has_other_nodes = true;
18192         }
18193         if (!nodes.length && other_nodes.length) {
18194             nodes= other_nodes;
18195         }
18196         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18197             return false;
18198         }
18199         
18200         return nodes[0];
18201     },
18202     createRange: function(sel)
18203     {
18204         // this has strange effects when using with 
18205         // top toolbar - not sure if it's a great idea.
18206         //this.editor.contentWindow.focus();
18207         if (typeof sel != "undefined") {
18208             try {
18209                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18210             } catch(e) {
18211                 return this.doc.createRange();
18212             }
18213         } else {
18214             return this.doc.createRange();
18215         }
18216     },
18217     getParentElement: function()
18218     {
18219         
18220         this.assignDocWin();
18221         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18222         
18223         var range = this.createRange(sel);
18224          
18225         try {
18226             var p = range.commonAncestorContainer;
18227             while (p.nodeType == 3) { // text node
18228                 p = p.parentNode;
18229             }
18230             return p;
18231         } catch (e) {
18232             return null;
18233         }
18234     
18235     },
18236     /***
18237      *
18238      * Range intersection.. the hard stuff...
18239      *  '-1' = before
18240      *  '0' = hits..
18241      *  '1' = after.
18242      *         [ -- selected range --- ]
18243      *   [fail]                        [fail]
18244      *
18245      *    basically..
18246      *      if end is before start or  hits it. fail.
18247      *      if start is after end or hits it fail.
18248      *
18249      *   if either hits (but other is outside. - then it's not 
18250      *   
18251      *    
18252      **/
18253     
18254     
18255     // @see http://www.thismuchiknow.co.uk/?p=64.
18256     rangeIntersectsNode : function(range, node)
18257     {
18258         var nodeRange = node.ownerDocument.createRange();
18259         try {
18260             nodeRange.selectNode(node);
18261         } catch (e) {
18262             nodeRange.selectNodeContents(node);
18263         }
18264     
18265         var rangeStartRange = range.cloneRange();
18266         rangeStartRange.collapse(true);
18267     
18268         var rangeEndRange = range.cloneRange();
18269         rangeEndRange.collapse(false);
18270     
18271         var nodeStartRange = nodeRange.cloneRange();
18272         nodeStartRange.collapse(true);
18273     
18274         var nodeEndRange = nodeRange.cloneRange();
18275         nodeEndRange.collapse(false);
18276     
18277         return rangeStartRange.compareBoundaryPoints(
18278                  Range.START_TO_START, nodeEndRange) == -1 &&
18279                rangeEndRange.compareBoundaryPoints(
18280                  Range.START_TO_START, nodeStartRange) == 1;
18281         
18282          
18283     },
18284     rangeCompareNode : function(range, node)
18285     {
18286         var nodeRange = node.ownerDocument.createRange();
18287         try {
18288             nodeRange.selectNode(node);
18289         } catch (e) {
18290             nodeRange.selectNodeContents(node);
18291         }
18292         
18293         
18294         range.collapse(true);
18295     
18296         nodeRange.collapse(true);
18297      
18298         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18299         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18300          
18301         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18302         
18303         var nodeIsBefore   =  ss == 1;
18304         var nodeIsAfter    = ee == -1;
18305         
18306         if (nodeIsBefore && nodeIsAfter)
18307             return 0; // outer
18308         if (!nodeIsBefore && nodeIsAfter)
18309             return 1; //right trailed.
18310         
18311         if (nodeIsBefore && !nodeIsAfter)
18312             return 2;  // left trailed.
18313         // fully contined.
18314         return 3;
18315     },
18316
18317     // private? - in a new class?
18318     cleanUpPaste :  function()
18319     {
18320         // cleans up the whole document..
18321         Roo.log('cleanuppaste');
18322         
18323         this.cleanUpChildren(this.doc.body);
18324         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18325         if (clean != this.doc.body.innerHTML) {
18326             this.doc.body.innerHTML = clean;
18327         }
18328         
18329     },
18330     
18331     cleanWordChars : function(input) {// change the chars to hex code
18332         var he = Roo.HtmlEditorCore;
18333         
18334         var output = input;
18335         Roo.each(he.swapCodes, function(sw) { 
18336             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18337             
18338             output = output.replace(swapper, sw[1]);
18339         });
18340         
18341         return output;
18342     },
18343     
18344     
18345     cleanUpChildren : function (n)
18346     {
18347         if (!n.childNodes.length) {
18348             return;
18349         }
18350         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18351            this.cleanUpChild(n.childNodes[i]);
18352         }
18353     },
18354     
18355     
18356         
18357     
18358     cleanUpChild : function (node)
18359     {
18360         var ed = this;
18361         //console.log(node);
18362         if (node.nodeName == "#text") {
18363             // clean up silly Windows -- stuff?
18364             return; 
18365         }
18366         if (node.nodeName == "#comment") {
18367             node.parentNode.removeChild(node);
18368             // clean up silly Windows -- stuff?
18369             return; 
18370         }
18371         var lcname = node.tagName.toLowerCase();
18372         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18373         // whitelist of tags..
18374         
18375         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18376             // remove node.
18377             node.parentNode.removeChild(node);
18378             return;
18379             
18380         }
18381         
18382         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18383         
18384         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18385         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18386         
18387         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18388         //    remove_keep_children = true;
18389         //}
18390         
18391         if (remove_keep_children) {
18392             this.cleanUpChildren(node);
18393             // inserts everything just before this node...
18394             while (node.childNodes.length) {
18395                 var cn = node.childNodes[0];
18396                 node.removeChild(cn);
18397                 node.parentNode.insertBefore(cn, node);
18398             }
18399             node.parentNode.removeChild(node);
18400             return;
18401         }
18402         
18403         if (!node.attributes || !node.attributes.length) {
18404             this.cleanUpChildren(node);
18405             return;
18406         }
18407         
18408         function cleanAttr(n,v)
18409         {
18410             
18411             if (v.match(/^\./) || v.match(/^\//)) {
18412                 return;
18413             }
18414             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18415                 return;
18416             }
18417             if (v.match(/^#/)) {
18418                 return;
18419             }
18420 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18421             node.removeAttribute(n);
18422             
18423         }
18424         
18425         var cwhite = this.cwhite;
18426         var cblack = this.cblack;
18427             
18428         function cleanStyle(n,v)
18429         {
18430             if (v.match(/expression/)) { //XSS?? should we even bother..
18431                 node.removeAttribute(n);
18432                 return;
18433             }
18434             
18435             var parts = v.split(/;/);
18436             var clean = [];
18437             
18438             Roo.each(parts, function(p) {
18439                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
18440                 if (!p.length) {
18441                     return true;
18442                 }
18443                 var l = p.split(':').shift().replace(/\s+/g,'');
18444                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
18445                 
18446                 if ( cwhite.length && cblack.indexOf(l) > -1) {
18447 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18448                     //node.removeAttribute(n);
18449                     return true;
18450                 }
18451                 //Roo.log()
18452                 // only allow 'c whitelisted system attributes'
18453                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
18454 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18455                     //node.removeAttribute(n);
18456                     return true;
18457                 }
18458                 
18459                 
18460                  
18461                 
18462                 clean.push(p);
18463                 return true;
18464             });
18465             if (clean.length) { 
18466                 node.setAttribute(n, clean.join(';'));
18467             } else {
18468                 node.removeAttribute(n);
18469             }
18470             
18471         }
18472         
18473         
18474         for (var i = node.attributes.length-1; i > -1 ; i--) {
18475             var a = node.attributes[i];
18476             //console.log(a);
18477             
18478             if (a.name.toLowerCase().substr(0,2)=='on')  {
18479                 node.removeAttribute(a.name);
18480                 continue;
18481             }
18482             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
18483                 node.removeAttribute(a.name);
18484                 continue;
18485             }
18486             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
18487                 cleanAttr(a.name,a.value); // fixme..
18488                 continue;
18489             }
18490             if (a.name == 'style') {
18491                 cleanStyle(a.name,a.value);
18492                 continue;
18493             }
18494             /// clean up MS crap..
18495             // tecnically this should be a list of valid class'es..
18496             
18497             
18498             if (a.name == 'class') {
18499                 if (a.value.match(/^Mso/)) {
18500                     node.className = '';
18501                 }
18502                 
18503                 if (a.value.match(/body/)) {
18504                     node.className = '';
18505                 }
18506                 continue;
18507             }
18508             
18509             // style cleanup!?
18510             // class cleanup?
18511             
18512         }
18513         
18514         
18515         this.cleanUpChildren(node);
18516         
18517         
18518     },
18519     /**
18520      * Clean up MS wordisms...
18521      */
18522     cleanWord : function(node)
18523     {
18524         var _t = this;
18525         var cleanWordChildren = function()
18526         {
18527             if (!node.childNodes.length) {
18528                 return;
18529             }
18530             for (var i = node.childNodes.length-1; i > -1 ; i--) {
18531                _t.cleanWord(node.childNodes[i]);
18532             }
18533         }
18534         
18535         
18536         if (!node) {
18537             this.cleanWord(this.doc.body);
18538             return;
18539         }
18540         if (node.nodeName == "#text") {
18541             // clean up silly Windows -- stuff?
18542             return; 
18543         }
18544         if (node.nodeName == "#comment") {
18545             node.parentNode.removeChild(node);
18546             // clean up silly Windows -- stuff?
18547             return; 
18548         }
18549         
18550         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
18551             node.parentNode.removeChild(node);
18552             return;
18553         }
18554         
18555         // remove - but keep children..
18556         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
18557             while (node.childNodes.length) {
18558                 var cn = node.childNodes[0];
18559                 node.removeChild(cn);
18560                 node.parentNode.insertBefore(cn, node);
18561             }
18562             node.parentNode.removeChild(node);
18563             cleanWordChildren();
18564             return;
18565         }
18566         // clean styles
18567         if (node.className.length) {
18568             
18569             var cn = node.className.split(/\W+/);
18570             var cna = [];
18571             Roo.each(cn, function(cls) {
18572                 if (cls.match(/Mso[a-zA-Z]+/)) {
18573                     return;
18574                 }
18575                 cna.push(cls);
18576             });
18577             node.className = cna.length ? cna.join(' ') : '';
18578             if (!cna.length) {
18579                 node.removeAttribute("class");
18580             }
18581         }
18582         
18583         if (node.hasAttribute("lang")) {
18584             node.removeAttribute("lang");
18585         }
18586         
18587         if (node.hasAttribute("style")) {
18588             
18589             var styles = node.getAttribute("style").split(";");
18590             var nstyle = [];
18591             Roo.each(styles, function(s) {
18592                 if (!s.match(/:/)) {
18593                     return;
18594                 }
18595                 var kv = s.split(":");
18596                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
18597                     return;
18598                 }
18599                 // what ever is left... we allow.
18600                 nstyle.push(s);
18601             });
18602             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
18603             if (!nstyle.length) {
18604                 node.removeAttribute('style');
18605             }
18606         }
18607         
18608         cleanWordChildren();
18609         
18610         
18611     },
18612     domToHTML : function(currentElement, depth, nopadtext) {
18613         
18614         depth = depth || 0;
18615         nopadtext = nopadtext || false;
18616     
18617         if (!currentElement) {
18618             return this.domToHTML(this.doc.body);
18619         }
18620         
18621         //Roo.log(currentElement);
18622         var j;
18623         var allText = false;
18624         var nodeName = currentElement.nodeName;
18625         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
18626         
18627         if  (nodeName == '#text') {
18628             
18629             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
18630         }
18631         
18632         
18633         var ret = '';
18634         if (nodeName != 'BODY') {
18635              
18636             var i = 0;
18637             // Prints the node tagName, such as <A>, <IMG>, etc
18638             if (tagName) {
18639                 var attr = [];
18640                 for(i = 0; i < currentElement.attributes.length;i++) {
18641                     // quoting?
18642                     var aname = currentElement.attributes.item(i).name;
18643                     if (!currentElement.attributes.item(i).value.length) {
18644                         continue;
18645                     }
18646                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
18647                 }
18648                 
18649                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
18650             } 
18651             else {
18652                 
18653                 // eack
18654             }
18655         } else {
18656             tagName = false;
18657         }
18658         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
18659             return ret;
18660         }
18661         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
18662             nopadtext = true;
18663         }
18664         
18665         
18666         // Traverse the tree
18667         i = 0;
18668         var currentElementChild = currentElement.childNodes.item(i);
18669         var allText = true;
18670         var innerHTML  = '';
18671         lastnode = '';
18672         while (currentElementChild) {
18673             // Formatting code (indent the tree so it looks nice on the screen)
18674             var nopad = nopadtext;
18675             if (lastnode == 'SPAN') {
18676                 nopad  = true;
18677             }
18678             // text
18679             if  (currentElementChild.nodeName == '#text') {
18680                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
18681                 toadd = nopadtext ? toadd : toadd.trim();
18682                 if (!nopad && toadd.length > 80) {
18683                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
18684                 }
18685                 innerHTML  += toadd;
18686                 
18687                 i++;
18688                 currentElementChild = currentElement.childNodes.item(i);
18689                 lastNode = '';
18690                 continue;
18691             }
18692             allText = false;
18693             
18694             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
18695                 
18696             // Recursively traverse the tree structure of the child node
18697             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
18698             lastnode = currentElementChild.nodeName;
18699             i++;
18700             currentElementChild=currentElement.childNodes.item(i);
18701         }
18702         
18703         ret += innerHTML;
18704         
18705         if (!allText) {
18706                 // The remaining code is mostly for formatting the tree
18707             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
18708         }
18709         
18710         
18711         if (tagName) {
18712             ret+= "</"+tagName+">";
18713         }
18714         return ret;
18715         
18716     },
18717         
18718     applyBlacklists : function()
18719     {
18720         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
18721         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
18722         
18723         this.white = [];
18724         this.black = [];
18725         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
18726             if (b.indexOf(tag) > -1) {
18727                 return;
18728             }
18729             this.white.push(tag);
18730             
18731         }, this);
18732         
18733         Roo.each(w, function(tag) {
18734             if (b.indexOf(tag) > -1) {
18735                 return;
18736             }
18737             if (this.white.indexOf(tag) > -1) {
18738                 return;
18739             }
18740             this.white.push(tag);
18741             
18742         }, this);
18743         
18744         
18745         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
18746             if (w.indexOf(tag) > -1) {
18747                 return;
18748             }
18749             this.black.push(tag);
18750             
18751         }, this);
18752         
18753         Roo.each(b, function(tag) {
18754             if (w.indexOf(tag) > -1) {
18755                 return;
18756             }
18757             if (this.black.indexOf(tag) > -1) {
18758                 return;
18759             }
18760             this.black.push(tag);
18761             
18762         }, this);
18763         
18764         
18765         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
18766         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
18767         
18768         this.cwhite = [];
18769         this.cblack = [];
18770         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
18771             if (b.indexOf(tag) > -1) {
18772                 return;
18773             }
18774             this.cwhite.push(tag);
18775             
18776         }, this);
18777         
18778         Roo.each(w, function(tag) {
18779             if (b.indexOf(tag) > -1) {
18780                 return;
18781             }
18782             if (this.cwhite.indexOf(tag) > -1) {
18783                 return;
18784             }
18785             this.cwhite.push(tag);
18786             
18787         }, this);
18788         
18789         
18790         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
18791             if (w.indexOf(tag) > -1) {
18792                 return;
18793             }
18794             this.cblack.push(tag);
18795             
18796         }, this);
18797         
18798         Roo.each(b, function(tag) {
18799             if (w.indexOf(tag) > -1) {
18800                 return;
18801             }
18802             if (this.cblack.indexOf(tag) > -1) {
18803                 return;
18804             }
18805             this.cblack.push(tag);
18806             
18807         }, this);
18808     },
18809     
18810     setStylesheets : function(stylesheets)
18811     {
18812         if(typeof(stylesheets) == 'string'){
18813             Roo.get(this.iframe.contentDocument.head).createChild({
18814                 tag : 'link',
18815                 rel : 'stylesheet',
18816                 type : 'text/css',
18817                 href : stylesheets
18818             });
18819             
18820             return;
18821         }
18822         var _this = this;
18823      
18824         Roo.each(stylesheets, function(s) {
18825             if(!s.length){
18826                 return;
18827             }
18828             
18829             Roo.get(_this.iframe.contentDocument.head).createChild({
18830                 tag : 'link',
18831                 rel : 'stylesheet',
18832                 type : 'text/css',
18833                 href : s
18834             });
18835         });
18836
18837         
18838     },
18839     
18840     removeStylesheets : function()
18841     {
18842         var _this = this;
18843         
18844         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
18845             s.remove();
18846         });
18847     }
18848     
18849     // hide stuff that is not compatible
18850     /**
18851      * @event blur
18852      * @hide
18853      */
18854     /**
18855      * @event change
18856      * @hide
18857      */
18858     /**
18859      * @event focus
18860      * @hide
18861      */
18862     /**
18863      * @event specialkey
18864      * @hide
18865      */
18866     /**
18867      * @cfg {String} fieldClass @hide
18868      */
18869     /**
18870      * @cfg {String} focusClass @hide
18871      */
18872     /**
18873      * @cfg {String} autoCreate @hide
18874      */
18875     /**
18876      * @cfg {String} inputType @hide
18877      */
18878     /**
18879      * @cfg {String} invalidClass @hide
18880      */
18881     /**
18882      * @cfg {String} invalidText @hide
18883      */
18884     /**
18885      * @cfg {String} msgFx @hide
18886      */
18887     /**
18888      * @cfg {String} validateOnBlur @hide
18889      */
18890 });
18891
18892 Roo.HtmlEditorCore.white = [
18893         'area', 'br', 'img', 'input', 'hr', 'wbr',
18894         
18895        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
18896        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
18897        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
18898        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
18899        'table',   'ul',         'xmp', 
18900        
18901        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
18902       'thead',   'tr', 
18903      
18904       'dir', 'menu', 'ol', 'ul', 'dl',
18905        
18906       'embed',  'object'
18907 ];
18908
18909
18910 Roo.HtmlEditorCore.black = [
18911     //    'embed',  'object', // enable - backend responsiblity to clean thiese
18912         'applet', // 
18913         'base',   'basefont', 'bgsound', 'blink',  'body', 
18914         'frame',  'frameset', 'head',    'html',   'ilayer', 
18915         'iframe', 'layer',  'link',     'meta',    'object',   
18916         'script', 'style' ,'title',  'xml' // clean later..
18917 ];
18918 Roo.HtmlEditorCore.clean = [
18919     'script', 'style', 'title', 'xml'
18920 ];
18921 Roo.HtmlEditorCore.remove = [
18922     'font'
18923 ];
18924 // attributes..
18925
18926 Roo.HtmlEditorCore.ablack = [
18927     'on'
18928 ];
18929     
18930 Roo.HtmlEditorCore.aclean = [ 
18931     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
18932 ];
18933
18934 // protocols..
18935 Roo.HtmlEditorCore.pwhite= [
18936         'http',  'https',  'mailto'
18937 ];
18938
18939 // white listed style attributes.
18940 Roo.HtmlEditorCore.cwhite= [
18941       //  'text-align', /// default is to allow most things..
18942       
18943          
18944 //        'font-size'//??
18945 ];
18946
18947 // black listed style attributes.
18948 Roo.HtmlEditorCore.cblack= [
18949       //  'font-size' -- this can be set by the project 
18950 ];
18951
18952
18953 Roo.HtmlEditorCore.swapCodes   =[ 
18954     [    8211, "--" ], 
18955     [    8212, "--" ], 
18956     [    8216,  "'" ],  
18957     [    8217, "'" ],  
18958     [    8220, '"' ],  
18959     [    8221, '"' ],  
18960     [    8226, "*" ],  
18961     [    8230, "..." ]
18962 ]; 
18963
18964     /*
18965  * - LGPL
18966  *
18967  * HtmlEditor
18968  * 
18969  */
18970
18971 /**
18972  * @class Roo.bootstrap.HtmlEditor
18973  * @extends Roo.bootstrap.TextArea
18974  * Bootstrap HtmlEditor class
18975
18976  * @constructor
18977  * Create a new HtmlEditor
18978  * @param {Object} config The config object
18979  */
18980
18981 Roo.bootstrap.HtmlEditor = function(config){
18982     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
18983     if (!this.toolbars) {
18984         this.toolbars = [];
18985     }
18986     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
18987     this.addEvents({
18988             /**
18989              * @event initialize
18990              * Fires when the editor is fully initialized (including the iframe)
18991              * @param {HtmlEditor} this
18992              */
18993             initialize: true,
18994             /**
18995              * @event activate
18996              * Fires when the editor is first receives the focus. Any insertion must wait
18997              * until after this event.
18998              * @param {HtmlEditor} this
18999              */
19000             activate: true,
19001              /**
19002              * @event beforesync
19003              * Fires before the textarea is updated with content from the editor iframe. Return false
19004              * to cancel the sync.
19005              * @param {HtmlEditor} this
19006              * @param {String} html
19007              */
19008             beforesync: true,
19009              /**
19010              * @event beforepush
19011              * Fires before the iframe editor is updated with content from the textarea. Return false
19012              * to cancel the push.
19013              * @param {HtmlEditor} this
19014              * @param {String} html
19015              */
19016             beforepush: true,
19017              /**
19018              * @event sync
19019              * Fires when the textarea is updated with content from the editor iframe.
19020              * @param {HtmlEditor} this
19021              * @param {String} html
19022              */
19023             sync: true,
19024              /**
19025              * @event push
19026              * Fires when the iframe editor is updated with content from the textarea.
19027              * @param {HtmlEditor} this
19028              * @param {String} html
19029              */
19030             push: true,
19031              /**
19032              * @event editmodechange
19033              * Fires when the editor switches edit modes
19034              * @param {HtmlEditor} this
19035              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19036              */
19037             editmodechange: true,
19038             /**
19039              * @event editorevent
19040              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19041              * @param {HtmlEditor} this
19042              */
19043             editorevent: true,
19044             /**
19045              * @event firstfocus
19046              * Fires when on first focus - needed by toolbars..
19047              * @param {HtmlEditor} this
19048              */
19049             firstfocus: true,
19050             /**
19051              * @event autosave
19052              * Auto save the htmlEditor value as a file into Events
19053              * @param {HtmlEditor} this
19054              */
19055             autosave: true,
19056             /**
19057              * @event savedpreview
19058              * preview the saved version of htmlEditor
19059              * @param {HtmlEditor} this
19060              */
19061             savedpreview: true
19062         });
19063 };
19064
19065
19066 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19067     
19068     
19069       /**
19070      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19071      */
19072     toolbars : false,
19073    
19074      /**
19075      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19076      *                        Roo.resizable.
19077      */
19078     resizable : false,
19079      /**
19080      * @cfg {Number} height (in pixels)
19081      */   
19082     height: 300,
19083    /**
19084      * @cfg {Number} width (in pixels)
19085      */   
19086     width: false,
19087     
19088     /**
19089      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19090      * 
19091      */
19092     stylesheets: false,
19093     
19094     // id of frame..
19095     frameId: false,
19096     
19097     // private properties
19098     validationEvent : false,
19099     deferHeight: true,
19100     initialized : false,
19101     activated : false,
19102     
19103     onFocus : Roo.emptyFn,
19104     iframePad:3,
19105     hideMode:'offsets',
19106     
19107     
19108     tbContainer : false,
19109     
19110     toolbarContainer :function() {
19111         return this.wrap.select('.x-html-editor-tb',true).first();
19112     },
19113
19114     /**
19115      * Protected method that will not generally be called directly. It
19116      * is called when the editor creates its toolbar. Override this method if you need to
19117      * add custom toolbar buttons.
19118      * @param {HtmlEditor} editor
19119      */
19120     createToolbar : function(){
19121         
19122         Roo.log("create toolbars");
19123         
19124         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19125         this.toolbars[0].render(this.toolbarContainer());
19126         
19127         return;
19128         
19129 //        if (!editor.toolbars || !editor.toolbars.length) {
19130 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19131 //        }
19132 //        
19133 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19134 //            editor.toolbars[i] = Roo.factory(
19135 //                    typeof(editor.toolbars[i]) == 'string' ?
19136 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19137 //                Roo.bootstrap.HtmlEditor);
19138 //            editor.toolbars[i].init(editor);
19139 //        }
19140     },
19141
19142      
19143     // private
19144     onRender : function(ct, position)
19145     {
19146        // Roo.log("Call onRender: " + this.xtype);
19147         var _t = this;
19148         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19149       
19150         this.wrap = this.inputEl().wrap({
19151             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19152         });
19153         
19154         this.editorcore.onRender(ct, position);
19155          
19156         if (this.resizable) {
19157             this.resizeEl = new Roo.Resizable(this.wrap, {
19158                 pinned : true,
19159                 wrap: true,
19160                 dynamic : true,
19161                 minHeight : this.height,
19162                 height: this.height,
19163                 handles : this.resizable,
19164                 width: this.width,
19165                 listeners : {
19166                     resize : function(r, w, h) {
19167                         _t.onResize(w,h); // -something
19168                     }
19169                 }
19170             });
19171             
19172         }
19173         this.createToolbar(this);
19174        
19175         
19176         if(!this.width && this.resizable){
19177             this.setSize(this.wrap.getSize());
19178         }
19179         if (this.resizeEl) {
19180             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19181             // should trigger onReize..
19182         }
19183         
19184     },
19185
19186     // private
19187     onResize : function(w, h)
19188     {
19189         Roo.log('resize: ' +w + ',' + h );
19190         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19191         var ew = false;
19192         var eh = false;
19193         
19194         if(this.inputEl() ){
19195             if(typeof w == 'number'){
19196                 var aw = w - this.wrap.getFrameWidth('lr');
19197                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19198                 ew = aw;
19199             }
19200             if(typeof h == 'number'){
19201                  var tbh = -11;  // fixme it needs to tool bar size!
19202                 for (var i =0; i < this.toolbars.length;i++) {
19203                     // fixme - ask toolbars for heights?
19204                     tbh += this.toolbars[i].el.getHeight();
19205                     //if (this.toolbars[i].footer) {
19206                     //    tbh += this.toolbars[i].footer.el.getHeight();
19207                     //}
19208                 }
19209               
19210                 
19211                 
19212                 
19213                 
19214                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19215                 ah -= 5; // knock a few pixes off for look..
19216                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19217                 var eh = ah;
19218             }
19219         }
19220         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19221         this.editorcore.onResize(ew,eh);
19222         
19223     },
19224
19225     /**
19226      * Toggles the editor between standard and source edit mode.
19227      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19228      */
19229     toggleSourceEdit : function(sourceEditMode)
19230     {
19231         this.editorcore.toggleSourceEdit(sourceEditMode);
19232         
19233         if(this.editorcore.sourceEditMode){
19234             Roo.log('editor - showing textarea');
19235             
19236 //            Roo.log('in');
19237 //            Roo.log(this.syncValue());
19238             this.syncValue();
19239             this.inputEl().removeClass(['hide', 'x-hidden']);
19240             this.inputEl().dom.removeAttribute('tabIndex');
19241             this.inputEl().focus();
19242         }else{
19243             Roo.log('editor - hiding textarea');
19244 //            Roo.log('out')
19245 //            Roo.log(this.pushValue()); 
19246             this.pushValue();
19247             
19248             this.inputEl().addClass(['hide', 'x-hidden']);
19249             this.inputEl().dom.setAttribute('tabIndex', -1);
19250             //this.deferFocus();
19251         }
19252          
19253         if(this.resizable){
19254             this.setSize(this.wrap.getSize());
19255         }
19256         
19257         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19258     },
19259  
19260     // private (for BoxComponent)
19261     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19262
19263     // private (for BoxComponent)
19264     getResizeEl : function(){
19265         return this.wrap;
19266     },
19267
19268     // private (for BoxComponent)
19269     getPositionEl : function(){
19270         return this.wrap;
19271     },
19272
19273     // private
19274     initEvents : function(){
19275         this.originalValue = this.getValue();
19276     },
19277
19278 //    /**
19279 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19280 //     * @method
19281 //     */
19282 //    markInvalid : Roo.emptyFn,
19283 //    /**
19284 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19285 //     * @method
19286 //     */
19287 //    clearInvalid : Roo.emptyFn,
19288
19289     setValue : function(v){
19290         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19291         this.editorcore.pushValue();
19292     },
19293
19294      
19295     // private
19296     deferFocus : function(){
19297         this.focus.defer(10, this);
19298     },
19299
19300     // doc'ed in Field
19301     focus : function(){
19302         this.editorcore.focus();
19303         
19304     },
19305       
19306
19307     // private
19308     onDestroy : function(){
19309         
19310         
19311         
19312         if(this.rendered){
19313             
19314             for (var i =0; i < this.toolbars.length;i++) {
19315                 // fixme - ask toolbars for heights?
19316                 this.toolbars[i].onDestroy();
19317             }
19318             
19319             this.wrap.dom.innerHTML = '';
19320             this.wrap.remove();
19321         }
19322     },
19323
19324     // private
19325     onFirstFocus : function(){
19326         //Roo.log("onFirstFocus");
19327         this.editorcore.onFirstFocus();
19328          for (var i =0; i < this.toolbars.length;i++) {
19329             this.toolbars[i].onFirstFocus();
19330         }
19331         
19332     },
19333     
19334     // private
19335     syncValue : function()
19336     {   
19337         this.editorcore.syncValue();
19338     },
19339     
19340     pushValue : function()
19341     {   
19342         this.editorcore.pushValue();
19343     }
19344      
19345     
19346     // hide stuff that is not compatible
19347     /**
19348      * @event blur
19349      * @hide
19350      */
19351     /**
19352      * @event change
19353      * @hide
19354      */
19355     /**
19356      * @event focus
19357      * @hide
19358      */
19359     /**
19360      * @event specialkey
19361      * @hide
19362      */
19363     /**
19364      * @cfg {String} fieldClass @hide
19365      */
19366     /**
19367      * @cfg {String} focusClass @hide
19368      */
19369     /**
19370      * @cfg {String} autoCreate @hide
19371      */
19372     /**
19373      * @cfg {String} inputType @hide
19374      */
19375     /**
19376      * @cfg {String} invalidClass @hide
19377      */
19378     /**
19379      * @cfg {String} invalidText @hide
19380      */
19381     /**
19382      * @cfg {String} msgFx @hide
19383      */
19384     /**
19385      * @cfg {String} validateOnBlur @hide
19386      */
19387 });
19388  
19389     
19390    
19391    
19392    
19393       
19394 Roo.namespace('Roo.bootstrap.htmleditor');
19395 /**
19396  * @class Roo.bootstrap.HtmlEditorToolbar1
19397  * Basic Toolbar
19398  * 
19399  * Usage:
19400  *
19401  new Roo.bootstrap.HtmlEditor({
19402     ....
19403     toolbars : [
19404         new Roo.bootstrap.HtmlEditorToolbar1({
19405             disable : { fonts: 1 , format: 1, ..., ... , ...],
19406             btns : [ .... ]
19407         })
19408     }
19409      
19410  * 
19411  * @cfg {Object} disable List of elements to disable..
19412  * @cfg {Array} btns List of additional buttons.
19413  * 
19414  * 
19415  * NEEDS Extra CSS? 
19416  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19417  */
19418  
19419 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19420 {
19421     
19422     Roo.apply(this, config);
19423     
19424     // default disabled, based on 'good practice'..
19425     this.disable = this.disable || {};
19426     Roo.applyIf(this.disable, {
19427         fontSize : true,
19428         colors : true,
19429         specialElements : true
19430     });
19431     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
19432     
19433     this.editor = config.editor;
19434     this.editorcore = config.editor.editorcore;
19435     
19436     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
19437     
19438     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
19439     // dont call parent... till later.
19440 }
19441 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
19442      
19443     bar : true,
19444     
19445     editor : false,
19446     editorcore : false,
19447     
19448     
19449     formats : [
19450         "p" ,  
19451         "h1","h2","h3","h4","h5","h6", 
19452         "pre", "code", 
19453         "abbr", "acronym", "address", "cite", "samp", "var",
19454         'div','span'
19455     ],
19456     
19457     onRender : function(ct, position)
19458     {
19459        // Roo.log("Call onRender: " + this.xtype);
19460         
19461        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
19462        Roo.log(this.el);
19463        this.el.dom.style.marginBottom = '0';
19464        var _this = this;
19465        var editorcore = this.editorcore;
19466        var editor= this.editor;
19467        
19468        var children = [];
19469        var btn = function(id,cmd , toggle, handler){
19470        
19471             var  event = toggle ? 'toggle' : 'click';
19472        
19473             var a = {
19474                 size : 'sm',
19475                 xtype: 'Button',
19476                 xns: Roo.bootstrap,
19477                 glyphicon : id,
19478                 cmd : id || cmd,
19479                 enableToggle:toggle !== false,
19480                 //html : 'submit'
19481                 pressed : toggle ? false : null,
19482                 listeners : {}
19483             }
19484             a.listeners[toggle ? 'toggle' : 'click'] = function() {
19485                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
19486             }
19487             children.push(a);
19488             return a;
19489        }
19490         
19491         var style = {
19492                 xtype: 'Button',
19493                 size : 'sm',
19494                 xns: Roo.bootstrap,
19495                 glyphicon : 'font',
19496                 //html : 'submit'
19497                 menu : {
19498                     xtype: 'Menu',
19499                     xns: Roo.bootstrap,
19500                     items:  []
19501                 }
19502         };
19503         Roo.each(this.formats, function(f) {
19504             style.menu.items.push({
19505                 xtype :'MenuItem',
19506                 xns: Roo.bootstrap,
19507                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
19508                 tagname : f,
19509                 listeners : {
19510                     click : function()
19511                     {
19512                         editorcore.insertTag(this.tagname);
19513                         editor.focus();
19514                     }
19515                 }
19516                 
19517             });
19518         });
19519          children.push(style);   
19520             
19521             
19522         btn('bold',false,true);
19523         btn('italic',false,true);
19524         btn('align-left', 'justifyleft',true);
19525         btn('align-center', 'justifycenter',true);
19526         btn('align-right' , 'justifyright',true);
19527         btn('link', false, false, function(btn) {
19528             //Roo.log("create link?");
19529             var url = prompt(this.createLinkText, this.defaultLinkValue);
19530             if(url && url != 'http:/'+'/'){
19531                 this.editorcore.relayCmd('createlink', url);
19532             }
19533         }),
19534         btn('list','insertunorderedlist',true);
19535         btn('pencil', false,true, function(btn){
19536                 Roo.log(this);
19537                 
19538                 this.toggleSourceEdit(btn.pressed);
19539         });
19540         /*
19541         var cog = {
19542                 xtype: 'Button',
19543                 size : 'sm',
19544                 xns: Roo.bootstrap,
19545                 glyphicon : 'cog',
19546                 //html : 'submit'
19547                 menu : {
19548                     xtype: 'Menu',
19549                     xns: Roo.bootstrap,
19550                     items:  []
19551                 }
19552         };
19553         
19554         cog.menu.items.push({
19555             xtype :'MenuItem',
19556             xns: Roo.bootstrap,
19557             html : Clean styles,
19558             tagname : f,
19559             listeners : {
19560                 click : function()
19561                 {
19562                     editorcore.insertTag(this.tagname);
19563                     editor.focus();
19564                 }
19565             }
19566             
19567         });
19568        */
19569         
19570          
19571        this.xtype = 'NavSimplebar';
19572         
19573         for(var i=0;i< children.length;i++) {
19574             
19575             this.buttons.add(this.addxtypeChild(children[i]));
19576             
19577         }
19578         
19579         editor.on('editorevent', this.updateToolbar, this);
19580     },
19581     onBtnClick : function(id)
19582     {
19583        this.editorcore.relayCmd(id);
19584        this.editorcore.focus();
19585     },
19586     
19587     /**
19588      * Protected method that will not generally be called directly. It triggers
19589      * a toolbar update by reading the markup state of the current selection in the editor.
19590      */
19591     updateToolbar: function(){
19592
19593         if(!this.editorcore.activated){
19594             this.editor.onFirstFocus(); // is this neeed?
19595             return;
19596         }
19597
19598         var btns = this.buttons; 
19599         var doc = this.editorcore.doc;
19600         btns.get('bold').setActive(doc.queryCommandState('bold'));
19601         btns.get('italic').setActive(doc.queryCommandState('italic'));
19602         //btns.get('underline').setActive(doc.queryCommandState('underline'));
19603         
19604         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
19605         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
19606         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
19607         
19608         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
19609         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
19610          /*
19611         
19612         var ans = this.editorcore.getAllAncestors();
19613         if (this.formatCombo) {
19614             
19615             
19616             var store = this.formatCombo.store;
19617             this.formatCombo.setValue("");
19618             for (var i =0; i < ans.length;i++) {
19619                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
19620                     // select it..
19621                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
19622                     break;
19623                 }
19624             }
19625         }
19626         
19627         
19628         
19629         // hides menus... - so this cant be on a menu...
19630         Roo.bootstrap.MenuMgr.hideAll();
19631         */
19632         Roo.bootstrap.MenuMgr.hideAll();
19633         //this.editorsyncValue();
19634     },
19635     onFirstFocus: function() {
19636         this.buttons.each(function(item){
19637            item.enable();
19638         });
19639     },
19640     toggleSourceEdit : function(sourceEditMode){
19641         
19642           
19643         if(sourceEditMode){
19644             Roo.log("disabling buttons");
19645            this.buttons.each( function(item){
19646                 if(item.cmd != 'pencil'){
19647                     item.disable();
19648                 }
19649             });
19650           
19651         }else{
19652             Roo.log("enabling buttons");
19653             if(this.editorcore.initialized){
19654                 this.buttons.each( function(item){
19655                     item.enable();
19656                 });
19657             }
19658             
19659         }
19660         Roo.log("calling toggole on editor");
19661         // tell the editor that it's been pressed..
19662         this.editor.toggleSourceEdit(sourceEditMode);
19663        
19664     }
19665 });
19666
19667
19668
19669
19670
19671 /**
19672  * @class Roo.bootstrap.Table.AbstractSelectionModel
19673  * @extends Roo.util.Observable
19674  * Abstract base class for grid SelectionModels.  It provides the interface that should be
19675  * implemented by descendant classes.  This class should not be directly instantiated.
19676  * @constructor
19677  */
19678 Roo.bootstrap.Table.AbstractSelectionModel = function(){
19679     this.locked = false;
19680     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
19681 };
19682
19683
19684 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
19685     /** @ignore Called by the grid automatically. Do not call directly. */
19686     init : function(grid){
19687         this.grid = grid;
19688         this.initEvents();
19689     },
19690
19691     /**
19692      * Locks the selections.
19693      */
19694     lock : function(){
19695         this.locked = true;
19696     },
19697
19698     /**
19699      * Unlocks the selections.
19700      */
19701     unlock : function(){
19702         this.locked = false;
19703     },
19704
19705     /**
19706      * Returns true if the selections are locked.
19707      * @return {Boolean}
19708      */
19709     isLocked : function(){
19710         return this.locked;
19711     }
19712 });
19713 /**
19714  * @extends Roo.bootstrap.Table.AbstractSelectionModel
19715  * @class Roo.bootstrap.Table.RowSelectionModel
19716  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
19717  * It supports multiple selections and keyboard selection/navigation. 
19718  * @constructor
19719  * @param {Object} config
19720  */
19721
19722 Roo.bootstrap.Table.RowSelectionModel = function(config){
19723     Roo.apply(this, config);
19724     this.selections = new Roo.util.MixedCollection(false, function(o){
19725         return o.id;
19726     });
19727
19728     this.last = false;
19729     this.lastActive = false;
19730
19731     this.addEvents({
19732         /**
19733              * @event selectionchange
19734              * Fires when the selection changes
19735              * @param {SelectionModel} this
19736              */
19737             "selectionchange" : true,
19738         /**
19739              * @event afterselectionchange
19740              * Fires after the selection changes (eg. by key press or clicking)
19741              * @param {SelectionModel} this
19742              */
19743             "afterselectionchange" : true,
19744         /**
19745              * @event beforerowselect
19746              * Fires when a row is selected being selected, return false to cancel.
19747              * @param {SelectionModel} this
19748              * @param {Number} rowIndex The selected index
19749              * @param {Boolean} keepExisting False if other selections will be cleared
19750              */
19751             "beforerowselect" : true,
19752         /**
19753              * @event rowselect
19754              * Fires when a row is selected.
19755              * @param {SelectionModel} this
19756              * @param {Number} rowIndex The selected index
19757              * @param {Roo.data.Record} r The record
19758              */
19759             "rowselect" : true,
19760         /**
19761              * @event rowdeselect
19762              * Fires when a row is deselected.
19763              * @param {SelectionModel} this
19764              * @param {Number} rowIndex The selected index
19765              */
19766         "rowdeselect" : true
19767     });
19768     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
19769     this.locked = false;
19770 };
19771
19772 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
19773     /**
19774      * @cfg {Boolean} singleSelect
19775      * True to allow selection of only one row at a time (defaults to false)
19776      */
19777     singleSelect : false,
19778
19779     // private
19780     initEvents : function(){
19781
19782         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
19783             this.grid.on("mousedown", this.handleMouseDown, this);
19784         }else{ // allow click to work like normal
19785             this.grid.on("rowclick", this.handleDragableRowClick, this);
19786         }
19787
19788         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
19789             "up" : function(e){
19790                 if(!e.shiftKey){
19791                     this.selectPrevious(e.shiftKey);
19792                 }else if(this.last !== false && this.lastActive !== false){
19793                     var last = this.last;
19794                     this.selectRange(this.last,  this.lastActive-1);
19795                     this.grid.getView().focusRow(this.lastActive);
19796                     if(last !== false){
19797                         this.last = last;
19798                     }
19799                 }else{
19800                     this.selectFirstRow();
19801                 }
19802                 this.fireEvent("afterselectionchange", this);
19803             },
19804             "down" : function(e){
19805                 if(!e.shiftKey){
19806                     this.selectNext(e.shiftKey);
19807                 }else if(this.last !== false && this.lastActive !== false){
19808                     var last = this.last;
19809                     this.selectRange(this.last,  this.lastActive+1);
19810                     this.grid.getView().focusRow(this.lastActive);
19811                     if(last !== false){
19812                         this.last = last;
19813                     }
19814                 }else{
19815                     this.selectFirstRow();
19816                 }
19817                 this.fireEvent("afterselectionchange", this);
19818             },
19819             scope: this
19820         });
19821
19822         var view = this.grid.view;
19823         view.on("refresh", this.onRefresh, this);
19824         view.on("rowupdated", this.onRowUpdated, this);
19825         view.on("rowremoved", this.onRemove, this);
19826     },
19827
19828     // private
19829     onRefresh : function(){
19830         var ds = this.grid.dataSource, i, v = this.grid.view;
19831         var s = this.selections;
19832         s.each(function(r){
19833             if((i = ds.indexOfId(r.id)) != -1){
19834                 v.onRowSelect(i);
19835             }else{
19836                 s.remove(r);
19837             }
19838         });
19839     },
19840
19841     // private
19842     onRemove : function(v, index, r){
19843         this.selections.remove(r);
19844     },
19845
19846     // private
19847     onRowUpdated : function(v, index, r){
19848         if(this.isSelected(r)){
19849             v.onRowSelect(index);
19850         }
19851     },
19852
19853     /**
19854      * Select records.
19855      * @param {Array} records The records to select
19856      * @param {Boolean} keepExisting (optional) True to keep existing selections
19857      */
19858     selectRecords : function(records, keepExisting){
19859         if(!keepExisting){
19860             this.clearSelections();
19861         }
19862         var ds = this.grid.dataSource;
19863         for(var i = 0, len = records.length; i < len; i++){
19864             this.selectRow(ds.indexOf(records[i]), true);
19865         }
19866     },
19867
19868     /**
19869      * Gets the number of selected rows.
19870      * @return {Number}
19871      */
19872     getCount : function(){
19873         return this.selections.length;
19874     },
19875
19876     /**
19877      * Selects the first row in the grid.
19878      */
19879     selectFirstRow : function(){
19880         this.selectRow(0);
19881     },
19882
19883     /**
19884      * Select the last row.
19885      * @param {Boolean} keepExisting (optional) True to keep existing selections
19886      */
19887     selectLastRow : function(keepExisting){
19888         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
19889     },
19890
19891     /**
19892      * Selects the row immediately following the last selected row.
19893      * @param {Boolean} keepExisting (optional) True to keep existing selections
19894      */
19895     selectNext : function(keepExisting){
19896         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
19897             this.selectRow(this.last+1, keepExisting);
19898             this.grid.getView().focusRow(this.last);
19899         }
19900     },
19901
19902     /**
19903      * Selects the row that precedes the last selected row.
19904      * @param {Boolean} keepExisting (optional) True to keep existing selections
19905      */
19906     selectPrevious : function(keepExisting){
19907         if(this.last){
19908             this.selectRow(this.last-1, keepExisting);
19909             this.grid.getView().focusRow(this.last);
19910         }
19911     },
19912
19913     /**
19914      * Returns the selected records
19915      * @return {Array} Array of selected records
19916      */
19917     getSelections : function(){
19918         return [].concat(this.selections.items);
19919     },
19920
19921     /**
19922      * Returns the first selected record.
19923      * @return {Record}
19924      */
19925     getSelected : function(){
19926         return this.selections.itemAt(0);
19927     },
19928
19929
19930     /**
19931      * Clears all selections.
19932      */
19933     clearSelections : function(fast){
19934         if(this.locked) return;
19935         if(fast !== true){
19936             var ds = this.grid.dataSource;
19937             var s = this.selections;
19938             s.each(function(r){
19939                 this.deselectRow(ds.indexOfId(r.id));
19940             }, this);
19941             s.clear();
19942         }else{
19943             this.selections.clear();
19944         }
19945         this.last = false;
19946     },
19947
19948
19949     /**
19950      * Selects all rows.
19951      */
19952     selectAll : function(){
19953         if(this.locked) return;
19954         this.selections.clear();
19955         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
19956             this.selectRow(i, true);
19957         }
19958     },
19959
19960     /**
19961      * Returns True if there is a selection.
19962      * @return {Boolean}
19963      */
19964     hasSelection : function(){
19965         return this.selections.length > 0;
19966     },
19967
19968     /**
19969      * Returns True if the specified row is selected.
19970      * @param {Number/Record} record The record or index of the record to check
19971      * @return {Boolean}
19972      */
19973     isSelected : function(index){
19974         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
19975         return (r && this.selections.key(r.id) ? true : false);
19976     },
19977
19978     /**
19979      * Returns True if the specified record id is selected.
19980      * @param {String} id The id of record to check
19981      * @return {Boolean}
19982      */
19983     isIdSelected : function(id){
19984         return (this.selections.key(id) ? true : false);
19985     },
19986
19987     // private
19988     handleMouseDown : function(e, t){
19989         var view = this.grid.getView(), rowIndex;
19990         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
19991             return;
19992         };
19993         if(e.shiftKey && this.last !== false){
19994             var last = this.last;
19995             this.selectRange(last, rowIndex, e.ctrlKey);
19996             this.last = last; // reset the last
19997             view.focusRow(rowIndex);
19998         }else{
19999             var isSelected = this.isSelected(rowIndex);
20000             if(e.button !== 0 && isSelected){
20001                 view.focusRow(rowIndex);
20002             }else if(e.ctrlKey && isSelected){
20003                 this.deselectRow(rowIndex);
20004             }else if(!isSelected){
20005                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20006                 view.focusRow(rowIndex);
20007             }
20008         }
20009         this.fireEvent("afterselectionchange", this);
20010     },
20011     // private
20012     handleDragableRowClick :  function(grid, rowIndex, e) 
20013     {
20014         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20015             this.selectRow(rowIndex, false);
20016             grid.view.focusRow(rowIndex);
20017              this.fireEvent("afterselectionchange", this);
20018         }
20019     },
20020     
20021     /**
20022      * Selects multiple rows.
20023      * @param {Array} rows Array of the indexes of the row to select
20024      * @param {Boolean} keepExisting (optional) True to keep existing selections
20025      */
20026     selectRows : function(rows, keepExisting){
20027         if(!keepExisting){
20028             this.clearSelections();
20029         }
20030         for(var i = 0, len = rows.length; i < len; i++){
20031             this.selectRow(rows[i], true);
20032         }
20033     },
20034
20035     /**
20036      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20037      * @param {Number} startRow The index of the first row in the range
20038      * @param {Number} endRow The index of the last row in the range
20039      * @param {Boolean} keepExisting (optional) True to retain existing selections
20040      */
20041     selectRange : function(startRow, endRow, keepExisting){
20042         if(this.locked) return;
20043         if(!keepExisting){
20044             this.clearSelections();
20045         }
20046         if(startRow <= endRow){
20047             for(var i = startRow; i <= endRow; i++){
20048                 this.selectRow(i, true);
20049             }
20050         }else{
20051             for(var i = startRow; i >= endRow; i--){
20052                 this.selectRow(i, true);
20053             }
20054         }
20055     },
20056
20057     /**
20058      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20059      * @param {Number} startRow The index of the first row in the range
20060      * @param {Number} endRow The index of the last row in the range
20061      */
20062     deselectRange : function(startRow, endRow, preventViewNotify){
20063         if(this.locked) return;
20064         for(var i = startRow; i <= endRow; i++){
20065             this.deselectRow(i, preventViewNotify);
20066         }
20067     },
20068
20069     /**
20070      * Selects a row.
20071      * @param {Number} row The index of the row to select
20072      * @param {Boolean} keepExisting (optional) True to keep existing selections
20073      */
20074     selectRow : function(index, keepExisting, preventViewNotify){
20075         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20076         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20077             if(!keepExisting || this.singleSelect){
20078                 this.clearSelections();
20079             }
20080             var r = this.grid.dataSource.getAt(index);
20081             this.selections.add(r);
20082             this.last = this.lastActive = index;
20083             if(!preventViewNotify){
20084                 this.grid.getView().onRowSelect(index);
20085             }
20086             this.fireEvent("rowselect", this, index, r);
20087             this.fireEvent("selectionchange", this);
20088         }
20089     },
20090
20091     /**
20092      * Deselects a row.
20093      * @param {Number} row The index of the row to deselect
20094      */
20095     deselectRow : function(index, preventViewNotify){
20096         if(this.locked) return;
20097         if(this.last == index){
20098             this.last = false;
20099         }
20100         if(this.lastActive == index){
20101             this.lastActive = false;
20102         }
20103         var r = this.grid.dataSource.getAt(index);
20104         this.selections.remove(r);
20105         if(!preventViewNotify){
20106             this.grid.getView().onRowDeselect(index);
20107         }
20108         this.fireEvent("rowdeselect", this, index);
20109         this.fireEvent("selectionchange", this);
20110     },
20111
20112     // private
20113     restoreLast : function(){
20114         if(this._last){
20115             this.last = this._last;
20116         }
20117     },
20118
20119     // private
20120     acceptsNav : function(row, col, cm){
20121         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20122     },
20123
20124     // private
20125     onEditorKey : function(field, e){
20126         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20127         if(k == e.TAB){
20128             e.stopEvent();
20129             ed.completeEdit();
20130             if(e.shiftKey){
20131                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20132             }else{
20133                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20134             }
20135         }else if(k == e.ENTER && !e.ctrlKey){
20136             e.stopEvent();
20137             ed.completeEdit();
20138             if(e.shiftKey){
20139                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20140             }else{
20141                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20142             }
20143         }else if(k == e.ESC){
20144             ed.cancelEdit();
20145         }
20146         if(newCell){
20147             g.startEditing(newCell[0], newCell[1]);
20148         }
20149     }
20150 });/*
20151  * Based on:
20152  * Ext JS Library 1.1.1
20153  * Copyright(c) 2006-2007, Ext JS, LLC.
20154  *
20155  * Originally Released Under LGPL - original licence link has changed is not relivant.
20156  *
20157  * Fork - LGPL
20158  * <script type="text/javascript">
20159  */
20160  
20161 /**
20162  * @class Roo.bootstrap.PagingToolbar
20163  * @extends Roo.Row
20164  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20165  * @constructor
20166  * Create a new PagingToolbar
20167  * @param {Object} config The config object
20168  */
20169 Roo.bootstrap.PagingToolbar = function(config)
20170 {
20171     // old args format still supported... - xtype is prefered..
20172         // created from xtype...
20173     var ds = config.dataSource;
20174     this.toolbarItems = [];
20175     if (config.items) {
20176         this.toolbarItems = config.items;
20177 //        config.items = [];
20178     }
20179     
20180     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20181     this.ds = ds;
20182     this.cursor = 0;
20183     if (ds) { 
20184         this.bind(ds);
20185     }
20186     
20187     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20188     
20189 };
20190
20191 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20192     /**
20193      * @cfg {Roo.data.Store} dataSource
20194      * The underlying data store providing the paged data
20195      */
20196     /**
20197      * @cfg {String/HTMLElement/Element} container
20198      * container The id or element that will contain the toolbar
20199      */
20200     /**
20201      * @cfg {Boolean} displayInfo
20202      * True to display the displayMsg (defaults to false)
20203      */
20204     /**
20205      * @cfg {Number} pageSize
20206      * The number of records to display per page (defaults to 20)
20207      */
20208     pageSize: 20,
20209     /**
20210      * @cfg {String} displayMsg
20211      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20212      */
20213     displayMsg : 'Displaying {0} - {1} of {2}',
20214     /**
20215      * @cfg {String} emptyMsg
20216      * The message to display when no records are found (defaults to "No data to display")
20217      */
20218     emptyMsg : 'No data to display',
20219     /**
20220      * Customizable piece of the default paging text (defaults to "Page")
20221      * @type String
20222      */
20223     beforePageText : "Page",
20224     /**
20225      * Customizable piece of the default paging text (defaults to "of %0")
20226      * @type String
20227      */
20228     afterPageText : "of {0}",
20229     /**
20230      * Customizable piece of the default paging text (defaults to "First Page")
20231      * @type String
20232      */
20233     firstText : "First Page",
20234     /**
20235      * Customizable piece of the default paging text (defaults to "Previous Page")
20236      * @type String
20237      */
20238     prevText : "Previous Page",
20239     /**
20240      * Customizable piece of the default paging text (defaults to "Next Page")
20241      * @type String
20242      */
20243     nextText : "Next Page",
20244     /**
20245      * Customizable piece of the default paging text (defaults to "Last Page")
20246      * @type String
20247      */
20248     lastText : "Last Page",
20249     /**
20250      * Customizable piece of the default paging text (defaults to "Refresh")
20251      * @type String
20252      */
20253     refreshText : "Refresh",
20254
20255     buttons : false,
20256     // private
20257     onRender : function(ct, position) 
20258     {
20259         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20260         this.navgroup.parentId = this.id;
20261         this.navgroup.onRender(this.el, null);
20262         // add the buttons to the navgroup
20263         
20264         if(this.displayInfo){
20265             Roo.log(this.el.select('ul.navbar-nav',true).first());
20266             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20267             this.displayEl = this.el.select('.x-paging-info', true).first();
20268 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20269 //            this.displayEl = navel.el.select('span',true).first();
20270         }
20271         
20272         var _this = this;
20273         
20274         if(this.buttons){
20275             Roo.each(_this.buttons, function(e){
20276                Roo.factory(e).onRender(_this.el, null);
20277             });
20278         }
20279             
20280         Roo.each(_this.toolbarItems, function(e) {
20281             _this.navgroup.addItem(e);
20282         });
20283         
20284         
20285         this.first = this.navgroup.addItem({
20286             tooltip: this.firstText,
20287             cls: "prev",
20288             icon : 'fa fa-backward',
20289             disabled: true,
20290             preventDefault: true,
20291             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20292         });
20293         
20294         this.prev =  this.navgroup.addItem({
20295             tooltip: this.prevText,
20296             cls: "prev",
20297             icon : 'fa fa-step-backward',
20298             disabled: true,
20299             preventDefault: true,
20300             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20301         });
20302     //this.addSeparator();
20303         
20304         
20305         var field = this.navgroup.addItem( {
20306             tagtype : 'span',
20307             cls : 'x-paging-position',
20308             
20309             html : this.beforePageText  +
20310                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20311                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20312          } ); //?? escaped?
20313         
20314         this.field = field.el.select('input', true).first();
20315         this.field.on("keydown", this.onPagingKeydown, this);
20316         this.field.on("focus", function(){this.dom.select();});
20317     
20318     
20319         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20320         //this.field.setHeight(18);
20321         //this.addSeparator();
20322         this.next = this.navgroup.addItem({
20323             tooltip: this.nextText,
20324             cls: "next",
20325             html : ' <i class="fa fa-step-forward">',
20326             disabled: true,
20327             preventDefault: true,
20328             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20329         });
20330         this.last = this.navgroup.addItem({
20331             tooltip: this.lastText,
20332             icon : 'fa fa-forward',
20333             cls: "next",
20334             disabled: true,
20335             preventDefault: true,
20336             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20337         });
20338     //this.addSeparator();
20339         this.loading = this.navgroup.addItem({
20340             tooltip: this.refreshText,
20341             icon: 'fa fa-refresh',
20342             preventDefault: true,
20343             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20344         });
20345
20346     },
20347
20348     // private
20349     updateInfo : function(){
20350         if(this.displayEl){
20351             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20352             var msg = count == 0 ?
20353                 this.emptyMsg :
20354                 String.format(
20355                     this.displayMsg,
20356                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20357                 );
20358             this.displayEl.update(msg);
20359         }
20360     },
20361
20362     // private
20363     onLoad : function(ds, r, o){
20364        this.cursor = o.params ? o.params.start : 0;
20365        var d = this.getPageData(),
20366             ap = d.activePage,
20367             ps = d.pages;
20368         
20369        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20370        this.field.dom.value = ap;
20371        this.first.setDisabled(ap == 1);
20372        this.prev.setDisabled(ap == 1);
20373        this.next.setDisabled(ap == ps);
20374        this.last.setDisabled(ap == ps);
20375        this.loading.enable();
20376        this.updateInfo();
20377     },
20378
20379     // private
20380     getPageData : function(){
20381         var total = this.ds.getTotalCount();
20382         return {
20383             total : total,
20384             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20385             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20386         };
20387     },
20388
20389     // private
20390     onLoadError : function(){
20391         this.loading.enable();
20392     },
20393
20394     // private
20395     onPagingKeydown : function(e){
20396         var k = e.getKey();
20397         var d = this.getPageData();
20398         if(k == e.RETURN){
20399             var v = this.field.dom.value, pageNum;
20400             if(!v || isNaN(pageNum = parseInt(v, 10))){
20401                 this.field.dom.value = d.activePage;
20402                 return;
20403             }
20404             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20405             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20406             e.stopEvent();
20407         }
20408         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))
20409         {
20410           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20411           this.field.dom.value = pageNum;
20412           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20413           e.stopEvent();
20414         }
20415         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20416         {
20417           var v = this.field.dom.value, pageNum; 
20418           var increment = (e.shiftKey) ? 10 : 1;
20419           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20420             increment *= -1;
20421           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20422             this.field.dom.value = d.activePage;
20423             return;
20424           }
20425           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
20426           {
20427             this.field.dom.value = parseInt(v, 10) + increment;
20428             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
20429             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20430           }
20431           e.stopEvent();
20432         }
20433     },
20434
20435     // private
20436     beforeLoad : function(){
20437         if(this.loading){
20438             this.loading.disable();
20439         }
20440     },
20441
20442     // private
20443     onClick : function(which){
20444         
20445         var ds = this.ds;
20446         if (!ds) {
20447             return;
20448         }
20449         
20450         switch(which){
20451             case "first":
20452                 ds.load({params:{start: 0, limit: this.pageSize}});
20453             break;
20454             case "prev":
20455                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
20456             break;
20457             case "next":
20458                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
20459             break;
20460             case "last":
20461                 var total = ds.getTotalCount();
20462                 var extra = total % this.pageSize;
20463                 var lastStart = extra ? (total - extra) : total-this.pageSize;
20464                 ds.load({params:{start: lastStart, limit: this.pageSize}});
20465             break;
20466             case "refresh":
20467                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
20468             break;
20469         }
20470     },
20471
20472     /**
20473      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
20474      * @param {Roo.data.Store} store The data store to unbind
20475      */
20476     unbind : function(ds){
20477         ds.un("beforeload", this.beforeLoad, this);
20478         ds.un("load", this.onLoad, this);
20479         ds.un("loadexception", this.onLoadError, this);
20480         ds.un("remove", this.updateInfo, this);
20481         ds.un("add", this.updateInfo, this);
20482         this.ds = undefined;
20483     },
20484
20485     /**
20486      * Binds the paging toolbar to the specified {@link Roo.data.Store}
20487      * @param {Roo.data.Store} store The data store to bind
20488      */
20489     bind : function(ds){
20490         ds.on("beforeload", this.beforeLoad, this);
20491         ds.on("load", this.onLoad, this);
20492         ds.on("loadexception", this.onLoadError, this);
20493         ds.on("remove", this.updateInfo, this);
20494         ds.on("add", this.updateInfo, this);
20495         this.ds = ds;
20496     }
20497 });/*
20498  * - LGPL
20499  *
20500  * element
20501  * 
20502  */
20503
20504 /**
20505  * @class Roo.bootstrap.MessageBar
20506  * @extends Roo.bootstrap.Component
20507  * Bootstrap MessageBar class
20508  * @cfg {String} html contents of the MessageBar
20509  * @cfg {String} weight (info | success | warning | danger) default info
20510  * @cfg {String} beforeClass insert the bar before the given class
20511  * @cfg {Boolean} closable (true | false) default false
20512  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
20513  * 
20514  * @constructor
20515  * Create a new Element
20516  * @param {Object} config The config object
20517  */
20518
20519 Roo.bootstrap.MessageBar = function(config){
20520     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
20521 };
20522
20523 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
20524     
20525     html: '',
20526     weight: 'info',
20527     closable: false,
20528     fixed: false,
20529     beforeClass: 'bootstrap-sticky-wrap',
20530     
20531     getAutoCreate : function(){
20532         
20533         var cfg = {
20534             tag: 'div',
20535             cls: 'alert alert-dismissable alert-' + this.weight,
20536             cn: [
20537                 {
20538                     tag: 'span',
20539                     cls: 'message',
20540                     html: this.html || ''
20541                 }
20542             ]
20543         }
20544         
20545         if(this.fixed){
20546             cfg.cls += ' alert-messages-fixed';
20547         }
20548         
20549         if(this.closable){
20550             cfg.cn.push({
20551                 tag: 'button',
20552                 cls: 'close',
20553                 html: 'x'
20554             });
20555         }
20556         
20557         return cfg;
20558     },
20559     
20560     onRender : function(ct, position)
20561     {
20562         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20563         
20564         if(!this.el){
20565             var cfg = Roo.apply({},  this.getAutoCreate());
20566             cfg.id = Roo.id();
20567             
20568             if (this.cls) {
20569                 cfg.cls += ' ' + this.cls;
20570             }
20571             if (this.style) {
20572                 cfg.style = this.style;
20573             }
20574             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
20575             
20576             this.el.setVisibilityMode(Roo.Element.DISPLAY);
20577         }
20578         
20579         this.el.select('>button.close').on('click', this.hide, this);
20580         
20581     },
20582     
20583     show : function()
20584     {
20585         if (!this.rendered) {
20586             this.render();
20587         }
20588         
20589         this.el.show();
20590         
20591         this.fireEvent('show', this);
20592         
20593     },
20594     
20595     hide : function()
20596     {
20597         if (!this.rendered) {
20598             this.render();
20599         }
20600         
20601         this.el.hide();
20602         
20603         this.fireEvent('hide', this);
20604     },
20605     
20606     update : function()
20607     {
20608 //        var e = this.el.dom.firstChild;
20609 //        
20610 //        if(this.closable){
20611 //            e = e.nextSibling;
20612 //        }
20613 //        
20614 //        e.data = this.html || '';
20615
20616         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
20617     }
20618    
20619 });
20620
20621  
20622
20623      /*
20624  * - LGPL
20625  *
20626  * Graph
20627  * 
20628  */
20629
20630
20631 /**
20632  * @class Roo.bootstrap.Graph
20633  * @extends Roo.bootstrap.Component
20634  * Bootstrap Graph class
20635 > Prameters
20636  -sm {number} sm 4
20637  -md {number} md 5
20638  @cfg {String} graphtype  bar | vbar | pie
20639  @cfg {number} g_x coodinator | centre x (pie)
20640  @cfg {number} g_y coodinator | centre y (pie)
20641  @cfg {number} g_r radius (pie)
20642  @cfg {number} g_height height of the chart (respected by all elements in the set)
20643  @cfg {number} g_width width of the chart (respected by all elements in the set)
20644  @cfg {Object} title The title of the chart
20645     
20646  -{Array}  values
20647  -opts (object) options for the chart 
20648      o {
20649      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
20650      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
20651      o vgutter (number)
20652      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.
20653      o stacked (boolean) whether or not to tread values as in a stacked bar chart
20654      o to
20655      o stretch (boolean)
20656      o }
20657  -opts (object) options for the pie
20658      o{
20659      o cut
20660      o startAngle (number)
20661      o endAngle (number)
20662      } 
20663  *
20664  * @constructor
20665  * Create a new Input
20666  * @param {Object} config The config object
20667  */
20668
20669 Roo.bootstrap.Graph = function(config){
20670     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
20671     
20672     this.addEvents({
20673         // img events
20674         /**
20675          * @event click
20676          * The img click event for the img.
20677          * @param {Roo.EventObject} e
20678          */
20679         "click" : true
20680     });
20681 };
20682
20683 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
20684     
20685     sm: 4,
20686     md: 5,
20687     graphtype: 'bar',
20688     g_height: 250,
20689     g_width: 400,
20690     g_x: 50,
20691     g_y: 50,
20692     g_r: 30,
20693     opts:{
20694         //g_colors: this.colors,
20695         g_type: 'soft',
20696         g_gutter: '20%'
20697
20698     },
20699     title : false,
20700
20701     getAutoCreate : function(){
20702         
20703         var cfg = {
20704             tag: 'div',
20705             html : null
20706         }
20707         
20708         
20709         return  cfg;
20710     },
20711
20712     onRender : function(ct,position){
20713         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
20714         this.raphael = Raphael(this.el.dom);
20715         
20716                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
20717                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
20718                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
20719                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
20720                 /*
20721                 r.text(160, 10, "Single Series Chart").attr(txtattr);
20722                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
20723                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
20724                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
20725                 
20726                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
20727                 r.barchart(330, 10, 300, 220, data1);
20728                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
20729                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
20730                 */
20731                 
20732                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
20733                 // r.barchart(30, 30, 560, 250,  xdata, {
20734                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
20735                 //     axis : "0 0 1 1",
20736                 //     axisxlabels :  xdata
20737                 //     //yvalues : cols,
20738                    
20739                 // });
20740 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
20741 //        
20742 //        this.load(null,xdata,{
20743 //                axis : "0 0 1 1",
20744 //                axisxlabels :  xdata
20745 //                });
20746
20747     },
20748
20749     load : function(graphtype,xdata,opts){
20750         this.raphael.clear();
20751         if(!graphtype) {
20752             graphtype = this.graphtype;
20753         }
20754         if(!opts){
20755             opts = this.opts;
20756         }
20757         var r = this.raphael,
20758             fin = function () {
20759                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
20760             },
20761             fout = function () {
20762                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
20763             },
20764             pfin = function() {
20765                 this.sector.stop();
20766                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
20767
20768                 if (this.label) {
20769                     this.label[0].stop();
20770                     this.label[0].attr({ r: 7.5 });
20771                     this.label[1].attr({ "font-weight": 800 });
20772                 }
20773             },
20774             pfout = function() {
20775                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
20776
20777                 if (this.label) {
20778                     this.label[0].animate({ r: 5 }, 500, "bounce");
20779                     this.label[1].attr({ "font-weight": 400 });
20780                 }
20781             };
20782
20783         switch(graphtype){
20784             case 'bar':
20785                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
20786                 break;
20787             case 'hbar':
20788                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
20789                 break;
20790             case 'pie':
20791 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
20792 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
20793 //            
20794                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
20795                 
20796                 break;
20797
20798         }
20799         
20800         if(this.title){
20801             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
20802         }
20803         
20804     },
20805     
20806     setTitle: function(o)
20807     {
20808         this.title = o;
20809     },
20810     
20811     initEvents: function() {
20812         
20813         if(!this.href){
20814             this.el.on('click', this.onClick, this);
20815         }
20816     },
20817     
20818     onClick : function(e)
20819     {
20820         Roo.log('img onclick');
20821         this.fireEvent('click', this, e);
20822     }
20823    
20824 });
20825
20826  
20827 /*
20828  * - LGPL
20829  *
20830  * numberBox
20831  * 
20832  */
20833 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20834
20835 /**
20836  * @class Roo.bootstrap.dash.NumberBox
20837  * @extends Roo.bootstrap.Component
20838  * Bootstrap NumberBox class
20839  * @cfg {String} headline Box headline
20840  * @cfg {String} content Box content
20841  * @cfg {String} icon Box icon
20842  * @cfg {String} footer Footer text
20843  * @cfg {String} fhref Footer href
20844  * 
20845  * @constructor
20846  * Create a new NumberBox
20847  * @param {Object} config The config object
20848  */
20849
20850
20851 Roo.bootstrap.dash.NumberBox = function(config){
20852     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
20853     
20854 };
20855
20856 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
20857     
20858     headline : '',
20859     content : '',
20860     icon : '',
20861     footer : '',
20862     fhref : '',
20863     ficon : '',
20864     
20865     getAutoCreate : function(){
20866         
20867         var cfg = {
20868             tag : 'div',
20869             cls : 'small-box ',
20870             cn : [
20871                 {
20872                     tag : 'div',
20873                     cls : 'inner',
20874                     cn :[
20875                         {
20876                             tag : 'h3',
20877                             cls : 'roo-headline',
20878                             html : this.headline
20879                         },
20880                         {
20881                             tag : 'p',
20882                             cls : 'roo-content',
20883                             html : this.content
20884                         }
20885                     ]
20886                 }
20887             ]
20888         }
20889         
20890         if(this.icon){
20891             cfg.cn.push({
20892                 tag : 'div',
20893                 cls : 'icon',
20894                 cn :[
20895                     {
20896                         tag : 'i',
20897                         cls : 'ion ' + this.icon
20898                     }
20899                 ]
20900             });
20901         }
20902         
20903         if(this.footer){
20904             var footer = {
20905                 tag : 'a',
20906                 cls : 'small-box-footer',
20907                 href : this.fhref || '#',
20908                 html : this.footer
20909             };
20910             
20911             cfg.cn.push(footer);
20912             
20913         }
20914         
20915         return  cfg;
20916     },
20917
20918     onRender : function(ct,position){
20919         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
20920
20921
20922        
20923                 
20924     },
20925
20926     setHeadline: function (value)
20927     {
20928         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
20929     },
20930     
20931     setFooter: function (value, href)
20932     {
20933         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
20934         
20935         if(href){
20936             this.el.select('a.small-box-footer',true).first().attr('href', href);
20937         }
20938         
20939     },
20940
20941     setContent: function (value)
20942     {
20943         this.el.select('.roo-content',true).first().dom.innerHTML = value;
20944     },
20945
20946     initEvents: function() 
20947     {   
20948         
20949     }
20950     
20951 });
20952
20953  
20954 /*
20955  * - LGPL
20956  *
20957  * TabBox
20958  * 
20959  */
20960 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20961
20962 /**
20963  * @class Roo.bootstrap.dash.TabBox
20964  * @extends Roo.bootstrap.Component
20965  * Bootstrap TabBox class
20966  * @cfg {String} title Title of the TabBox
20967  * @cfg {String} icon Icon of the TabBox
20968  * @cfg {Boolean} showtabs (true|false) show the tabs default true
20969  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
20970  * 
20971  * @constructor
20972  * Create a new TabBox
20973  * @param {Object} config The config object
20974  */
20975
20976
20977 Roo.bootstrap.dash.TabBox = function(config){
20978     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
20979     this.addEvents({
20980         // raw events
20981         /**
20982          * @event addpane
20983          * When a pane is added
20984          * @param {Roo.bootstrap.dash.TabPane} pane
20985          */
20986         "addpane" : true,
20987         /**
20988          * @event activatepane
20989          * When a pane is activated
20990          * @param {Roo.bootstrap.dash.TabPane} pane
20991          */
20992         "activatepane" : true
20993         
20994          
20995     });
20996     
20997     this.panes = [];
20998 };
20999
21000 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21001
21002     title : '',
21003     icon : false,
21004     showtabs : true,
21005     tabScrollable : false,
21006     
21007     getChildContainer : function()
21008     {
21009         return this.el.select('.tab-content', true).first();
21010     },
21011     
21012     getAutoCreate : function(){
21013         
21014         var header = {
21015             tag: 'li',
21016             cls: 'pull-left header',
21017             html: this.title,
21018             cn : []
21019         };
21020         
21021         if(this.icon){
21022             header.cn.push({
21023                 tag: 'i',
21024                 cls: 'fa ' + this.icon
21025             });
21026         }
21027         
21028         var h = {
21029             tag: 'ul',
21030             cls: 'nav nav-tabs pull-right',
21031             cn: [
21032                 header
21033             ]
21034         };
21035         
21036         if(this.tabScrollable){
21037             h = {
21038                 tag: 'div',
21039                 cls: 'tab-header',
21040                 cn: [
21041                     {
21042                         tag: 'ul',
21043                         cls: 'nav nav-tabs pull-right',
21044                         cn: [
21045                             header
21046                         ]
21047                     }
21048                 ]
21049             }
21050         }
21051         
21052         var cfg = {
21053             tag: 'div',
21054             cls: 'nav-tabs-custom',
21055             cn: [
21056                 h,
21057                 {
21058                     tag: 'div',
21059                     cls: 'tab-content no-padding',
21060                     cn: []
21061                 }
21062             ]
21063         }
21064
21065         return  cfg;
21066     },
21067     initEvents : function()
21068     {
21069         //Roo.log('add add pane handler');
21070         this.on('addpane', this.onAddPane, this);
21071     },
21072      /**
21073      * Updates the box title
21074      * @param {String} html to set the title to.
21075      */
21076     setTitle : function(value)
21077     {
21078         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21079     },
21080     onAddPane : function(pane)
21081     {
21082         this.panes.push(pane);
21083         //Roo.log('addpane');
21084         //Roo.log(pane);
21085         // tabs are rendere left to right..
21086         if(!this.showtabs){
21087             return;
21088         }
21089         
21090         var ctr = this.el.select('.nav-tabs', true).first();
21091          
21092          
21093         var existing = ctr.select('.nav-tab',true);
21094         var qty = existing.getCount();;
21095         
21096         
21097         var tab = ctr.createChild({
21098             tag : 'li',
21099             cls : 'nav-tab' + (qty ? '' : ' active'),
21100             cn : [
21101                 {
21102                     tag : 'a',
21103                     href:'#',
21104                     html : pane.title
21105                 }
21106             ]
21107         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21108         pane.tab = tab;
21109         
21110         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21111         if (!qty) {
21112             pane.el.addClass('active');
21113         }
21114         
21115                 
21116     },
21117     onTabClick : function(ev,un,ob,pane)
21118     {
21119         //Roo.log('tab - prev default');
21120         ev.preventDefault();
21121         
21122         
21123         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21124         pane.tab.addClass('active');
21125         //Roo.log(pane.title);
21126         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21127         // technically we should have a deactivate event.. but maybe add later.
21128         // and it should not de-activate the selected tab...
21129         this.fireEvent('activatepane', pane);
21130         pane.el.addClass('active');
21131         pane.fireEvent('activate');
21132         
21133         
21134     },
21135     
21136     getActivePane : function()
21137     {
21138         var r = false;
21139         Roo.each(this.panes, function(p) {
21140             if(p.el.hasClass('active')){
21141                 r = p;
21142                 return false;
21143             }
21144             
21145             return;
21146         });
21147         
21148         return r;
21149     }
21150     
21151     
21152 });
21153
21154  
21155 /*
21156  * - LGPL
21157  *
21158  * Tab pane
21159  * 
21160  */
21161 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21162 /**
21163  * @class Roo.bootstrap.TabPane
21164  * @extends Roo.bootstrap.Component
21165  * Bootstrap TabPane class
21166  * @cfg {Boolean} active (false | true) Default false
21167  * @cfg {String} title title of panel
21168
21169  * 
21170  * @constructor
21171  * Create a new TabPane
21172  * @param {Object} config The config object
21173  */
21174
21175 Roo.bootstrap.dash.TabPane = function(config){
21176     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21177     
21178     this.addEvents({
21179         // raw events
21180         /**
21181          * @event activate
21182          * When a pane is activated
21183          * @param {Roo.bootstrap.dash.TabPane} pane
21184          */
21185         "activate" : true
21186          
21187     });
21188 };
21189
21190 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21191     
21192     active : false,
21193     title : '',
21194     
21195     // the tabBox that this is attached to.
21196     tab : false,
21197      
21198     getAutoCreate : function() 
21199     {
21200         var cfg = {
21201             tag: 'div',
21202             cls: 'tab-pane'
21203         }
21204         
21205         if(this.active){
21206             cfg.cls += ' active';
21207         }
21208         
21209         return cfg;
21210     },
21211     initEvents  : function()
21212     {
21213         //Roo.log('trigger add pane handler');
21214         this.parent().fireEvent('addpane', this)
21215     },
21216     
21217      /**
21218      * Updates the tab title 
21219      * @param {String} html to set the title to.
21220      */
21221     setTitle: function(str)
21222     {
21223         if (!this.tab) {
21224             return;
21225         }
21226         this.title = str;
21227         this.tab.select('a', true).first().dom.innerHTML = str;
21228         
21229     }
21230     
21231     
21232     
21233 });
21234
21235  
21236
21237
21238  /*
21239  * - LGPL
21240  *
21241  * menu
21242  * 
21243  */
21244 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21245
21246 /**
21247  * @class Roo.bootstrap.menu.Menu
21248  * @extends Roo.bootstrap.Component
21249  * Bootstrap Menu class - container for Menu
21250  * @cfg {String} html Text of the menu
21251  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21252  * @cfg {String} icon Font awesome icon
21253  * @cfg {String} pos Menu align to (top | bottom) default bottom
21254  * 
21255  * 
21256  * @constructor
21257  * Create a new Menu
21258  * @param {Object} config The config object
21259  */
21260
21261
21262 Roo.bootstrap.menu.Menu = function(config){
21263     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21264     
21265     this.addEvents({
21266         /**
21267          * @event beforeshow
21268          * Fires before this menu is displayed
21269          * @param {Roo.bootstrap.menu.Menu} this
21270          */
21271         beforeshow : true,
21272         /**
21273          * @event beforehide
21274          * Fires before this menu is hidden
21275          * @param {Roo.bootstrap.menu.Menu} this
21276          */
21277         beforehide : true,
21278         /**
21279          * @event show
21280          * Fires after this menu is displayed
21281          * @param {Roo.bootstrap.menu.Menu} this
21282          */
21283         show : true,
21284         /**
21285          * @event hide
21286          * Fires after this menu is hidden
21287          * @param {Roo.bootstrap.menu.Menu} this
21288          */
21289         hide : true,
21290         /**
21291          * @event click
21292          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21293          * @param {Roo.bootstrap.menu.Menu} this
21294          * @param {Roo.EventObject} e
21295          */
21296         click : true
21297     });
21298     
21299 };
21300
21301 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21302     
21303     submenu : false,
21304     html : '',
21305     weight : 'default',
21306     icon : false,
21307     pos : 'bottom',
21308     
21309     
21310     getChildContainer : function() {
21311         if(this.isSubMenu){
21312             return this.el;
21313         }
21314         
21315         return this.el.select('ul.dropdown-menu', true).first();  
21316     },
21317     
21318     getAutoCreate : function()
21319     {
21320         var text = [
21321             {
21322                 tag : 'span',
21323                 cls : 'roo-menu-text',
21324                 html : this.html
21325             }
21326         ];
21327         
21328         if(this.icon){
21329             text.unshift({
21330                 tag : 'i',
21331                 cls : 'fa ' + this.icon
21332             })
21333         }
21334         
21335         
21336         var cfg = {
21337             tag : 'div',
21338             cls : 'btn-group',
21339             cn : [
21340                 {
21341                     tag : 'button',
21342                     cls : 'dropdown-button btn btn-' + this.weight,
21343                     cn : text
21344                 },
21345                 {
21346                     tag : 'button',
21347                     cls : 'dropdown-toggle btn btn-' + this.weight,
21348                     cn : [
21349                         {
21350                             tag : 'span',
21351                             cls : 'caret'
21352                         }
21353                     ]
21354                 },
21355                 {
21356                     tag : 'ul',
21357                     cls : 'dropdown-menu'
21358                 }
21359             ]
21360             
21361         };
21362         
21363         if(this.pos == 'top'){
21364             cfg.cls += ' dropup';
21365         }
21366         
21367         if(this.isSubMenu){
21368             cfg = {
21369                 tag : 'ul',
21370                 cls : 'dropdown-menu'
21371             }
21372         }
21373         
21374         return cfg;
21375     },
21376     
21377     onRender : function(ct, position)
21378     {
21379         this.isSubMenu = ct.hasClass('dropdown-submenu');
21380         
21381         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21382     },
21383     
21384     initEvents : function() 
21385     {
21386         if(this.isSubMenu){
21387             return;
21388         }
21389         
21390         this.hidden = true;
21391         
21392         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21393         this.triggerEl.on('click', this.onTriggerPress, this);
21394         
21395         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21396         this.buttonEl.on('click', this.onClick, this);
21397         
21398     },
21399     
21400     list : function()
21401     {
21402         if(this.isSubMenu){
21403             return this.el;
21404         }
21405         
21406         return this.el.select('ul.dropdown-menu', true).first();
21407     },
21408     
21409     onClick : function(e)
21410     {
21411         this.fireEvent("click", this, e);
21412     },
21413     
21414     onTriggerPress  : function(e)
21415     {   
21416         if (this.isVisible()) {
21417             this.hide();
21418         } else {
21419             this.show();
21420         }
21421     },
21422     
21423     isVisible : function(){
21424         return !this.hidden;
21425     },
21426     
21427     show : function()
21428     {
21429         this.fireEvent("beforeshow", this);
21430         
21431         this.hidden = false;
21432         this.el.addClass('open');
21433         
21434         Roo.get(document).on("mouseup", this.onMouseUp, this);
21435         
21436         this.fireEvent("show", this);
21437         
21438         
21439     },
21440     
21441     hide : function()
21442     {
21443         this.fireEvent("beforehide", this);
21444         
21445         this.hidden = true;
21446         this.el.removeClass('open');
21447         
21448         Roo.get(document).un("mouseup", this.onMouseUp);
21449         
21450         this.fireEvent("hide", this);
21451     },
21452     
21453     onMouseUp : function()
21454     {
21455         this.hide();
21456     }
21457     
21458 });
21459
21460  
21461  /*
21462  * - LGPL
21463  *
21464  * menu item
21465  * 
21466  */
21467 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21468
21469 /**
21470  * @class Roo.bootstrap.menu.Item
21471  * @extends Roo.bootstrap.Component
21472  * Bootstrap MenuItem class
21473  * @cfg {Boolean} submenu (true | false) default false
21474  * @cfg {String} html text of the item
21475  * @cfg {String} href the link
21476  * @cfg {Boolean} disable (true | false) default false
21477  * @cfg {Boolean} preventDefault (true | false) default true
21478  * @cfg {String} icon Font awesome icon
21479  * @cfg {String} pos Submenu align to (left | right) default right 
21480  * 
21481  * 
21482  * @constructor
21483  * Create a new Item
21484  * @param {Object} config The config object
21485  */
21486
21487
21488 Roo.bootstrap.menu.Item = function(config){
21489     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
21490     this.addEvents({
21491         /**
21492          * @event mouseover
21493          * Fires when the mouse is hovering over this menu
21494          * @param {Roo.bootstrap.menu.Item} this
21495          * @param {Roo.EventObject} e
21496          */
21497         mouseover : true,
21498         /**
21499          * @event mouseout
21500          * Fires when the mouse exits this menu
21501          * @param {Roo.bootstrap.menu.Item} this
21502          * @param {Roo.EventObject} e
21503          */
21504         mouseout : true,
21505         // raw events
21506         /**
21507          * @event click
21508          * The raw click event for the entire grid.
21509          * @param {Roo.EventObject} e
21510          */
21511         click : true
21512     });
21513 };
21514
21515 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
21516     
21517     submenu : false,
21518     href : '',
21519     html : '',
21520     preventDefault: true,
21521     disable : false,
21522     icon : false,
21523     pos : 'right',
21524     
21525     getAutoCreate : function()
21526     {
21527         var text = [
21528             {
21529                 tag : 'span',
21530                 cls : 'roo-menu-item-text',
21531                 html : this.html
21532             }
21533         ];
21534         
21535         if(this.icon){
21536             text.unshift({
21537                 tag : 'i',
21538                 cls : 'fa ' + this.icon
21539             })
21540         }
21541         
21542         var cfg = {
21543             tag : 'li',
21544             cn : [
21545                 {
21546                     tag : 'a',
21547                     href : this.href || '#',
21548                     cn : text
21549                 }
21550             ]
21551         };
21552         
21553         if(this.disable){
21554             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
21555         }
21556         
21557         if(this.submenu){
21558             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
21559             
21560             if(this.pos == 'left'){
21561                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
21562             }
21563         }
21564         
21565         return cfg;
21566     },
21567     
21568     initEvents : function() 
21569     {
21570         this.el.on('mouseover', this.onMouseOver, this);
21571         this.el.on('mouseout', this.onMouseOut, this);
21572         
21573         this.el.select('a', true).first().on('click', this.onClick, this);
21574         
21575     },
21576     
21577     onClick : function(e)
21578     {
21579         if(this.preventDefault){
21580             e.preventDefault();
21581         }
21582         
21583         this.fireEvent("click", this, e);
21584     },
21585     
21586     onMouseOver : function(e)
21587     {
21588         if(this.submenu && this.pos == 'left'){
21589             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
21590         }
21591         
21592         this.fireEvent("mouseover", this, e);
21593     },
21594     
21595     onMouseOut : function(e)
21596     {
21597         this.fireEvent("mouseout", this, e);
21598     }
21599 });
21600
21601  
21602
21603  /*
21604  * - LGPL
21605  *
21606  * menu separator
21607  * 
21608  */
21609 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21610
21611 /**
21612  * @class Roo.bootstrap.menu.Separator
21613  * @extends Roo.bootstrap.Component
21614  * Bootstrap Separator class
21615  * 
21616  * @constructor
21617  * Create a new Separator
21618  * @param {Object} config The config object
21619  */
21620
21621
21622 Roo.bootstrap.menu.Separator = function(config){
21623     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
21624 };
21625
21626 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
21627     
21628     getAutoCreate : function(){
21629         var cfg = {
21630             tag : 'li',
21631             cls: 'divider'
21632         };
21633         
21634         return cfg;
21635     }
21636    
21637 });
21638
21639  
21640
21641  /*
21642  * - LGPL
21643  *
21644  * Tooltip
21645  * 
21646  */
21647
21648 /**
21649  * @class Roo.bootstrap.Tooltip
21650  * Bootstrap Tooltip class
21651  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
21652  * to determine which dom element triggers the tooltip.
21653  * 
21654  * It needs to add support for additional attributes like tooltip-position
21655  * 
21656  * @constructor
21657  * Create a new Toolti
21658  * @param {Object} config The config object
21659  */
21660
21661 Roo.bootstrap.Tooltip = function(config){
21662     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
21663 };
21664
21665 Roo.apply(Roo.bootstrap.Tooltip, {
21666     /**
21667      * @function init initialize tooltip monitoring.
21668      * @static
21669      */
21670     currentEl : false,
21671     currentTip : false,
21672     currentRegion : false,
21673     
21674     //  init : delay?
21675     
21676     init : function()
21677     {
21678         Roo.get(document).on('mouseover', this.enter ,this);
21679         Roo.get(document).on('mouseout', this.leave, this);
21680          
21681         
21682         this.currentTip = new Roo.bootstrap.Tooltip();
21683     },
21684     
21685     enter : function(ev)
21686     {
21687         var dom = ev.getTarget();
21688         //Roo.log(['enter',dom]);
21689         var el = Roo.fly(dom);
21690         if (this.currentEl) {
21691             //Roo.log(dom);
21692             //Roo.log(this.currentEl);
21693             //Roo.log(this.currentEl.contains(dom));
21694             if (this.currentEl == el) {
21695                 return;
21696             }
21697             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
21698                 return;
21699             }
21700
21701         }
21702         
21703         
21704         
21705         if (this.currentTip.el) {
21706             this.currentTip.el.hide(); // force hiding...
21707         }    
21708         //Roo.log(el);
21709         if (!el.attr('tooltip')) { // parents who have tip?
21710             return;
21711         }
21712         this.currentEl = el;
21713         this.currentTip.bind(el);
21714         this.currentRegion = Roo.lib.Region.getRegion(dom);
21715         this.currentTip.enter();
21716         
21717     },
21718     leave : function(ev)
21719     {
21720         var dom = ev.getTarget();
21721         //Roo.log(['leave',dom]);
21722         if (!this.currentEl) {
21723             return;
21724         }
21725         
21726         
21727         if (dom != this.currentEl.dom) {
21728             return;
21729         }
21730         var xy = ev.getXY();
21731         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
21732             return;
21733         }
21734         // only activate leave if mouse cursor is outside... bounding box..
21735         
21736         
21737         
21738         
21739         if (this.currentTip) {
21740             this.currentTip.leave();
21741         }
21742         //Roo.log('clear currentEl');
21743         this.currentEl = false;
21744         
21745         
21746     },
21747     alignment : {
21748         'left' : ['r-l', [-2,0], 'right'],
21749         'right' : ['l-r', [2,0], 'left'],
21750         'bottom' : ['t-b', [0,2], 'top'],
21751         'top' : [ 'b-t', [0,-2], 'bottom']
21752     }
21753     
21754 });
21755
21756
21757 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
21758     
21759     
21760     bindEl : false,
21761     
21762     delay : null, // can be { show : 300 , hide: 500}
21763     
21764     timeout : null,
21765     
21766     hoverState : null, //???
21767     
21768     placement : 'bottom', 
21769     
21770     getAutoCreate : function(){
21771     
21772         var cfg = {
21773            cls : 'tooltip',
21774            role : 'tooltip',
21775            cn : [
21776                 {
21777                     cls : 'tooltip-arrow'
21778                 },
21779                 {
21780                     cls : 'tooltip-inner'
21781                 }
21782            ]
21783         };
21784         
21785         return cfg;
21786     },
21787     bind : function(el)
21788     {
21789         this.bindEl = el;
21790     },
21791       
21792     
21793     enter : function () {
21794        
21795         if (this.timeout != null) {
21796             clearTimeout(this.timeout);
21797         }
21798         
21799         this.hoverState = 'in';
21800          //Roo.log("enter - show");
21801         if (!this.delay || !this.delay.show) {
21802             this.show();
21803             return;
21804         }
21805         var _t = this;
21806         this.timeout = setTimeout(function () {
21807             if (_t.hoverState == 'in') {
21808                 _t.show();
21809             }
21810         }, this.delay.show);
21811     },
21812     leave : function()
21813     {
21814         clearTimeout(this.timeout);
21815     
21816         this.hoverState = 'out';
21817          if (!this.delay || !this.delay.hide) {
21818             this.hide();
21819             return 
21820         }
21821        
21822         var _t = this;
21823         this.timeout = setTimeout(function () {
21824             //Roo.log("leave - timeout");
21825             
21826             if (_t.hoverState == 'out') {
21827                 _t.hide();
21828                 Roo.bootstrap.Tooltip.currentEl = false;
21829             }
21830         }, delay)
21831     },
21832     
21833     show : function ()
21834     {
21835         if (!this.el) {
21836             this.render(document.body);
21837         }
21838         // set content.
21839         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
21840         this.el.select('.tooltip-inner',true).first().dom.innerHTML = this.bindEl.attr('tooltip');
21841         
21842         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
21843         
21844         var placement = typeof this.placement == 'function' ?
21845             this.placement.call(this, this.el, on_el) :
21846             this.placement;
21847             
21848         var autoToken = /\s?auto?\s?/i;
21849         var autoPlace = autoToken.test(placement);
21850         if (autoPlace) {
21851             placement = placement.replace(autoToken, '') || 'top';
21852         }
21853         
21854         //this.el.detach()
21855         //this.el.setXY([0,0]);
21856         this.el.show();
21857         //this.el.dom.style.display='block';
21858         this.el.addClass(placement);
21859         
21860         //this.el.appendTo(on_el);
21861         
21862         var p = this.getPosition();
21863         var box = this.el.getBox();
21864         
21865         if (autoPlace) {
21866             // fixme..
21867         }
21868         var align = Roo.bootstrap.Tooltip.alignment[placement];
21869         this.el.alignTo(this.bindEl, align[0],align[1]);
21870         //var arrow = this.el.select('.arrow',true).first();
21871         //arrow.set(align[2], 
21872         
21873         this.el.addClass('in fade');
21874         this.hoverState = null;
21875         
21876         if (this.el.hasClass('fade')) {
21877             // fade it?
21878         }
21879         
21880     },
21881     hide : function()
21882     {
21883          
21884         if (!this.el) {
21885             return;
21886         }
21887         //this.el.setXY([0,0]);
21888         this.el.removeClass('in');
21889         //this.el.hide();
21890         
21891     }
21892     
21893 });
21894  
21895
21896  /*
21897  * - LGPL
21898  *
21899  * Location Picker
21900  * 
21901  */
21902
21903 /**
21904  * @class Roo.bootstrap.LocationPicker
21905  * @extends Roo.bootstrap.Component
21906  * Bootstrap LocationPicker class
21907  * @cfg {Number} latitude Position when init default 0
21908  * @cfg {Number} longitude Position when init default 0
21909  * @cfg {Number} zoom default 15
21910  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
21911  * @cfg {Boolean} mapTypeControl default false
21912  * @cfg {Boolean} disableDoubleClickZoom default false
21913  * @cfg {Boolean} scrollwheel default true
21914  * @cfg {Boolean} streetViewControl default false
21915  * @cfg {Number} radius default 0
21916  * @cfg {String} locationName
21917  * @cfg {Boolean} draggable default true
21918  * @cfg {Boolean} enableAutocomplete default false
21919  * @cfg {Boolean} enableReverseGeocode default true
21920  * @cfg {String} markerTitle
21921  * 
21922  * @constructor
21923  * Create a new LocationPicker
21924  * @param {Object} config The config object
21925  */
21926
21927
21928 Roo.bootstrap.LocationPicker = function(config){
21929     
21930     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
21931     
21932     this.addEvents({
21933         /**
21934          * @event initial
21935          * Fires when the picker initialized.
21936          * @param {Roo.bootstrap.LocationPicker} this
21937          * @param {Google Location} location
21938          */
21939         initial : true,
21940         /**
21941          * @event positionchanged
21942          * Fires when the picker position changed.
21943          * @param {Roo.bootstrap.LocationPicker} this
21944          * @param {Google Location} location
21945          */
21946         positionchanged : true,
21947         /**
21948          * @event resize
21949          * Fires when the map resize.
21950          * @param {Roo.bootstrap.LocationPicker} this
21951          */
21952         resize : true,
21953         /**
21954          * @event show
21955          * Fires when the map show.
21956          * @param {Roo.bootstrap.LocationPicker} this
21957          */
21958         show : true,
21959         /**
21960          * @event hide
21961          * Fires when the map hide.
21962          * @param {Roo.bootstrap.LocationPicker} this
21963          */
21964         hide : true,
21965         /**
21966          * @event mapClick
21967          * Fires when click the map.
21968          * @param {Roo.bootstrap.LocationPicker} this
21969          * @param {Map event} e
21970          */
21971         mapClick : true,
21972         /**
21973          * @event mapRightClick
21974          * Fires when right click the map.
21975          * @param {Roo.bootstrap.LocationPicker} this
21976          * @param {Map event} e
21977          */
21978         mapRightClick : true,
21979         /**
21980          * @event markerClick
21981          * Fires when click the marker.
21982          * @param {Roo.bootstrap.LocationPicker} this
21983          * @param {Map event} e
21984          */
21985         markerClick : true,
21986         /**
21987          * @event markerRightClick
21988          * Fires when right click the marker.
21989          * @param {Roo.bootstrap.LocationPicker} this
21990          * @param {Map event} e
21991          */
21992         markerRightClick : true,
21993         /**
21994          * @event OverlayViewDraw
21995          * Fires when OverlayView Draw
21996          * @param {Roo.bootstrap.LocationPicker} this
21997          */
21998         OverlayViewDraw : true,
21999         /**
22000          * @event OverlayViewOnAdd
22001          * Fires when OverlayView Draw
22002          * @param {Roo.bootstrap.LocationPicker} this
22003          */
22004         OverlayViewOnAdd : true,
22005         /**
22006          * @event OverlayViewOnRemove
22007          * Fires when OverlayView Draw
22008          * @param {Roo.bootstrap.LocationPicker} this
22009          */
22010         OverlayViewOnRemove : true,
22011         /**
22012          * @event OverlayViewShow
22013          * Fires when OverlayView Draw
22014          * @param {Roo.bootstrap.LocationPicker} this
22015          * @param {Pixel} cpx
22016          */
22017         OverlayViewShow : true,
22018         /**
22019          * @event OverlayViewHide
22020          * Fires when OverlayView Draw
22021          * @param {Roo.bootstrap.LocationPicker} this
22022          */
22023         OverlayViewHide : true
22024     });
22025         
22026 };
22027
22028 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22029     
22030     gMapContext: false,
22031     
22032     latitude: 0,
22033     longitude: 0,
22034     zoom: 15,
22035     mapTypeId: false,
22036     mapTypeControl: false,
22037     disableDoubleClickZoom: false,
22038     scrollwheel: true,
22039     streetViewControl: false,
22040     radius: 0,
22041     locationName: '',
22042     draggable: true,
22043     enableAutocomplete: false,
22044     enableReverseGeocode: true,
22045     markerTitle: '',
22046     
22047     getAutoCreate: function()
22048     {
22049
22050         var cfg = {
22051             tag: 'div',
22052             cls: 'roo-location-picker'
22053         };
22054         
22055         return cfg
22056     },
22057     
22058     initEvents: function(ct, position)
22059     {       
22060         if(!this.el.getWidth() || this.isApplied()){
22061             return;
22062         }
22063         
22064         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22065         
22066         this.initial();
22067     },
22068     
22069     initial: function()
22070     {
22071         if(!this.mapTypeId){
22072             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22073         }
22074         
22075         this.gMapContext = this.GMapContext();
22076         
22077         this.initOverlayView();
22078         
22079         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22080         
22081         var _this = this;
22082                 
22083         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22084             _this.setPosition(_this.gMapContext.marker.position);
22085         });
22086         
22087         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22088             _this.fireEvent('mapClick', this, event);
22089             
22090         });
22091
22092         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22093             _this.fireEvent('mapRightClick', this, event);
22094             
22095         });
22096         
22097         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22098             _this.fireEvent('markerClick', this, event);
22099             
22100         });
22101
22102         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22103             _this.fireEvent('markerRightClick', this, event);
22104             
22105         });
22106         
22107         this.setPosition(this.gMapContext.location);
22108         
22109         this.fireEvent('initial', this, this.gMapContext.location);
22110     },
22111     
22112     initOverlayView: function()
22113     {
22114         var _this = this;
22115         
22116         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22117             
22118             draw: function()
22119             {
22120                 _this.fireEvent('OverlayViewDraw', _this);
22121             },
22122             
22123             onAdd: function()
22124             {
22125                 _this.fireEvent('OverlayViewOnAdd', _this);
22126             },
22127             
22128             onRemove: function()
22129             {
22130                 _this.fireEvent('OverlayViewOnRemove', _this);
22131             },
22132             
22133             show: function(cpx)
22134             {
22135                 _this.fireEvent('OverlayViewShow', _this, cpx);
22136             },
22137             
22138             hide: function()
22139             {
22140                 _this.fireEvent('OverlayViewHide', _this);
22141             }
22142             
22143         });
22144     },
22145     
22146     fromLatLngToContainerPixel: function(event)
22147     {
22148         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22149     },
22150     
22151     isApplied: function() 
22152     {
22153         return this.getGmapContext() == false ? false : true;
22154     },
22155     
22156     getGmapContext: function() 
22157     {
22158         return this.gMapContext
22159     },
22160     
22161     GMapContext: function() 
22162     {
22163         var _map = new google.maps.Map(this.el.dom, this);
22164         var _marker = new google.maps.Marker({
22165             position: new google.maps.LatLng(this.latitude, this.longitude),
22166             map: _map,
22167             title: this.markerTitle,
22168             draggable: this.draggable
22169         });
22170         
22171         return {
22172             map: _map,
22173             marker: _marker,
22174             circle: null,
22175             location: _marker.position,
22176             radius: this.radius,
22177             locationName: this.locationName,
22178             addressComponents: {
22179                 formatted_address: null,
22180                 addressLine1: null,
22181                 addressLine2: null,
22182                 streetName: null,
22183                 streetNumber: null,
22184                 city: null,
22185                 district: null,
22186                 state: null,
22187                 stateOrProvince: null
22188             },
22189             settings: this,
22190             domContainer: this.el.dom,
22191             geodecoder: new google.maps.Geocoder()
22192         };
22193     },
22194     
22195     drawCircle: function(center, radius, options) 
22196     {
22197         if (this.gMapContext.circle != null) {
22198             this.gMapContext.circle.setMap(null);
22199         }
22200         if (radius > 0) {
22201             radius *= 1;
22202             options = Roo.apply({}, options, {
22203                 strokeColor: "#0000FF",
22204                 strokeOpacity: .35,
22205                 strokeWeight: 2,
22206                 fillColor: "#0000FF",
22207                 fillOpacity: .2
22208             });
22209             
22210             options.map = this.gMapContext.map;
22211             options.radius = radius;
22212             options.center = center;
22213             this.gMapContext.circle = new google.maps.Circle(options);
22214             return this.gMapContext.circle;
22215         }
22216         
22217         return null;
22218     },
22219     
22220     setPosition: function(location) 
22221     {
22222         this.gMapContext.location = location;
22223         this.gMapContext.marker.setPosition(location);
22224         this.gMapContext.map.panTo(location);
22225         this.drawCircle(location, this.gMapContext.radius, {});
22226         
22227         var _this = this;
22228         
22229         if (this.gMapContext.settings.enableReverseGeocode) {
22230             this.gMapContext.geodecoder.geocode({
22231                 latLng: this.gMapContext.location
22232             }, function(results, status) {
22233                 
22234                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22235                     _this.gMapContext.locationName = results[0].formatted_address;
22236                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22237                     
22238                     _this.fireEvent('positionchanged', this, location);
22239                 }
22240             });
22241             
22242             return;
22243         }
22244         
22245         this.fireEvent('positionchanged', this, location);
22246     },
22247     
22248     resize: function()
22249     {
22250         google.maps.event.trigger(this.gMapContext.map, "resize");
22251         
22252         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22253         
22254         this.fireEvent('resize', this);
22255     },
22256     
22257     setPositionByLatLng: function(latitude, longitude)
22258     {
22259         this.setPosition(new google.maps.LatLng(latitude, longitude));
22260     },
22261     
22262     getCurrentPosition: function() 
22263     {
22264         return {
22265             latitude: this.gMapContext.location.lat(),
22266             longitude: this.gMapContext.location.lng()
22267         };
22268     },
22269     
22270     getAddressName: function() 
22271     {
22272         return this.gMapContext.locationName;
22273     },
22274     
22275     getAddressComponents: function() 
22276     {
22277         return this.gMapContext.addressComponents;
22278     },
22279     
22280     address_component_from_google_geocode: function(address_components) 
22281     {
22282         var result = {};
22283         
22284         for (var i = 0; i < address_components.length; i++) {
22285             var component = address_components[i];
22286             if (component.types.indexOf("postal_code") >= 0) {
22287                 result.postalCode = component.short_name;
22288             } else if (component.types.indexOf("street_number") >= 0) {
22289                 result.streetNumber = component.short_name;
22290             } else if (component.types.indexOf("route") >= 0) {
22291                 result.streetName = component.short_name;
22292             } else if (component.types.indexOf("neighborhood") >= 0) {
22293                 result.city = component.short_name;
22294             } else if (component.types.indexOf("locality") >= 0) {
22295                 result.city = component.short_name;
22296             } else if (component.types.indexOf("sublocality") >= 0) {
22297                 result.district = component.short_name;
22298             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22299                 result.stateOrProvince = component.short_name;
22300             } else if (component.types.indexOf("country") >= 0) {
22301                 result.country = component.short_name;
22302             }
22303         }
22304         
22305         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22306         result.addressLine2 = "";
22307         return result;
22308     },
22309     
22310     setZoomLevel: function(zoom)
22311     {
22312         this.gMapContext.map.setZoom(zoom);
22313     },
22314     
22315     show: function()
22316     {
22317         if(!this.el){
22318             return;
22319         }
22320         
22321         this.el.show();
22322         
22323         this.resize();
22324         
22325         this.fireEvent('show', this);
22326     },
22327     
22328     hide: function()
22329     {
22330         if(!this.el){
22331             return;
22332         }
22333         
22334         this.el.hide();
22335         
22336         this.fireEvent('hide', this);
22337     }
22338     
22339 });
22340
22341 Roo.apply(Roo.bootstrap.LocationPicker, {
22342     
22343     OverlayView : function(map, options)
22344     {
22345         options = options || {};
22346         
22347         this.setMap(map);
22348     }
22349     
22350     
22351 });/*
22352  * - LGPL
22353  *
22354  * Alert
22355  * 
22356  */
22357
22358 /**
22359  * @class Roo.bootstrap.Alert
22360  * @extends Roo.bootstrap.Component
22361  * Bootstrap Alert class
22362  * @cfg {String} title The title of alert
22363  * @cfg {String} html The content of alert
22364  * @cfg {String} weight (  success | info | warning | danger )
22365  * @cfg {String} faicon font-awesomeicon
22366  * 
22367  * @constructor
22368  * Create a new alert
22369  * @param {Object} config The config object
22370  */
22371
22372
22373 Roo.bootstrap.Alert = function(config){
22374     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22375     
22376 };
22377
22378 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22379     
22380     title: '',
22381     html: '',
22382     weight: false,
22383     faicon: false,
22384     
22385     getAutoCreate : function()
22386     {
22387         
22388         var cfg = {
22389             tag : 'div',
22390             cls : 'alert',
22391             cn : [
22392                 {
22393                     tag : 'i',
22394                     cls : 'roo-alert-icon'
22395                     
22396                 },
22397                 {
22398                     tag : 'b',
22399                     cls : 'roo-alert-title',
22400                     html : this.title
22401                 },
22402                 {
22403                     tag : 'span',
22404                     cls : 'roo-alert-text',
22405                     html : this.html
22406                 }
22407             ]
22408         };
22409         
22410         if(this.faicon){
22411             cfg.cn[0].cls += ' fa ' + this.faicon;
22412         }
22413         
22414         if(this.weight){
22415             cfg.cls += ' alert-' + this.weight;
22416         }
22417         
22418         return cfg;
22419     },
22420     
22421     initEvents: function() 
22422     {
22423         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22424     },
22425     
22426     setTitle : function(str)
22427     {
22428         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
22429     },
22430     
22431     setText : function(str)
22432     {
22433         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
22434     },
22435     
22436     setWeight : function(weight)
22437     {
22438         if(this.weight){
22439             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
22440         }
22441         
22442         this.weight = weight;
22443         
22444         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
22445     },
22446     
22447     setIcon : function(icon)
22448     {
22449         if(this.faicon){
22450             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
22451         }
22452         
22453         this.faicon = icon
22454         
22455         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
22456     },
22457     
22458     hide: function() 
22459     {
22460         this.el.hide();   
22461     },
22462     
22463     show: function() 
22464     {  
22465         this.el.show();   
22466     }
22467     
22468 });
22469
22470