Roo/bootstrap/Header.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         
1373         
1374         var cfg = {
1375             tag: 'h' + (1 *this.level),
1376             html: this.html || ''
1377         } ;
1378         
1379         return cfg;
1380     }
1381    
1382 });
1383
1384  
1385
1386  /*
1387  * Based on:
1388  * Ext JS Library 1.1.1
1389  * Copyright(c) 2006-2007, Ext JS, LLC.
1390  *
1391  * Originally Released Under LGPL - original licence link has changed is not relivant.
1392  *
1393  * Fork - LGPL
1394  * <script type="text/javascript">
1395  */
1396  
1397 /**
1398  * @class Roo.bootstrap.MenuMgr
1399  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1400  * @singleton
1401  */
1402 Roo.bootstrap.MenuMgr = function(){
1403    var menus, active, groups = {}, attached = false, lastShow = new Date();
1404
1405    // private - called when first menu is created
1406    function init(){
1407        menus = {};
1408        active = new Roo.util.MixedCollection();
1409        Roo.get(document).addKeyListener(27, function(){
1410            if(active.length > 0){
1411                hideAll();
1412            }
1413        });
1414    }
1415
1416    // private
1417    function hideAll(){
1418        if(active && active.length > 0){
1419            var c = active.clone();
1420            c.each(function(m){
1421                m.hide();
1422            });
1423        }
1424    }
1425
1426    // private
1427    function onHide(m){
1428        active.remove(m);
1429        if(active.length < 1){
1430            Roo.get(document).un("mouseup", onMouseDown);
1431             
1432            attached = false;
1433        }
1434    }
1435
1436    // private
1437    function onShow(m){
1438        var last = active.last();
1439        lastShow = new Date();
1440        active.add(m);
1441        if(!attached){
1442           Roo.get(document).on("mouseup", onMouseDown);
1443            
1444            attached = true;
1445        }
1446        if(m.parentMenu){
1447           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1448           m.parentMenu.activeChild = m;
1449        }else if(last && last.isVisible()){
1450           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1451        }
1452    }
1453
1454    // private
1455    function onBeforeHide(m){
1456        if(m.activeChild){
1457            m.activeChild.hide();
1458        }
1459        if(m.autoHideTimer){
1460            clearTimeout(m.autoHideTimer);
1461            delete m.autoHideTimer;
1462        }
1463    }
1464
1465    // private
1466    function onBeforeShow(m){
1467        var pm = m.parentMenu;
1468        if(!pm && !m.allowOtherMenus){
1469            hideAll();
1470        }else if(pm && pm.activeChild && active != m){
1471            pm.activeChild.hide();
1472        }
1473    }
1474
1475    // private
1476    function onMouseDown(e){
1477         Roo.log("on MouseDown");
1478         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1479            hideAll();
1480         }
1481         
1482         
1483    }
1484
1485    // private
1486    function onBeforeCheck(mi, state){
1487        if(state){
1488            var g = groups[mi.group];
1489            for(var i = 0, l = g.length; i < l; i++){
1490                if(g[i] != mi){
1491                    g[i].setChecked(false);
1492                }
1493            }
1494        }
1495    }
1496
1497    return {
1498
1499        /**
1500         * Hides all menus that are currently visible
1501         */
1502        hideAll : function(){
1503             hideAll();  
1504        },
1505
1506        // private
1507        register : function(menu){
1508            if(!menus){
1509                init();
1510            }
1511            menus[menu.id] = menu;
1512            menu.on("beforehide", onBeforeHide);
1513            menu.on("hide", onHide);
1514            menu.on("beforeshow", onBeforeShow);
1515            menu.on("show", onShow);
1516            var g = menu.group;
1517            if(g && menu.events["checkchange"]){
1518                if(!groups[g]){
1519                    groups[g] = [];
1520                }
1521                groups[g].push(menu);
1522                menu.on("checkchange", onCheck);
1523            }
1524        },
1525
1526         /**
1527          * Returns a {@link Roo.menu.Menu} object
1528          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1529          * be used to generate and return a new Menu instance.
1530          */
1531        get : function(menu){
1532            if(typeof menu == "string"){ // menu id
1533                return menus[menu];
1534            }else if(menu.events){  // menu instance
1535                return menu;
1536            }
1537            /*else if(typeof menu.length == 'number'){ // array of menu items?
1538                return new Roo.bootstrap.Menu({items:menu});
1539            }else{ // otherwise, must be a config
1540                return new Roo.bootstrap.Menu(menu);
1541            }
1542            */
1543            return false;
1544        },
1545
1546        // private
1547        unregister : function(menu){
1548            delete menus[menu.id];
1549            menu.un("beforehide", onBeforeHide);
1550            menu.un("hide", onHide);
1551            menu.un("beforeshow", onBeforeShow);
1552            menu.un("show", onShow);
1553            var g = menu.group;
1554            if(g && menu.events["checkchange"]){
1555                groups[g].remove(menu);
1556                menu.un("checkchange", onCheck);
1557            }
1558        },
1559
1560        // private
1561        registerCheckable : function(menuItem){
1562            var g = menuItem.group;
1563            if(g){
1564                if(!groups[g]){
1565                    groups[g] = [];
1566                }
1567                groups[g].push(menuItem);
1568                menuItem.on("beforecheckchange", onBeforeCheck);
1569            }
1570        },
1571
1572        // private
1573        unregisterCheckable : function(menuItem){
1574            var g = menuItem.group;
1575            if(g){
1576                groups[g].remove(menuItem);
1577                menuItem.un("beforecheckchange", onBeforeCheck);
1578            }
1579        }
1580    };
1581 }();/*
1582  * - LGPL
1583  *
1584  * menu
1585  * 
1586  */
1587
1588 /**
1589  * @class Roo.bootstrap.Menu
1590  * @extends Roo.bootstrap.Component
1591  * Bootstrap Menu class - container for MenuItems
1592  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1593  * 
1594  * @constructor
1595  * Create a new Menu
1596  * @param {Object} config The config object
1597  */
1598
1599
1600 Roo.bootstrap.Menu = function(config){
1601     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1602     if (this.registerMenu) {
1603         Roo.bootstrap.MenuMgr.register(this);
1604     }
1605     this.addEvents({
1606         /**
1607          * @event beforeshow
1608          * Fires before this menu is displayed
1609          * @param {Roo.menu.Menu} this
1610          */
1611         beforeshow : true,
1612         /**
1613          * @event beforehide
1614          * Fires before this menu is hidden
1615          * @param {Roo.menu.Menu} this
1616          */
1617         beforehide : true,
1618         /**
1619          * @event show
1620          * Fires after this menu is displayed
1621          * @param {Roo.menu.Menu} this
1622          */
1623         show : true,
1624         /**
1625          * @event hide
1626          * Fires after this menu is hidden
1627          * @param {Roo.menu.Menu} this
1628          */
1629         hide : true,
1630         /**
1631          * @event click
1632          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1633          * @param {Roo.menu.Menu} this
1634          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1635          * @param {Roo.EventObject} e
1636          */
1637         click : true,
1638         /**
1639          * @event mouseover
1640          * Fires when the mouse is hovering over this menu
1641          * @param {Roo.menu.Menu} this
1642          * @param {Roo.EventObject} e
1643          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1644          */
1645         mouseover : true,
1646         /**
1647          * @event mouseout
1648          * Fires when the mouse exits this menu
1649          * @param {Roo.menu.Menu} this
1650          * @param {Roo.EventObject} e
1651          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1652          */
1653         mouseout : true,
1654         /**
1655          * @event itemclick
1656          * Fires when a menu item contained in this menu is clicked
1657          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1658          * @param {Roo.EventObject} e
1659          */
1660         itemclick: true
1661     });
1662     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1663 };
1664
1665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1666     
1667    /// html : false,
1668     //align : '',
1669     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1670     type: false,
1671     /**
1672      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1673      */
1674     registerMenu : true,
1675     
1676     menuItems :false, // stores the menu items..
1677     
1678     hidden:true,
1679     
1680     parentMenu : false,
1681     
1682     getChildContainer : function() {
1683         return this.el;  
1684     },
1685     
1686     getAutoCreate : function(){
1687          
1688         //if (['right'].indexOf(this.align)!==-1) {
1689         //    cfg.cn[1].cls += ' pull-right'
1690         //}
1691         
1692         
1693         var cfg = {
1694             tag : 'ul',
1695             cls : 'dropdown-menu' ,
1696             style : 'z-index:1000'
1697             
1698         }
1699         
1700         if (this.type === 'submenu') {
1701             cfg.cls = 'submenu active';
1702         }
1703         if (this.type === 'treeview') {
1704             cfg.cls = 'treeview-menu';
1705         }
1706         
1707         return cfg;
1708     },
1709     initEvents : function() {
1710         
1711        // Roo.log("ADD event");
1712        // Roo.log(this.triggerEl.dom);
1713         this.triggerEl.on('click', this.onTriggerPress, this);
1714         this.triggerEl.addClass('dropdown-toggle');
1715         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1716
1717         this.el.on("mouseover", this.onMouseOver, this);
1718         this.el.on("mouseout", this.onMouseOut, this);
1719         
1720         
1721     },
1722     findTargetItem : function(e){
1723         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1724         if(!t){
1725             return false;
1726         }
1727         //Roo.log(t);         Roo.log(t.id);
1728         if(t && t.id){
1729             //Roo.log(this.menuitems);
1730             return this.menuitems.get(t.id);
1731             
1732             //return this.items.get(t.menuItemId);
1733         }
1734         
1735         return false;
1736     },
1737     onClick : function(e){
1738         Roo.log("menu.onClick");
1739         var t = this.findTargetItem(e);
1740         if(!t || t.isContainer){
1741             return;
1742         }
1743         Roo.log(e);
1744         /*
1745         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1746             if(t == this.activeItem && t.shouldDeactivate(e)){
1747                 this.activeItem.deactivate();
1748                 delete this.activeItem;
1749                 return;
1750             }
1751             if(t.canActivate){
1752                 this.setActiveItem(t, true);
1753             }
1754             return;
1755             
1756             
1757         }
1758         */
1759        
1760         Roo.log('pass click event');
1761         
1762         t.onClick(e);
1763         
1764         this.fireEvent("click", this, t, e);
1765         
1766         this.hide();
1767     },
1768      onMouseOver : function(e){
1769         var t  = this.findTargetItem(e);
1770         //Roo.log(t);
1771         //if(t){
1772         //    if(t.canActivate && !t.disabled){
1773         //        this.setActiveItem(t, true);
1774         //    }
1775         //}
1776         
1777         this.fireEvent("mouseover", this, e, t);
1778     },
1779     isVisible : function(){
1780         return !this.hidden;
1781     },
1782      onMouseOut : function(e){
1783         var t  = this.findTargetItem(e);
1784         
1785         //if(t ){
1786         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1787         //        this.activeItem.deactivate();
1788         //        delete this.activeItem;
1789         //    }
1790         //}
1791         this.fireEvent("mouseout", this, e, t);
1792     },
1793     
1794     
1795     /**
1796      * Displays this menu relative to another element
1797      * @param {String/HTMLElement/Roo.Element} element The element to align to
1798      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1799      * the element (defaults to this.defaultAlign)
1800      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1801      */
1802     show : function(el, pos, parentMenu){
1803         this.parentMenu = parentMenu;
1804         if(!this.el){
1805             this.render();
1806         }
1807         this.fireEvent("beforeshow", this);
1808         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1809     },
1810      /**
1811      * Displays this menu at a specific xy position
1812      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1813      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1814      */
1815     showAt : function(xy, parentMenu, /* private: */_e){
1816         this.parentMenu = parentMenu;
1817         if(!this.el){
1818             this.render();
1819         }
1820         if(_e !== false){
1821             this.fireEvent("beforeshow", this);
1822             
1823             //xy = this.el.adjustForConstraints(xy);
1824         }
1825         //this.el.setXY(xy);
1826         //this.el.show();
1827         this.hideMenuItems();
1828         this.hidden = false;
1829         this.triggerEl.addClass('open');
1830         this.focus();
1831         this.fireEvent("show", this);
1832     },
1833     
1834     focus : function(){
1835         return;
1836         if(!this.hidden){
1837             this.doFocus.defer(50, this);
1838         }
1839     },
1840
1841     doFocus : function(){
1842         if(!this.hidden){
1843             this.focusEl.focus();
1844         }
1845     },
1846
1847     /**
1848      * Hides this menu and optionally all parent menus
1849      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1850      */
1851     hide : function(deep){
1852         
1853         this.hideMenuItems();
1854         if(this.el && this.isVisible()){
1855             this.fireEvent("beforehide", this);
1856             if(this.activeItem){
1857                 this.activeItem.deactivate();
1858                 this.activeItem = null;
1859             }
1860             this.triggerEl.removeClass('open');;
1861             this.hidden = true;
1862             this.fireEvent("hide", this);
1863         }
1864         if(deep === true && this.parentMenu){
1865             this.parentMenu.hide(true);
1866         }
1867     },
1868     
1869     onTriggerPress  : function(e)
1870     {
1871         
1872         Roo.log('trigger press');
1873         //Roo.log(e.getTarget());
1874        // Roo.log(this.triggerEl.dom);
1875         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1876             return;
1877         }
1878         if (this.isVisible()) {
1879             Roo.log('hide');
1880             this.hide();
1881         } else {
1882             this.show(this.triggerEl, false, false);
1883         }
1884         
1885         
1886     },
1887     
1888          
1889        
1890     
1891     hideMenuItems : function()
1892     {
1893         //$(backdrop).remove()
1894         Roo.select('.open',true).each(function(aa) {
1895             
1896             aa.removeClass('open');
1897           //var parent = getParent($(this))
1898           //var relatedTarget = { relatedTarget: this }
1899           
1900            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1901           //if (e.isDefaultPrevented()) return
1902            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1903         })
1904     },
1905     addxtypeChild : function (tree, cntr) {
1906         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1907           
1908         this.menuitems.add(comp);
1909         return comp;
1910
1911     },
1912     getEl : function()
1913     {
1914         Roo.log(this.el);
1915         return this.el;
1916     }
1917 });
1918
1919  
1920  /*
1921  * - LGPL
1922  *
1923  * menu item
1924  * 
1925  */
1926
1927
1928 /**
1929  * @class Roo.bootstrap.MenuItem
1930  * @extends Roo.bootstrap.Component
1931  * Bootstrap MenuItem class
1932  * @cfg {String} html the menu label
1933  * @cfg {String} href the link
1934  * @cfg {Boolean} preventDefault (true | false) default true
1935  * @cfg {Boolean} isContainer (true | false) default false
1936  * 
1937  * 
1938  * @constructor
1939  * Create a new MenuItem
1940  * @param {Object} config The config object
1941  */
1942
1943
1944 Roo.bootstrap.MenuItem = function(config){
1945     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1946     this.addEvents({
1947         // raw events
1948         /**
1949          * @event click
1950          * The raw click event for the entire grid.
1951          * @param {Roo.EventObject} e
1952          */
1953         "click" : true
1954     });
1955 };
1956
1957 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1958     
1959     href : false,
1960     html : false,
1961     preventDefault: true,
1962     isContainer : false,
1963     
1964     getAutoCreate : function(){
1965         
1966         if(this.isContainer){
1967             return {
1968                 tag: 'li',
1969                 cls: 'dropdown-menu-item'
1970             };
1971         }
1972         
1973         var cfg= {
1974             tag: 'li',
1975             cls: 'dropdown-menu-item',
1976             cn: [
1977                     {
1978                         tag : 'a',
1979                         href : '#',
1980                         html : 'Link'
1981                     }
1982                 ]
1983         };
1984         if (this.parent().type == 'treeview') {
1985             cfg.cls = 'treeview-menu';
1986         }
1987         
1988         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1989         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1990         return cfg;
1991     },
1992     
1993     initEvents: function() {
1994         
1995         //this.el.select('a').on('click', this.onClick, this);
1996         
1997     },
1998     onClick : function(e)
1999     {
2000         Roo.log('item on click ');
2001         //if(this.preventDefault){
2002         //    e.preventDefault();
2003         //}
2004         //this.parent().hideMenuItems();
2005         
2006         this.fireEvent('click', this, e);
2007     },
2008     getEl : function()
2009     {
2010         return this.el;
2011     }
2012 });
2013
2014  
2015
2016  /*
2017  * - LGPL
2018  *
2019  * menu separator
2020  * 
2021  */
2022
2023
2024 /**
2025  * @class Roo.bootstrap.MenuSeparator
2026  * @extends Roo.bootstrap.Component
2027  * Bootstrap MenuSeparator class
2028  * 
2029  * @constructor
2030  * Create a new MenuItem
2031  * @param {Object} config The config object
2032  */
2033
2034
2035 Roo.bootstrap.MenuSeparator = function(config){
2036     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2037 };
2038
2039 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2040     
2041     getAutoCreate : function(){
2042         var cfg = {
2043             cls: 'divider',
2044             tag : 'li'
2045         };
2046         
2047         return cfg;
2048     }
2049    
2050 });
2051
2052  
2053
2054  
2055 /*
2056 * Licence: LGPL
2057 */
2058
2059 /**
2060  * @class Roo.bootstrap.Modal
2061  * @extends Roo.bootstrap.Component
2062  * Bootstrap Modal class
2063  * @cfg {String} title Title of dialog
2064  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2065  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2066  * @cfg {Boolean} specificTitle default false
2067  * @cfg {Array} buttons Array of buttons or standard button set..
2068  * @cfg {String} buttonPosition (left|right|center) default right
2069  * @cfg {Boolean} animate default true
2070  * @cfg {Boolean} allow_close default true
2071  * 
2072  * @constructor
2073  * Create a new Modal Dialog
2074  * @param {Object} config The config object
2075  */
2076
2077 Roo.bootstrap.Modal = function(config){
2078     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2079     this.addEvents({
2080         // raw events
2081         /**
2082          * @event btnclick
2083          * The raw btnclick event for the button
2084          * @param {Roo.EventObject} e
2085          */
2086         "btnclick" : true
2087     });
2088     this.buttons = this.buttons || [];
2089      
2090     if (this.tmpl) {
2091         this.tmpl = Roo.factory(this.tmpl);
2092     }
2093     
2094 };
2095
2096 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2097     
2098     title : 'test dialog',
2099    
2100     buttons : false,
2101     
2102     // set on load...
2103      
2104     html: false,
2105     
2106     tmp: false,
2107     
2108     specificTitle: false,
2109     
2110     buttonPosition: 'right',
2111     
2112     allow_close : true,
2113     
2114     animate : true,
2115     
2116     
2117      // private
2118     bodyEl:  false,
2119     footerEl:  false,
2120     titleEl:  false,
2121     closeEl:  false,
2122     
2123     
2124     onRender : function(ct, position)
2125     {
2126         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2127      
2128         if(!this.el){
2129             var cfg = Roo.apply({},  this.getAutoCreate());
2130             cfg.id = Roo.id();
2131             //if(!cfg.name){
2132             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2133             //}
2134             //if (!cfg.name.length) {
2135             //    delete cfg.name;
2136            // }
2137             if (this.cls) {
2138                 cfg.cls += ' ' + this.cls;
2139             }
2140             if (this.style) {
2141                 cfg.style = this.style;
2142             }
2143             this.el = Roo.get(document.body).createChild(cfg, position);
2144         }
2145         //var type = this.el.dom.type;
2146         
2147         
2148         
2149         
2150         if(this.tabIndex !== undefined){
2151             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2152         }
2153         
2154         
2155         this.bodyEl = this.el.select('.modal-body',true).first();
2156         this.closeEl = this.el.select('.modal-header .close', true).first();
2157         this.footerEl = this.el.select('.modal-footer',true).first();
2158         this.titleEl = this.el.select('.modal-title',true).first();
2159         
2160         
2161          
2162         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2163         this.maskEl.enableDisplayMode("block");
2164         this.maskEl.hide();
2165         //this.el.addClass("x-dlg-modal");
2166     
2167         if (this.buttons.length) {
2168             Roo.each(this.buttons, function(bb) {
2169                 b = Roo.apply({}, bb);
2170                 b.xns = b.xns || Roo.bootstrap;
2171                 b.xtype = b.xtype || 'Button';
2172                 if (typeof(b.listeners) == 'undefined') {
2173                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2174                 }
2175                 
2176                 var btn = Roo.factory(b);
2177                 
2178                 btn.onRender(this.el.select('.modal-footer div').first());
2179                 
2180             },this);
2181         }
2182         // render the children.
2183         var nitems = [];
2184         
2185         if(typeof(this.items) != 'undefined'){
2186             var items = this.items;
2187             delete this.items;
2188
2189             for(var i =0;i < items.length;i++) {
2190                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2191             }
2192         }
2193         
2194         this.items = nitems;
2195         
2196         // where are these used - they used to be body/close/footer
2197         
2198        
2199         this.initEvents();
2200         //this.el.addClass([this.fieldClass, this.cls]);
2201         
2202     },
2203     getAutoCreate : function(){
2204         
2205         
2206         var bdy = {
2207                 cls : 'modal-body',
2208                 html : this.html || ''
2209         };
2210         
2211         var title = {
2212             tag: 'h4',
2213             cls : 'modal-title',
2214             html : this.title
2215         };
2216         
2217         if(this.specificTitle){
2218             title = this.title;
2219             
2220         };
2221         
2222         var header = [];
2223         if (this.allow_close) {
2224             header.push({
2225                 tag: 'button',
2226                 cls : 'close',
2227                 html : '&times'
2228             });
2229         }
2230         header.push(title);
2231         
2232         var modal = {
2233             cls: "modal",
2234             style : 'display: none',
2235             cn : [
2236                 {
2237                     cls: "modal-dialog",
2238                     cn : [
2239                         {
2240                             cls : "modal-content",
2241                             cn : [
2242                                 {
2243                                     cls : 'modal-header',
2244                                     cn : header
2245                                 },
2246                                 bdy,
2247                                 {
2248                                     cls : 'modal-footer',
2249                                     cn : [
2250                                         {
2251                                             tag: 'div',
2252                                             cls: 'btn-' + this.buttonPosition
2253                                         }
2254                                     ]
2255                                     
2256                                 }
2257                                 
2258                                 
2259                             ]
2260                             
2261                         }
2262                     ]
2263                         
2264                 }
2265             ]
2266         };
2267         
2268         if(this.animate){
2269             modal.cls += ' fade';
2270         }
2271         
2272         return modal;
2273           
2274     },
2275     getChildContainer : function() {
2276          
2277          return this.bodyEl;
2278         
2279     },
2280     getButtonContainer : function() {
2281          return this.el.select('.modal-footer div',true).first();
2282         
2283     },
2284     initEvents : function()
2285     {
2286         if (this.allow_close) {
2287             this.closeEl.on('click', this.hide, this);
2288         }
2289
2290     },
2291     show : function() {
2292         
2293         if (!this.rendered) {
2294             this.render();
2295         }
2296         
2297         this.el.setStyle('display', 'block');
2298         
2299         if(this.animate){
2300             var _this = this;
2301             (function(){ _this.el.addClass('in'); }).defer(50);
2302         }else{
2303             this.el.addClass('in');
2304         }
2305         
2306         // not sure how we can show data in here.. 
2307         //if (this.tmpl) {
2308         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2309         //}
2310         
2311         Roo.get(document.body).addClass("x-body-masked");
2312         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2313         this.maskEl.show();
2314         this.el.setStyle('zIndex', '10001');
2315        
2316         this.fireEvent('show', this);
2317         
2318         
2319     },
2320     hide : function()
2321     {
2322         this.maskEl.hide();
2323         Roo.get(document.body).removeClass("x-body-masked");
2324         this.el.removeClass('in');
2325         
2326         if(this.animate){
2327             var _this = this;
2328             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2329         }else{
2330             this.el.setStyle('display', 'none');
2331         }
2332         
2333         this.fireEvent('hide', this);
2334     },
2335     
2336     addButton : function(str, cb)
2337     {
2338          
2339         
2340         var b = Roo.apply({}, { html : str } );
2341         b.xns = b.xns || Roo.bootstrap;
2342         b.xtype = b.xtype || 'Button';
2343         if (typeof(b.listeners) == 'undefined') {
2344             b.listeners = { click : cb.createDelegate(this)  };
2345         }
2346         
2347         var btn = Roo.factory(b);
2348            
2349         btn.onRender(this.el.select('.modal-footer div').first());
2350         
2351         return btn;   
2352        
2353     },
2354     
2355     setDefaultButton : function(btn)
2356     {
2357         //this.el.select('.modal-footer').()
2358     },
2359     resizeTo: function(w,h)
2360     {
2361         // skip..
2362     },
2363     setContentSize  : function(w, h)
2364     {
2365         
2366     },
2367     onButtonClick: function(btn,e)
2368     {
2369         //Roo.log([a,b,c]);
2370         this.fireEvent('btnclick', btn.name, e);
2371     },
2372      /**
2373      * Set the title of the Dialog
2374      * @param {String} str new Title
2375      */
2376     setTitle: function(str) {
2377         this.titleEl.dom.innerHTML = str;    
2378     },
2379     /**
2380      * Set the body of the Dialog
2381      * @param {String} str new Title
2382      */
2383     setBody: function(str) {
2384         this.bodyEl.dom.innerHTML = str;    
2385     },
2386     /**
2387      * Set the body of the Dialog using the template
2388      * @param {Obj} data - apply this data to the template and replace the body contents.
2389      */
2390     applyBody: function(obj)
2391     {
2392         if (!this.tmpl) {
2393             Roo.log("Error - using apply Body without a template");
2394             //code
2395         }
2396         this.tmpl.overwrite(this.bodyEl, obj);
2397     }
2398     
2399 });
2400
2401
2402 Roo.apply(Roo.bootstrap.Modal,  {
2403     /**
2404          * Button config that displays a single OK button
2405          * @type Object
2406          */
2407         OK :  [{
2408             name : 'ok',
2409             weight : 'primary',
2410             html : 'OK'
2411         }], 
2412         /**
2413          * Button config that displays Yes and No buttons
2414          * @type Object
2415          */
2416         YESNO : [
2417             {
2418                 name  : 'no',
2419                 html : 'No'
2420             },
2421             {
2422                 name  :'yes',
2423                 weight : 'primary',
2424                 html : 'Yes'
2425             }
2426         ],
2427         
2428         /**
2429          * Button config that displays OK and Cancel buttons
2430          * @type Object
2431          */
2432         OKCANCEL : [
2433             {
2434                name : 'cancel',
2435                 html : 'Cancel'
2436             },
2437             {
2438                 name : 'ok',
2439                 weight : 'primary',
2440                 html : 'OK'
2441             }
2442         ],
2443         /**
2444          * Button config that displays Yes, No and Cancel buttons
2445          * @type Object
2446          */
2447         YESNOCANCEL : [
2448             {
2449                 name : 'yes',
2450                 weight : 'primary',
2451                 html : 'Yes'
2452             },
2453             {
2454                 name : 'no',
2455                 html : 'No'
2456             },
2457             {
2458                 name : 'cancel',
2459                 html : 'Cancel'
2460             }
2461         ]
2462 });
2463  
2464  /*
2465  * - LGPL
2466  *
2467  * messagebox - can be used as a replace
2468  * 
2469  */
2470 /**
2471  * @class Roo.MessageBox
2472  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2473  * Example usage:
2474  *<pre><code>
2475 // Basic alert:
2476 Roo.Msg.alert('Status', 'Changes saved successfully.');
2477
2478 // Prompt for user data:
2479 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2480     if (btn == 'ok'){
2481         // process text value...
2482     }
2483 });
2484
2485 // Show a dialog using config options:
2486 Roo.Msg.show({
2487    title:'Save Changes?',
2488    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2489    buttons: Roo.Msg.YESNOCANCEL,
2490    fn: processResult,
2491    animEl: 'elId'
2492 });
2493 </code></pre>
2494  * @singleton
2495  */
2496 Roo.bootstrap.MessageBox = function(){
2497     var dlg, opt, mask, waitTimer;
2498     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2499     var buttons, activeTextEl, bwidth;
2500
2501     
2502     // private
2503     var handleButton = function(button){
2504         dlg.hide();
2505         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2506     };
2507
2508     // private
2509     var handleHide = function(){
2510         if(opt && opt.cls){
2511             dlg.el.removeClass(opt.cls);
2512         }
2513         //if(waitTimer){
2514         //    Roo.TaskMgr.stop(waitTimer);
2515         //    waitTimer = null;
2516         //}
2517     };
2518
2519     // private
2520     var updateButtons = function(b){
2521         var width = 0;
2522         if(!b){
2523             buttons["ok"].hide();
2524             buttons["cancel"].hide();
2525             buttons["yes"].hide();
2526             buttons["no"].hide();
2527             //dlg.footer.dom.style.display = 'none';
2528             return width;
2529         }
2530         dlg.footerEl.dom.style.display = '';
2531         for(var k in buttons){
2532             if(typeof buttons[k] != "function"){
2533                 if(b[k]){
2534                     buttons[k].show();
2535                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2536                     width += buttons[k].el.getWidth()+15;
2537                 }else{
2538                     buttons[k].hide();
2539                 }
2540             }
2541         }
2542         return width;
2543     };
2544
2545     // private
2546     var handleEsc = function(d, k, e){
2547         if(opt && opt.closable !== false){
2548             dlg.hide();
2549         }
2550         if(e){
2551             e.stopEvent();
2552         }
2553     };
2554
2555     return {
2556         /**
2557          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2558          * @return {Roo.BasicDialog} The BasicDialog element
2559          */
2560         getDialog : function(){
2561            if(!dlg){
2562                 dlg = new Roo.bootstrap.Modal( {
2563                     //draggable: true,
2564                     //resizable:false,
2565                     //constraintoviewport:false,
2566                     //fixedcenter:true,
2567                     //collapsible : false,
2568                     //shim:true,
2569                     //modal: true,
2570                   //  width:400,
2571                   //  height:100,
2572                     //buttonAlign:"center",
2573                     closeClick : function(){
2574                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2575                             handleButton("no");
2576                         }else{
2577                             handleButton("cancel");
2578                         }
2579                     }
2580                 });
2581                 dlg.render();
2582                 dlg.on("hide", handleHide);
2583                 mask = dlg.mask;
2584                 //dlg.addKeyListener(27, handleEsc);
2585                 buttons = {};
2586                 this.buttons = buttons;
2587                 var bt = this.buttonText;
2588                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2589                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2590                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2591                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2592                 Roo.log(buttons)
2593                 bodyEl = dlg.bodyEl.createChild({
2594
2595                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2596                         '<textarea class="roo-mb-textarea"></textarea>' +
2597                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2598                 });
2599                 msgEl = bodyEl.dom.firstChild;
2600                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2601                 textboxEl.enableDisplayMode();
2602                 textboxEl.addKeyListener([10,13], function(){
2603                     if(dlg.isVisible() && opt && opt.buttons){
2604                         if(opt.buttons.ok){
2605                             handleButton("ok");
2606                         }else if(opt.buttons.yes){
2607                             handleButton("yes");
2608                         }
2609                     }
2610                 });
2611                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2612                 textareaEl.enableDisplayMode();
2613                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2614                 progressEl.enableDisplayMode();
2615                 var pf = progressEl.dom.firstChild;
2616                 if (pf) {
2617                     pp = Roo.get(pf.firstChild);
2618                     pp.setHeight(pf.offsetHeight);
2619                 }
2620                 
2621             }
2622             return dlg;
2623         },
2624
2625         /**
2626          * Updates the message box body text
2627          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2628          * the XHTML-compliant non-breaking space character '&amp;#160;')
2629          * @return {Roo.MessageBox} This message box
2630          */
2631         updateText : function(text){
2632             if(!dlg.isVisible() && !opt.width){
2633                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2634             }
2635             msgEl.innerHTML = text || '&#160;';
2636       
2637             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2638             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2639             var w = Math.max(
2640                     Math.min(opt.width || cw , this.maxWidth), 
2641                     Math.max(opt.minWidth || this.minWidth, bwidth)
2642             );
2643             if(opt.prompt){
2644                 activeTextEl.setWidth(w);
2645             }
2646             if(dlg.isVisible()){
2647                 dlg.fixedcenter = false;
2648             }
2649             // to big, make it scroll. = But as usual stupid IE does not support
2650             // !important..
2651             
2652             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2653                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2654                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2655             } else {
2656                 bodyEl.dom.style.height = '';
2657                 bodyEl.dom.style.overflowY = '';
2658             }
2659             if (cw > w) {
2660                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2661             } else {
2662                 bodyEl.dom.style.overflowX = '';
2663             }
2664             
2665             dlg.setContentSize(w, bodyEl.getHeight());
2666             if(dlg.isVisible()){
2667                 dlg.fixedcenter = true;
2668             }
2669             return this;
2670         },
2671
2672         /**
2673          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2674          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2675          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2676          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2677          * @return {Roo.MessageBox} This message box
2678          */
2679         updateProgress : function(value, text){
2680             if(text){
2681                 this.updateText(text);
2682             }
2683             if (pp) { // weird bug on my firefox - for some reason this is not defined
2684                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2685             }
2686             return this;
2687         },        
2688
2689         /**
2690          * Returns true if the message box is currently displayed
2691          * @return {Boolean} True if the message box is visible, else false
2692          */
2693         isVisible : function(){
2694             return dlg && dlg.isVisible();  
2695         },
2696
2697         /**
2698          * Hides the message box if it is displayed
2699          */
2700         hide : function(){
2701             if(this.isVisible()){
2702                 dlg.hide();
2703             }  
2704         },
2705
2706         /**
2707          * Displays a new message box, or reinitializes an existing message box, based on the config options
2708          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2709          * The following config object properties are supported:
2710          * <pre>
2711 Property    Type             Description
2712 ----------  ---------------  ------------------------------------------------------------------------------------
2713 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2714                                    closes (defaults to undefined)
2715 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2716                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2717 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2718                                    progress and wait dialogs will ignore this property and always hide the
2719                                    close button as they can only be closed programmatically.
2720 cls               String           A custom CSS class to apply to the message box element
2721 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2722                                    displayed (defaults to 75)
2723 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2724                                    function will be btn (the name of the button that was clicked, if applicable,
2725                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2726                                    Progress and wait dialogs will ignore this option since they do not respond to
2727                                    user actions and can only be closed programmatically, so any required function
2728                                    should be called by the same code after it closes the dialog.
2729 icon              String           A CSS class that provides a background image to be used as an icon for
2730                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2731 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2732 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2733 modal             Boolean          False to allow user interaction with the page while the message box is
2734                                    displayed (defaults to true)
2735 msg               String           A string that will replace the existing message box body text (defaults
2736                                    to the XHTML-compliant non-breaking space character '&#160;')
2737 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2738 progress          Boolean          True to display a progress bar (defaults to false)
2739 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2740 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2741 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2742 title             String           The title text
2743 value             String           The string value to set into the active textbox element if displayed
2744 wait              Boolean          True to display a progress bar (defaults to false)
2745 width             Number           The width of the dialog in pixels
2746 </pre>
2747          *
2748          * Example usage:
2749          * <pre><code>
2750 Roo.Msg.show({
2751    title: 'Address',
2752    msg: 'Please enter your address:',
2753    width: 300,
2754    buttons: Roo.MessageBox.OKCANCEL,
2755    multiline: true,
2756    fn: saveAddress,
2757    animEl: 'addAddressBtn'
2758 });
2759 </code></pre>
2760          * @param {Object} config Configuration options
2761          * @return {Roo.MessageBox} This message box
2762          */
2763         show : function(options)
2764         {
2765             
2766             // this causes nightmares if you show one dialog after another
2767             // especially on callbacks..
2768              
2769             if(this.isVisible()){
2770                 
2771                 this.hide();
2772                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2773                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2774                 Roo.log("New Dialog Message:" +  options.msg )
2775                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2776                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2777                 
2778             }
2779             var d = this.getDialog();
2780             opt = options;
2781             d.setTitle(opt.title || "&#160;");
2782             d.closeEl.setDisplayed(opt.closable !== false);
2783             activeTextEl = textboxEl;
2784             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2785             if(opt.prompt){
2786                 if(opt.multiline){
2787                     textboxEl.hide();
2788                     textareaEl.show();
2789                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2790                         opt.multiline : this.defaultTextHeight);
2791                     activeTextEl = textareaEl;
2792                 }else{
2793                     textboxEl.show();
2794                     textareaEl.hide();
2795                 }
2796             }else{
2797                 textboxEl.hide();
2798                 textareaEl.hide();
2799             }
2800             progressEl.setDisplayed(opt.progress === true);
2801             this.updateProgress(0);
2802             activeTextEl.dom.value = opt.value || "";
2803             if(opt.prompt){
2804                 dlg.setDefaultButton(activeTextEl);
2805             }else{
2806                 var bs = opt.buttons;
2807                 var db = null;
2808                 if(bs && bs.ok){
2809                     db = buttons["ok"];
2810                 }else if(bs && bs.yes){
2811                     db = buttons["yes"];
2812                 }
2813                 dlg.setDefaultButton(db);
2814             }
2815             bwidth = updateButtons(opt.buttons);
2816             this.updateText(opt.msg);
2817             if(opt.cls){
2818                 d.el.addClass(opt.cls);
2819             }
2820             d.proxyDrag = opt.proxyDrag === true;
2821             d.modal = opt.modal !== false;
2822             d.mask = opt.modal !== false ? mask : false;
2823             if(!d.isVisible()){
2824                 // force it to the end of the z-index stack so it gets a cursor in FF
2825                 document.body.appendChild(dlg.el.dom);
2826                 d.animateTarget = null;
2827                 d.show(options.animEl);
2828             }
2829             return this;
2830         },
2831
2832         /**
2833          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2834          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2835          * and closing the message box when the process is complete.
2836          * @param {String} title The title bar text
2837          * @param {String} msg The message box body text
2838          * @return {Roo.MessageBox} This message box
2839          */
2840         progress : function(title, msg){
2841             this.show({
2842                 title : title,
2843                 msg : msg,
2844                 buttons: false,
2845                 progress:true,
2846                 closable:false,
2847                 minWidth: this.minProgressWidth,
2848                 modal : true
2849             });
2850             return this;
2851         },
2852
2853         /**
2854          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2855          * If a callback function is passed it will be called after the user clicks the button, and the
2856          * id of the button that was clicked will be passed as the only parameter to the callback
2857          * (could also be the top-right close button).
2858          * @param {String} title The title bar text
2859          * @param {String} msg The message box body text
2860          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2861          * @param {Object} scope (optional) The scope of the callback function
2862          * @return {Roo.MessageBox} This message box
2863          */
2864         alert : function(title, msg, fn, scope){
2865             this.show({
2866                 title : title,
2867                 msg : msg,
2868                 buttons: this.OK,
2869                 fn: fn,
2870                 scope : scope,
2871                 modal : true
2872             });
2873             return this;
2874         },
2875
2876         /**
2877          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2878          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2879          * You are responsible for closing the message box when the process is complete.
2880          * @param {String} msg The message box body text
2881          * @param {String} title (optional) The title bar text
2882          * @return {Roo.MessageBox} This message box
2883          */
2884         wait : function(msg, title){
2885             this.show({
2886                 title : title,
2887                 msg : msg,
2888                 buttons: false,
2889                 closable:false,
2890                 progress:true,
2891                 modal:true,
2892                 width:300,
2893                 wait:true
2894             });
2895             waitTimer = Roo.TaskMgr.start({
2896                 run: function(i){
2897                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2898                 },
2899                 interval: 1000
2900             });
2901             return this;
2902         },
2903
2904         /**
2905          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2906          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2907          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2908          * @param {String} title The title bar text
2909          * @param {String} msg The message box body text
2910          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2911          * @param {Object} scope (optional) The scope of the callback function
2912          * @return {Roo.MessageBox} This message box
2913          */
2914         confirm : function(title, msg, fn, scope){
2915             this.show({
2916                 title : title,
2917                 msg : msg,
2918                 buttons: this.YESNO,
2919                 fn: fn,
2920                 scope : scope,
2921                 modal : true
2922             });
2923             return this;
2924         },
2925
2926         /**
2927          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2928          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2929          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2930          * (could also be the top-right close button) and the text that was entered will be passed as the two
2931          * parameters to the callback.
2932          * @param {String} title The title bar text
2933          * @param {String} msg The message box body text
2934          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2935          * @param {Object} scope (optional) The scope of the callback function
2936          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2937          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2938          * @return {Roo.MessageBox} This message box
2939          */
2940         prompt : function(title, msg, fn, scope, multiline){
2941             this.show({
2942                 title : title,
2943                 msg : msg,
2944                 buttons: this.OKCANCEL,
2945                 fn: fn,
2946                 minWidth:250,
2947                 scope : scope,
2948                 prompt:true,
2949                 multiline: multiline,
2950                 modal : true
2951             });
2952             return this;
2953         },
2954
2955         /**
2956          * Button config that displays a single OK button
2957          * @type Object
2958          */
2959         OK : {ok:true},
2960         /**
2961          * Button config that displays Yes and No buttons
2962          * @type Object
2963          */
2964         YESNO : {yes:true, no:true},
2965         /**
2966          * Button config that displays OK and Cancel buttons
2967          * @type Object
2968          */
2969         OKCANCEL : {ok:true, cancel:true},
2970         /**
2971          * Button config that displays Yes, No and Cancel buttons
2972          * @type Object
2973          */
2974         YESNOCANCEL : {yes:true, no:true, cancel:true},
2975
2976         /**
2977          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2978          * @type Number
2979          */
2980         defaultTextHeight : 75,
2981         /**
2982          * The maximum width in pixels of the message box (defaults to 600)
2983          * @type Number
2984          */
2985         maxWidth : 600,
2986         /**
2987          * The minimum width in pixels of the message box (defaults to 100)
2988          * @type Number
2989          */
2990         minWidth : 100,
2991         /**
2992          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2993          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2994          * @type Number
2995          */
2996         minProgressWidth : 250,
2997         /**
2998          * An object containing the default button text strings that can be overriden for localized language support.
2999          * Supported properties are: ok, cancel, yes and no.
3000          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3001          * @type Object
3002          */
3003         buttonText : {
3004             ok : "OK",
3005             cancel : "Cancel",
3006             yes : "Yes",
3007             no : "No"
3008         }
3009     };
3010 }();
3011
3012 /**
3013  * Shorthand for {@link Roo.MessageBox}
3014  */
3015 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3016 Roo.Msg = Roo.Msg || Roo.MessageBox;
3017 /*
3018  * - LGPL
3019  *
3020  * navbar
3021  * 
3022  */
3023
3024 /**
3025  * @class Roo.bootstrap.Navbar
3026  * @extends Roo.bootstrap.Component
3027  * Bootstrap Navbar class
3028
3029  * @constructor
3030  * Create a new Navbar
3031  * @param {Object} config The config object
3032  */
3033
3034
3035 Roo.bootstrap.Navbar = function(config){
3036     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3037     
3038 };
3039
3040 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3041     
3042     
3043    
3044     // private
3045     navItems : false,
3046     loadMask : false,
3047     
3048     
3049     getAutoCreate : function(){
3050         
3051         
3052         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3053         
3054     },
3055     
3056     initEvents :function ()
3057     {
3058         //Roo.log(this.el.select('.navbar-toggle',true));
3059         this.el.select('.navbar-toggle',true).on('click', function() {
3060            // Roo.log('click');
3061             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3062         }, this);
3063         
3064         var mark = {
3065             tag: "div",
3066             cls:"x-dlg-mask"
3067         }
3068         
3069         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3070         
3071         var size = this.el.getSize();
3072         this.maskEl.setSize(size.width, size.height);
3073         this.maskEl.enableDisplayMode("block");
3074         this.maskEl.hide();
3075         
3076         if(this.loadMask){
3077             this.maskEl.show();
3078         }
3079     },
3080     
3081     
3082     getChildContainer : function()
3083     {
3084         if (this.el.select('.collapse').getCount()) {
3085             return this.el.select('.collapse',true).first();
3086         }
3087         
3088         return this.el;
3089     },
3090     
3091     mask : function()
3092     {
3093         this.maskEl.show();
3094     },
3095     
3096     unmask : function()
3097     {
3098         this.maskEl.hide();
3099     } 
3100     
3101     
3102     
3103     
3104 });
3105
3106
3107
3108  
3109
3110  /*
3111  * - LGPL
3112  *
3113  * navbar
3114  * 
3115  */
3116
3117 /**
3118  * @class Roo.bootstrap.NavSimplebar
3119  * @extends Roo.bootstrap.Navbar
3120  * Bootstrap Sidebar class
3121  *
3122  * @cfg {Boolean} inverse is inverted color
3123  * 
3124  * @cfg {String} type (nav | pills | tabs)
3125  * @cfg {Boolean} arrangement stacked | justified
3126  * @cfg {String} align (left | right) alignment
3127  * 
3128  * @cfg {Boolean} main (true|false) main nav bar? default false
3129  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3130  * 
3131  * @cfg {String} tag (header|footer|nav|div) default is nav 
3132
3133  * 
3134  * 
3135  * 
3136  * @constructor
3137  * Create a new Sidebar
3138  * @param {Object} config The config object
3139  */
3140
3141
3142 Roo.bootstrap.NavSimplebar = function(config){
3143     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3144 };
3145
3146 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3147     
3148     inverse: false,
3149     
3150     type: false,
3151     arrangement: '',
3152     align : false,
3153     
3154     
3155     
3156     main : false,
3157     
3158     
3159     tag : false,
3160     
3161     
3162     getAutoCreate : function(){
3163         
3164         
3165         var cfg = {
3166             tag : this.tag || 'div',
3167             cls : 'navbar'
3168         };
3169           
3170         
3171         cfg.cn = [
3172             {
3173                 cls: 'nav',
3174                 tag : 'ul'
3175             }
3176         ];
3177         
3178          
3179         this.type = this.type || 'nav';
3180         if (['tabs','pills'].indexOf(this.type)!==-1) {
3181             cfg.cn[0].cls += ' nav-' + this.type
3182         
3183         
3184         } else {
3185             if (this.type!=='nav') {
3186                 Roo.log('nav type must be nav/tabs/pills')
3187             }
3188             cfg.cn[0].cls += ' navbar-nav'
3189         }
3190         
3191         
3192         
3193         
3194         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3195             cfg.cn[0].cls += ' nav-' + this.arrangement;
3196         }
3197         
3198         
3199         if (this.align === 'right') {
3200             cfg.cn[0].cls += ' navbar-right';
3201         }
3202         
3203         if (this.inverse) {
3204             cfg.cls += ' navbar-inverse';
3205             
3206         }
3207         
3208         
3209         return cfg;
3210     
3211         
3212     }
3213     
3214     
3215     
3216 });
3217
3218
3219
3220  
3221
3222  
3223        /*
3224  * - LGPL
3225  *
3226  * navbar
3227  * 
3228  */
3229
3230 /**
3231  * @class Roo.bootstrap.NavHeaderbar
3232  * @extends Roo.bootstrap.NavSimplebar
3233  * Bootstrap Sidebar class
3234  *
3235  * @cfg {String} brand what is brand
3236  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3237  * @cfg {String} brand_href href of the brand
3238  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3239  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3240  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3241  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3242  * 
3243  * @constructor
3244  * Create a new Sidebar
3245  * @param {Object} config The config object
3246  */
3247
3248
3249 Roo.bootstrap.NavHeaderbar = function(config){
3250     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3251       
3252 };
3253
3254 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3255     
3256     position: '',
3257     brand: '',
3258     brand_href: false,
3259     srButton : true,
3260     autohide : false,
3261     desktopCenter : false,
3262    
3263     
3264     getAutoCreate : function(){
3265         
3266         var   cfg = {
3267             tag: this.nav || 'nav',
3268             cls: 'navbar',
3269             role: 'navigation',
3270             cn: []
3271         };
3272         
3273         var cn = cfg.cn;
3274         if (this.desktopCenter) {
3275             cn.push({cls : 'container', cn : []});
3276             cn = cn[0].cn;
3277         }
3278         
3279         if(this.srButton){
3280             cn.push({
3281                 tag: 'div',
3282                 cls: 'navbar-header',
3283                 cn: [
3284                     {
3285                         tag: 'button',
3286                         type: 'button',
3287                         cls: 'navbar-toggle',
3288                         'data-toggle': 'collapse',
3289                         cn: [
3290                             {
3291                                 tag: 'span',
3292                                 cls: 'sr-only',
3293                                 html: 'Toggle navigation'
3294                             },
3295                             {
3296                                 tag: 'span',
3297                                 cls: 'icon-bar'
3298                             },
3299                             {
3300                                 tag: 'span',
3301                                 cls: 'icon-bar'
3302                             },
3303                             {
3304                                 tag: 'span',
3305                                 cls: 'icon-bar'
3306                             }
3307                         ]
3308                     }
3309                 ]
3310             });
3311         }
3312         
3313         cn.push({
3314             tag: 'div',
3315             cls: 'collapse navbar-collapse',
3316             cn : []
3317         });
3318         
3319         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3320         
3321         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3322             cfg.cls += ' navbar-' + this.position;
3323             
3324             // tag can override this..
3325             
3326             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3327         }
3328         
3329         if (this.brand !== '') {
3330             cn[0].cn.push({
3331                 tag: 'a',
3332                 href: this.brand_href ? this.brand_href : '#',
3333                 cls: 'navbar-brand',
3334                 cn: [
3335                 this.brand
3336                 ]
3337             });
3338         }
3339         
3340         if(this.main){
3341             cfg.cls += ' main-nav';
3342         }
3343         
3344         
3345         return cfg;
3346
3347         
3348     },
3349     getHeaderChildContainer : function()
3350     {
3351         if (this.el.select('.navbar-header').getCount()) {
3352             return this.el.select('.navbar-header',true).first();
3353         }
3354         
3355         return this.getChildContainer();
3356     },
3357     
3358     
3359     initEvents : function()
3360     {
3361         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3362         
3363         if (this.autohide) {
3364             
3365             var prevScroll = 0;
3366             var ft = this.el;
3367             
3368             Roo.get(document).on('scroll',function(e) {
3369                 var ns = Roo.get(document).getScroll().top;
3370                 var os = prevScroll;
3371                 prevScroll = ns;
3372                 
3373                 if(ns > os){
3374                     ft.removeClass('slideDown');
3375                     ft.addClass('slideUp');
3376                     return;
3377                 }
3378                 ft.removeClass('slideUp');
3379                 ft.addClass('slideDown');
3380                  
3381               
3382           },this);
3383         }
3384     }    
3385           
3386       
3387     
3388     
3389 });
3390
3391
3392
3393  
3394
3395  /*
3396  * - LGPL
3397  *
3398  * navbar
3399  * 
3400  */
3401
3402 /**
3403  * @class Roo.bootstrap.NavSidebar
3404  * @extends Roo.bootstrap.Navbar
3405  * Bootstrap Sidebar class
3406  * 
3407  * @constructor
3408  * Create a new Sidebar
3409  * @param {Object} config The config object
3410  */
3411
3412
3413 Roo.bootstrap.NavSidebar = function(config){
3414     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3415 };
3416
3417 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3418     
3419     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3420     
3421     getAutoCreate : function(){
3422         
3423         
3424         return  {
3425             tag: 'div',
3426             cls: 'sidebar sidebar-nav'
3427         };
3428     
3429         
3430     }
3431     
3432     
3433     
3434 });
3435
3436
3437
3438  
3439
3440  /*
3441  * - LGPL
3442  *
3443  * nav group
3444  * 
3445  */
3446
3447 /**
3448  * @class Roo.bootstrap.NavGroup
3449  * @extends Roo.bootstrap.Component
3450  * Bootstrap NavGroup class
3451  * @cfg {String} align left | right
3452  * @cfg {Boolean} inverse false | true
3453  * @cfg {String} type (nav|pills|tab) default nav
3454  * @cfg {String} navId - reference Id for navbar.
3455
3456  * 
3457  * @constructor
3458  * Create a new nav group
3459  * @param {Object} config The config object
3460  */
3461
3462 Roo.bootstrap.NavGroup = function(config){
3463     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3464     this.navItems = [];
3465    
3466     Roo.bootstrap.NavGroup.register(this);
3467      this.addEvents({
3468         /**
3469              * @event changed
3470              * Fires when the active item changes
3471              * @param {Roo.bootstrap.NavGroup} this
3472              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3473              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3474          */
3475         'changed': true
3476      });
3477     
3478 };
3479
3480 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3481     
3482     align: '',
3483     inverse: false,
3484     form: false,
3485     type: 'nav',
3486     navId : '',
3487     // private
3488     
3489     navItems : false, 
3490     
3491     getAutoCreate : function()
3492     {
3493         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3494         
3495         cfg = {
3496             tag : 'ul',
3497             cls: 'nav' 
3498         }
3499         
3500         if (['tabs','pills'].indexOf(this.type)!==-1) {
3501             cfg.cls += ' nav-' + this.type
3502         } else {
3503             if (this.type!=='nav') {
3504                 Roo.log('nav type must be nav/tabs/pills')
3505             }
3506             cfg.cls += ' navbar-nav'
3507         }
3508         
3509         if (this.parent().sidebar) {
3510             cfg = {
3511                 tag: 'ul',
3512                 cls: 'dashboard-menu sidebar-menu'
3513             }
3514             
3515             return cfg;
3516         }
3517         
3518         if (this.form === true) {
3519             cfg = {
3520                 tag: 'form',
3521                 cls: 'navbar-form'
3522             }
3523             
3524             if (this.align === 'right') {
3525                 cfg.cls += ' navbar-right';
3526             } else {
3527                 cfg.cls += ' navbar-left';
3528             }
3529         }
3530         
3531         if (this.align === 'right') {
3532             cfg.cls += ' navbar-right';
3533         }
3534         
3535         if (this.inverse) {
3536             cfg.cls += ' navbar-inverse';
3537             
3538         }
3539         
3540         
3541         return cfg;
3542     },
3543     /**
3544     * sets the active Navigation item
3545     * @param {Roo.bootstrap.NavItem} the new current navitem
3546     */
3547     setActiveItem : function(item)
3548     {
3549         var prev = false;
3550         Roo.each(this.navItems, function(v){
3551             if (v == item) {
3552                 return ;
3553             }
3554             if (v.isActive()) {
3555                 v.setActive(false, true);
3556                 prev = v;
3557                 
3558             }
3559             
3560         });
3561
3562         item.setActive(true, true);
3563         this.fireEvent('changed', this, item, prev);
3564         
3565         
3566     },
3567     /**
3568     * gets the active Navigation item
3569     * @return {Roo.bootstrap.NavItem} the current navitem
3570     */
3571     getActive : function()
3572     {
3573         
3574         var prev = false;
3575         Roo.each(this.navItems, function(v){
3576             
3577             if (v.isActive()) {
3578                 prev = v;
3579                 
3580             }
3581             
3582         });
3583         return prev;
3584     },
3585     
3586     indexOfNav : function()
3587     {
3588         
3589         var prev = false;
3590         Roo.each(this.navItems, function(v,i){
3591             
3592             if (v.isActive()) {
3593                 prev = i;
3594                 
3595             }
3596             
3597         });
3598         return prev;
3599     },
3600     /**
3601     * adds a Navigation item
3602     * @param {Roo.bootstrap.NavItem} the navitem to add
3603     */
3604     addItem : function(cfg)
3605     {
3606         var cn = new Roo.bootstrap.NavItem(cfg);
3607         this.register(cn);
3608         cn.parentId = this.id;
3609         cn.onRender(this.el, null);
3610         return cn;
3611     },
3612     /**
3613     * register a Navigation item
3614     * @param {Roo.bootstrap.NavItem} the navitem to add
3615     */
3616     register : function(item)
3617     {
3618         this.navItems.push( item);
3619         item.navId = this.navId;
3620     
3621     },
3622     
3623     /**
3624     * clear all the Navigation item
3625     */
3626    
3627     clearAll : function()
3628     {
3629         this.navItems = [];
3630         this.el.dom.innerHTML = '';
3631     },
3632     
3633     getNavItem: function(tabId)
3634     {
3635         var ret = false;
3636         Roo.each(this.navItems, function(e) {
3637             if (e.tabId == tabId) {
3638                ret =  e;
3639                return false;
3640             }
3641             return true;
3642             
3643         });
3644         return ret;
3645     },
3646     
3647     setActiveNext : function()
3648     {
3649         var i = this.indexOfNav(this.getActive());
3650         if (i > this.navItems.length) {
3651             return;
3652         }
3653         this.setActiveItem(this.navItems[i+1]);
3654     },
3655     setActivePrev : function()
3656     {
3657         var i = this.indexOfNav(this.getActive());
3658         if (i  < 1) {
3659             return;
3660         }
3661         this.setActiveItem(this.navItems[i-1]);
3662     },
3663     clearWasActive : function(except) {
3664         Roo.each(this.navItems, function(e) {
3665             if (e.tabId != except.tabId && e.was_active) {
3666                e.was_active = false;
3667                return false;
3668             }
3669             return true;
3670             
3671         });
3672     },
3673     getWasActive : function ()
3674     {
3675         var r = false;
3676         Roo.each(this.navItems, function(e) {
3677             if (e.was_active) {
3678                r = e;
3679                return false;
3680             }
3681             return true;
3682             
3683         });
3684         return r;
3685     }
3686     
3687     
3688 });
3689
3690  
3691 Roo.apply(Roo.bootstrap.NavGroup, {
3692     
3693     groups: {},
3694      /**
3695     * register a Navigation Group
3696     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3697     */
3698     register : function(navgrp)
3699     {
3700         this.groups[navgrp.navId] = navgrp;
3701         
3702     },
3703     /**
3704     * fetch a Navigation Group based on the navigation ID
3705     * @param {string} the navgroup to add
3706     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3707     */
3708     get: function(navId) {
3709         if (typeof(this.groups[navId]) == 'undefined') {
3710             return false;
3711             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3712         }
3713         return this.groups[navId] ;
3714     }
3715     
3716     
3717     
3718 });
3719
3720  /*
3721  * - LGPL
3722  *
3723  * row
3724  * 
3725  */
3726
3727 /**
3728  * @class Roo.bootstrap.NavItem
3729  * @extends Roo.bootstrap.Component
3730  * Bootstrap Navbar.NavItem class
3731  * @cfg {String} href  link to
3732  * @cfg {String} html content of button
3733  * @cfg {String} badge text inside badge
3734  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3735  * @cfg {String} glyphicon name of glyphicon
3736  * @cfg {String} icon name of font awesome icon
3737  * @cfg {Boolean} active Is item active
3738  * @cfg {Boolean} disabled Is item disabled
3739  
3740  * @cfg {Boolean} preventDefault (true | false) default false
3741  * @cfg {String} tabId the tab that this item activates.
3742  * @cfg {String} tagtype (a|span) render as a href or span?
3743   
3744  * @constructor
3745  * Create a new Navbar Item
3746  * @param {Object} config The config object
3747  */
3748 Roo.bootstrap.NavItem = function(config){
3749     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3750     this.addEvents({
3751         // raw events
3752         /**
3753          * @event click
3754          * The raw click event for the entire grid.
3755          * @param {Roo.EventObject} e
3756          */
3757         "click" : true,
3758          /**
3759             * @event changed
3760             * Fires when the active item active state changes
3761             * @param {Roo.bootstrap.NavItem} this
3762             * @param {boolean} state the new state
3763              
3764          */
3765         'changed': true
3766     });
3767    
3768 };
3769
3770 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3771     
3772     href: false,
3773     html: '',
3774     badge: '',
3775     icon: false,
3776     glyphicon: false,
3777     active: false,
3778     preventDefault : false,
3779     tabId : false,
3780     tagtype : 'a',
3781     disabled : false,
3782     
3783     was_active : false,
3784     
3785     getAutoCreate : function(){
3786          
3787         var cfg = {
3788             tag: 'li',
3789             cls: 'nav-item'
3790             
3791         }
3792         if (this.active) {
3793             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3794         }
3795         if (this.disabled) {
3796             cfg.cls += ' disabled';
3797         }
3798         
3799         if (this.href || this.html || this.glyphicon || this.icon) {
3800             cfg.cn = [
3801                 {
3802                     tag: this.tagtype,
3803                     href : this.href || "#",
3804                     html: this.html || ''
3805                 }
3806             ];
3807             
3808             if (this.icon) {
3809                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3810             }
3811
3812             if(this.glyphicon) {
3813                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3814             }
3815             
3816             if (this.menu) {
3817                 
3818                 cfg.cn[0].html += " <span class='caret'></span>";
3819              
3820             }
3821             
3822             if (this.badge !== '') {
3823                  
3824                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3825             }
3826         }
3827         
3828         
3829         
3830         return cfg;
3831     },
3832     initEvents: function() 
3833     {
3834         if (typeof (this.menu) != 'undefined') {
3835             this.menu.parentType = this.xtype;
3836             this.menu.triggerEl = this.el;
3837             this.menu = this.addxtype(Roo.apply({}, this.menu));
3838         }
3839         
3840         this.el.select('a',true).on('click', this.onClick, this);
3841         
3842         if(this.tagtype == 'span'){
3843             this.el.select('span',true).on('click', this.onClick, this);
3844         }
3845        
3846         // at this point parent should be available..
3847         this.parent().register(this);
3848     },
3849     
3850     onClick : function(e)
3851     {
3852         if(this.preventDefault || this.href == '#'){
3853             e.preventDefault();
3854         }
3855         
3856         if (this.disabled) {
3857             return;
3858         }
3859         
3860         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3861         if (tg && tg.transition) {
3862             Roo.log("waiting for the transitionend");
3863             return;
3864         }
3865         
3866         Roo.log("fire event clicked");
3867         if(this.fireEvent('click', this, e) === false){
3868             return;
3869         };
3870         
3871         if(this.tagtype == 'span'){
3872             return;
3873         }
3874         
3875         var p = this.parent();
3876         if (['tabs','pills'].indexOf(p.type)!==-1) {
3877             if (typeof(p.setActiveItem) !== 'undefined') {
3878                 p.setActiveItem(this);
3879             }
3880         }
3881         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3882         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3883             // remove the collapsed menu expand...
3884             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3885         }
3886         
3887     },
3888     
3889     isActive: function () {
3890         return this.active
3891     },
3892     setActive : function(state, fire, is_was_active)
3893     {
3894         if (this.active && !state & this.navId) {
3895             this.was_active = true;
3896             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3897             if (nv) {
3898                 nv.clearWasActive(this);
3899             }
3900             
3901         }
3902         this.active = state;
3903         
3904         if (!state ) {
3905             this.el.removeClass('active');
3906         } else if (!this.el.hasClass('active')) {
3907             this.el.addClass('active');
3908         }
3909         if (fire) {
3910             this.fireEvent('changed', this, state);
3911         }
3912         
3913         // show a panel if it's registered and related..
3914         
3915         if (!this.navId || !this.tabId || !state || is_was_active) {
3916             return;
3917         }
3918         
3919         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3920         if (!tg) {
3921             return;
3922         }
3923         var pan = tg.getPanelByName(this.tabId);
3924         if (!pan) {
3925             return;
3926         }
3927         // if we can not flip to new panel - go back to old nav highlight..
3928         if (false == tg.showPanel(pan)) {
3929             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3930             if (nv) {
3931                 var onav = nv.getWasActive();
3932                 if (onav) {
3933                     onav.setActive(true, false, true);
3934                 }
3935             }
3936             
3937         }
3938         
3939         
3940         
3941     },
3942      // this should not be here...
3943     setDisabled : function(state)
3944     {
3945         this.disabled = state;
3946         if (!state ) {
3947             this.el.removeClass('disabled');
3948         } else if (!this.el.hasClass('disabled')) {
3949             this.el.addClass('disabled');
3950         }
3951         
3952     },
3953     
3954     /**
3955      * Fetch the element to display the tooltip on.
3956      * @return {Roo.Element} defaults to this.el
3957      */
3958     tooltipEl : function()
3959     {
3960         return this.el.select('' + this.tagtype + '', true).first();
3961     }
3962 });
3963  
3964
3965  /*
3966  * - LGPL
3967  *
3968  * sidebar item
3969  *
3970  *  li
3971  *    <span> icon </span>
3972  *    <span> text </span>
3973  *    <span>badge </span>
3974  */
3975
3976 /**
3977  * @class Roo.bootstrap.NavSidebarItem
3978  * @extends Roo.bootstrap.NavItem
3979  * Bootstrap Navbar.NavSidebarItem class
3980  * @constructor
3981  * Create a new Navbar Button
3982  * @param {Object} config The config object
3983  */
3984 Roo.bootstrap.NavSidebarItem = function(config){
3985     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3986     this.addEvents({
3987         // raw events
3988         /**
3989          * @event click
3990          * The raw click event for the entire grid.
3991          * @param {Roo.EventObject} e
3992          */
3993         "click" : true,
3994          /**
3995             * @event changed
3996             * Fires when the active item active state changes
3997             * @param {Roo.bootstrap.NavSidebarItem} this
3998             * @param {boolean} state the new state
3999              
4000          */
4001         'changed': true
4002     });
4003    
4004 };
4005
4006 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4007     
4008     
4009     getAutoCreate : function(){
4010         
4011         
4012         var a = {
4013                 tag: 'a',
4014                 href : this.href || '#',
4015                 cls: '',
4016                 html : '',
4017                 cn : []
4018         };
4019         var cfg = {
4020             tag: 'li',
4021             cls: '',
4022             cn: [ a ]
4023         }
4024         var span = {
4025             tag: 'span',
4026             html : this.html || ''
4027         }
4028         
4029         
4030         if (this.active) {
4031             cfg.cls += ' active';
4032         }
4033         
4034         // left icon..
4035         if (this.glyphicon || this.icon) {
4036             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4037             a.cn.push({ tag : 'i', cls : c }) ;
4038         }
4039         // html..
4040         a.cn.push(span);
4041         // then badge..
4042         if (this.badge !== '') {
4043             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4044         }
4045         // fi
4046         if (this.menu) {
4047             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4048             a.cls += 'dropdown-toggle treeview' ;
4049             
4050         }
4051         
4052         
4053         
4054         return cfg;
4055          
4056            
4057     }
4058    
4059      
4060  
4061 });
4062  
4063
4064  /*
4065  * - LGPL
4066  *
4067  * row
4068  * 
4069  */
4070
4071 /**
4072  * @class Roo.bootstrap.Row
4073  * @extends Roo.bootstrap.Component
4074  * Bootstrap Row class (contains columns...)
4075  * 
4076  * @constructor
4077  * Create a new Row
4078  * @param {Object} config The config object
4079  */
4080
4081 Roo.bootstrap.Row = function(config){
4082     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4083 };
4084
4085 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4086     
4087     getAutoCreate : function(){
4088        return {
4089             cls: 'row clearfix'
4090        };
4091     }
4092     
4093     
4094 });
4095
4096  
4097
4098  /*
4099  * - LGPL
4100  *
4101  * element
4102  * 
4103  */
4104
4105 /**
4106  * @class Roo.bootstrap.Element
4107  * @extends Roo.bootstrap.Component
4108  * Bootstrap Element class
4109  * @cfg {String} html contents of the element
4110  * @cfg {String} tag tag of the element
4111  * @cfg {String} cls class of the element
4112  * 
4113  * @constructor
4114  * Create a new Element
4115  * @param {Object} config The config object
4116  */
4117
4118 Roo.bootstrap.Element = function(config){
4119     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4120 };
4121
4122 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4123     
4124     tag: 'div',
4125     cls: '',
4126     html: '',
4127      
4128     
4129     getAutoCreate : function(){
4130         
4131         var cfg = {
4132             tag: this.tag,
4133             cls: this.cls,
4134             html: this.html
4135         }
4136         
4137         
4138         
4139         return cfg;
4140     }
4141    
4142 });
4143
4144  
4145
4146  /*
4147  * - LGPL
4148  *
4149  * pagination
4150  * 
4151  */
4152
4153 /**
4154  * @class Roo.bootstrap.Pagination
4155  * @extends Roo.bootstrap.Component
4156  * Bootstrap Pagination class
4157  * @cfg {String} size xs | sm | md | lg
4158  * @cfg {Boolean} inverse false | true
4159  * 
4160  * @constructor
4161  * Create a new Pagination
4162  * @param {Object} config The config object
4163  */
4164
4165 Roo.bootstrap.Pagination = function(config){
4166     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4167 };
4168
4169 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4170     
4171     cls: false,
4172     size: false,
4173     inverse: false,
4174     
4175     getAutoCreate : function(){
4176         var cfg = {
4177             tag: 'ul',
4178                 cls: 'pagination'
4179         };
4180         if (this.inverse) {
4181             cfg.cls += ' inverse';
4182         }
4183         if (this.html) {
4184             cfg.html=this.html;
4185         }
4186         if (this.cls) {
4187             cfg.cls += " " + this.cls;
4188         }
4189         return cfg;
4190     }
4191    
4192 });
4193
4194  
4195
4196  /*
4197  * - LGPL
4198  *
4199  * Pagination item
4200  * 
4201  */
4202
4203
4204 /**
4205  * @class Roo.bootstrap.PaginationItem
4206  * @extends Roo.bootstrap.Component
4207  * Bootstrap PaginationItem class
4208  * @cfg {String} html text
4209  * @cfg {String} href the link
4210  * @cfg {Boolean} preventDefault (true | false) default true
4211  * @cfg {Boolean} active (true | false) default false
4212  * @cfg {Boolean} disabled default false
4213  * 
4214  * 
4215  * @constructor
4216  * Create a new PaginationItem
4217  * @param {Object} config The config object
4218  */
4219
4220
4221 Roo.bootstrap.PaginationItem = function(config){
4222     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4223     this.addEvents({
4224         // raw events
4225         /**
4226          * @event click
4227          * The raw click event for the entire grid.
4228          * @param {Roo.EventObject} e
4229          */
4230         "click" : true
4231     });
4232 };
4233
4234 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4235     
4236     href : false,
4237     html : false,
4238     preventDefault: true,
4239     active : false,
4240     cls : false,
4241     disabled: false,
4242     
4243     getAutoCreate : function(){
4244         var cfg= {
4245             tag: 'li',
4246             cn: [
4247                 {
4248                     tag : 'a',
4249                     href : this.href ? this.href : '#',
4250                     html : this.html ? this.html : ''
4251                 }
4252             ]
4253         };
4254         
4255         if(this.cls){
4256             cfg.cls = this.cls;
4257         }
4258         
4259         if(this.disabled){
4260             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4261         }
4262         
4263         if(this.active){
4264             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4265         }
4266         
4267         return cfg;
4268     },
4269     
4270     initEvents: function() {
4271         
4272         this.el.on('click', this.onClick, this);
4273         
4274     },
4275     onClick : function(e)
4276     {
4277         Roo.log('PaginationItem on click ');
4278         if(this.preventDefault){
4279             e.preventDefault();
4280         }
4281         
4282         if(this.disabled){
4283             return;
4284         }
4285         
4286         this.fireEvent('click', this, e);
4287     }
4288    
4289 });
4290
4291  
4292
4293  /*
4294  * - LGPL
4295  *
4296  * slider
4297  * 
4298  */
4299
4300
4301 /**
4302  * @class Roo.bootstrap.Slider
4303  * @extends Roo.bootstrap.Component
4304  * Bootstrap Slider class
4305  *    
4306  * @constructor
4307  * Create a new Slider
4308  * @param {Object} config The config object
4309  */
4310
4311 Roo.bootstrap.Slider = function(config){
4312     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4313 };
4314
4315 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4316     
4317     getAutoCreate : function(){
4318         
4319         var cfg = {
4320             tag: 'div',
4321             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4322             cn: [
4323                 {
4324                     tag: 'a',
4325                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4326                 }
4327             ]
4328         }
4329         
4330         return cfg;
4331     }
4332    
4333 });
4334
4335  /*
4336  * Based on:
4337  * Ext JS Library 1.1.1
4338  * Copyright(c) 2006-2007, Ext JS, LLC.
4339  *
4340  * Originally Released Under LGPL - original licence link has changed is not relivant.
4341  *
4342  * Fork - LGPL
4343  * <script type="text/javascript">
4344  */
4345  
4346
4347 /**
4348  * @class Roo.grid.ColumnModel
4349  * @extends Roo.util.Observable
4350  * This is the default implementation of a ColumnModel used by the Grid. It defines
4351  * the columns in the grid.
4352  * <br>Usage:<br>
4353  <pre><code>
4354  var colModel = new Roo.grid.ColumnModel([
4355         {header: "Ticker", width: 60, sortable: true, locked: true},
4356         {header: "Company Name", width: 150, sortable: true},
4357         {header: "Market Cap.", width: 100, sortable: true},
4358         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4359         {header: "Employees", width: 100, sortable: true, resizable: false}
4360  ]);
4361  </code></pre>
4362  * <p>
4363  
4364  * The config options listed for this class are options which may appear in each
4365  * individual column definition.
4366  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4367  * @constructor
4368  * @param {Object} config An Array of column config objects. See this class's
4369  * config objects for details.
4370 */
4371 Roo.grid.ColumnModel = function(config){
4372         /**
4373      * The config passed into the constructor
4374      */
4375     this.config = config;
4376     this.lookup = {};
4377
4378     // if no id, create one
4379     // if the column does not have a dataIndex mapping,
4380     // map it to the order it is in the config
4381     for(var i = 0, len = config.length; i < len; i++){
4382         var c = config[i];
4383         if(typeof c.dataIndex == "undefined"){
4384             c.dataIndex = i;
4385         }
4386         if(typeof c.renderer == "string"){
4387             c.renderer = Roo.util.Format[c.renderer];
4388         }
4389         if(typeof c.id == "undefined"){
4390             c.id = Roo.id();
4391         }
4392         if(c.editor && c.editor.xtype){
4393             c.editor  = Roo.factory(c.editor, Roo.grid);
4394         }
4395         if(c.editor && c.editor.isFormField){
4396             c.editor = new Roo.grid.GridEditor(c.editor);
4397         }
4398         this.lookup[c.id] = c;
4399     }
4400
4401     /**
4402      * The width of columns which have no width specified (defaults to 100)
4403      * @type Number
4404      */
4405     this.defaultWidth = 100;
4406
4407     /**
4408      * Default sortable of columns which have no sortable specified (defaults to false)
4409      * @type Boolean
4410      */
4411     this.defaultSortable = false;
4412
4413     this.addEvents({
4414         /**
4415              * @event widthchange
4416              * Fires when the width of a column changes.
4417              * @param {ColumnModel} this
4418              * @param {Number} columnIndex The column index
4419              * @param {Number} newWidth The new width
4420              */
4421             "widthchange": true,
4422         /**
4423              * @event headerchange
4424              * Fires when the text of a header changes.
4425              * @param {ColumnModel} this
4426              * @param {Number} columnIndex The column index
4427              * @param {Number} newText The new header text
4428              */
4429             "headerchange": true,
4430         /**
4431              * @event hiddenchange
4432              * Fires when a column is hidden or "unhidden".
4433              * @param {ColumnModel} this
4434              * @param {Number} columnIndex The column index
4435              * @param {Boolean} hidden true if hidden, false otherwise
4436              */
4437             "hiddenchange": true,
4438             /**
4439          * @event columnmoved
4440          * Fires when a column is moved.
4441          * @param {ColumnModel} this
4442          * @param {Number} oldIndex
4443          * @param {Number} newIndex
4444          */
4445         "columnmoved" : true,
4446         /**
4447          * @event columlockchange
4448          * Fires when a column's locked state is changed
4449          * @param {ColumnModel} this
4450          * @param {Number} colIndex
4451          * @param {Boolean} locked true if locked
4452          */
4453         "columnlockchange" : true
4454     });
4455     Roo.grid.ColumnModel.superclass.constructor.call(this);
4456 };
4457 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4458     /**
4459      * @cfg {String} header The header text to display in the Grid view.
4460      */
4461     /**
4462      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4463      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4464      * specified, the column's index is used as an index into the Record's data Array.
4465      */
4466     /**
4467      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4468      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4469      */
4470     /**
4471      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4472      * Defaults to the value of the {@link #defaultSortable} property.
4473      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4474      */
4475     /**
4476      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4477      */
4478     /**
4479      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4480      */
4481     /**
4482      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4483      */
4484     /**
4485      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4486      */
4487     /**
4488      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4489      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4490      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4491      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4492      */
4493        /**
4494      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4495      */
4496     /**
4497      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4498      */
4499     /**
4500      * @cfg {String} cursor (Optional)
4501      */
4502     /**
4503      * @cfg {String} tooltip (Optional)
4504      */
4505     /**
4506      * Returns the id of the column at the specified index.
4507      * @param {Number} index The column index
4508      * @return {String} the id
4509      */
4510     getColumnId : function(index){
4511         return this.config[index].id;
4512     },
4513
4514     /**
4515      * Returns the column for a specified id.
4516      * @param {String} id The column id
4517      * @return {Object} the column
4518      */
4519     getColumnById : function(id){
4520         return this.lookup[id];
4521     },
4522
4523     
4524     /**
4525      * Returns the column for a specified dataIndex.
4526      * @param {String} dataIndex The column dataIndex
4527      * @return {Object|Boolean} the column or false if not found
4528      */
4529     getColumnByDataIndex: function(dataIndex){
4530         var index = this.findColumnIndex(dataIndex);
4531         return index > -1 ? this.config[index] : false;
4532     },
4533     
4534     /**
4535      * Returns the index for a specified column id.
4536      * @param {String} id The column id
4537      * @return {Number} the index, or -1 if not found
4538      */
4539     getIndexById : function(id){
4540         for(var i = 0, len = this.config.length; i < len; i++){
4541             if(this.config[i].id == id){
4542                 return i;
4543             }
4544         }
4545         return -1;
4546     },
4547     
4548     /**
4549      * Returns the index for a specified column dataIndex.
4550      * @param {String} dataIndex The column dataIndex
4551      * @return {Number} the index, or -1 if not found
4552      */
4553     
4554     findColumnIndex : function(dataIndex){
4555         for(var i = 0, len = this.config.length; i < len; i++){
4556             if(this.config[i].dataIndex == dataIndex){
4557                 return i;
4558             }
4559         }
4560         return -1;
4561     },
4562     
4563     
4564     moveColumn : function(oldIndex, newIndex){
4565         var c = this.config[oldIndex];
4566         this.config.splice(oldIndex, 1);
4567         this.config.splice(newIndex, 0, c);
4568         this.dataMap = null;
4569         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4570     },
4571
4572     isLocked : function(colIndex){
4573         return this.config[colIndex].locked === true;
4574     },
4575
4576     setLocked : function(colIndex, value, suppressEvent){
4577         if(this.isLocked(colIndex) == value){
4578             return;
4579         }
4580         this.config[colIndex].locked = value;
4581         if(!suppressEvent){
4582             this.fireEvent("columnlockchange", this, colIndex, value);
4583         }
4584     },
4585
4586     getTotalLockedWidth : function(){
4587         var totalWidth = 0;
4588         for(var i = 0; i < this.config.length; i++){
4589             if(this.isLocked(i) && !this.isHidden(i)){
4590                 this.totalWidth += this.getColumnWidth(i);
4591             }
4592         }
4593         return totalWidth;
4594     },
4595
4596     getLockedCount : function(){
4597         for(var i = 0, len = this.config.length; i < len; i++){
4598             if(!this.isLocked(i)){
4599                 return i;
4600             }
4601         }
4602     },
4603
4604     /**
4605      * Returns the number of columns.
4606      * @return {Number}
4607      */
4608     getColumnCount : function(visibleOnly){
4609         if(visibleOnly === true){
4610             var c = 0;
4611             for(var i = 0, len = this.config.length; i < len; i++){
4612                 if(!this.isHidden(i)){
4613                     c++;
4614                 }
4615             }
4616             return c;
4617         }
4618         return this.config.length;
4619     },
4620
4621     /**
4622      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4623      * @param {Function} fn
4624      * @param {Object} scope (optional)
4625      * @return {Array} result
4626      */
4627     getColumnsBy : function(fn, scope){
4628         var r = [];
4629         for(var i = 0, len = this.config.length; i < len; i++){
4630             var c = this.config[i];
4631             if(fn.call(scope||this, c, i) === true){
4632                 r[r.length] = c;
4633             }
4634         }
4635         return r;
4636     },
4637
4638     /**
4639      * Returns true if the specified column is sortable.
4640      * @param {Number} col The column index
4641      * @return {Boolean}
4642      */
4643     isSortable : function(col){
4644         if(typeof this.config[col].sortable == "undefined"){
4645             return this.defaultSortable;
4646         }
4647         return this.config[col].sortable;
4648     },
4649
4650     /**
4651      * Returns the rendering (formatting) function defined for the column.
4652      * @param {Number} col The column index.
4653      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4654      */
4655     getRenderer : function(col){
4656         if(!this.config[col].renderer){
4657             return Roo.grid.ColumnModel.defaultRenderer;
4658         }
4659         return this.config[col].renderer;
4660     },
4661
4662     /**
4663      * Sets the rendering (formatting) function for a column.
4664      * @param {Number} col The column index
4665      * @param {Function} fn The function to use to process the cell's raw data
4666      * to return HTML markup for the grid view. The render function is called with
4667      * the following parameters:<ul>
4668      * <li>Data value.</li>
4669      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4670      * <li>css A CSS style string to apply to the table cell.</li>
4671      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4672      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4673      * <li>Row index</li>
4674      * <li>Column index</li>
4675      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4676      */
4677     setRenderer : function(col, fn){
4678         this.config[col].renderer = fn;
4679     },
4680
4681     /**
4682      * Returns the width for the specified column.
4683      * @param {Number} col The column index
4684      * @return {Number}
4685      */
4686     getColumnWidth : function(col){
4687         return this.config[col].width * 1 || this.defaultWidth;
4688     },
4689
4690     /**
4691      * Sets the width for a column.
4692      * @param {Number} col The column index
4693      * @param {Number} width The new width
4694      */
4695     setColumnWidth : function(col, width, suppressEvent){
4696         this.config[col].width = width;
4697         this.totalWidth = null;
4698         if(!suppressEvent){
4699              this.fireEvent("widthchange", this, col, width);
4700         }
4701     },
4702
4703     /**
4704      * Returns the total width of all columns.
4705      * @param {Boolean} includeHidden True to include hidden column widths
4706      * @return {Number}
4707      */
4708     getTotalWidth : function(includeHidden){
4709         if(!this.totalWidth){
4710             this.totalWidth = 0;
4711             for(var i = 0, len = this.config.length; i < len; i++){
4712                 if(includeHidden || !this.isHidden(i)){
4713                     this.totalWidth += this.getColumnWidth(i);
4714                 }
4715             }
4716         }
4717         return this.totalWidth;
4718     },
4719
4720     /**
4721      * Returns the header for the specified column.
4722      * @param {Number} col The column index
4723      * @return {String}
4724      */
4725     getColumnHeader : function(col){
4726         return this.config[col].header;
4727     },
4728
4729     /**
4730      * Sets the header for a column.
4731      * @param {Number} col The column index
4732      * @param {String} header The new header
4733      */
4734     setColumnHeader : function(col, header){
4735         this.config[col].header = header;
4736         this.fireEvent("headerchange", this, col, header);
4737     },
4738
4739     /**
4740      * Returns the tooltip for the specified column.
4741      * @param {Number} col The column index
4742      * @return {String}
4743      */
4744     getColumnTooltip : function(col){
4745             return this.config[col].tooltip;
4746     },
4747     /**
4748      * Sets the tooltip for a column.
4749      * @param {Number} col The column index
4750      * @param {String} tooltip The new tooltip
4751      */
4752     setColumnTooltip : function(col, tooltip){
4753             this.config[col].tooltip = tooltip;
4754     },
4755
4756     /**
4757      * Returns the dataIndex for the specified column.
4758      * @param {Number} col The column index
4759      * @return {Number}
4760      */
4761     getDataIndex : function(col){
4762         return this.config[col].dataIndex;
4763     },
4764
4765     /**
4766      * Sets the dataIndex for a column.
4767      * @param {Number} col The column index
4768      * @param {Number} dataIndex The new dataIndex
4769      */
4770     setDataIndex : function(col, dataIndex){
4771         this.config[col].dataIndex = dataIndex;
4772     },
4773
4774     
4775     
4776     /**
4777      * Returns true if the cell is editable.
4778      * @param {Number} colIndex The column index
4779      * @param {Number} rowIndex The row index
4780      * @return {Boolean}
4781      */
4782     isCellEditable : function(colIndex, rowIndex){
4783         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4784     },
4785
4786     /**
4787      * Returns the editor defined for the cell/column.
4788      * return false or null to disable editing.
4789      * @param {Number} colIndex The column index
4790      * @param {Number} rowIndex The row index
4791      * @return {Object}
4792      */
4793     getCellEditor : function(colIndex, rowIndex){
4794         return this.config[colIndex].editor;
4795     },
4796
4797     /**
4798      * Sets if a column is editable.
4799      * @param {Number} col The column index
4800      * @param {Boolean} editable True if the column is editable
4801      */
4802     setEditable : function(col, editable){
4803         this.config[col].editable = editable;
4804     },
4805
4806
4807     /**
4808      * Returns true if the column is hidden.
4809      * @param {Number} colIndex The column index
4810      * @return {Boolean}
4811      */
4812     isHidden : function(colIndex){
4813         return this.config[colIndex].hidden;
4814     },
4815
4816
4817     /**
4818      * Returns true if the column width cannot be changed
4819      */
4820     isFixed : function(colIndex){
4821         return this.config[colIndex].fixed;
4822     },
4823
4824     /**
4825      * Returns true if the column can be resized
4826      * @return {Boolean}
4827      */
4828     isResizable : function(colIndex){
4829         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4830     },
4831     /**
4832      * Sets if a column is hidden.
4833      * @param {Number} colIndex The column index
4834      * @param {Boolean} hidden True if the column is hidden
4835      */
4836     setHidden : function(colIndex, hidden){
4837         this.config[colIndex].hidden = hidden;
4838         this.totalWidth = null;
4839         this.fireEvent("hiddenchange", this, colIndex, hidden);
4840     },
4841
4842     /**
4843      * Sets the editor for a column.
4844      * @param {Number} col The column index
4845      * @param {Object} editor The editor object
4846      */
4847     setEditor : function(col, editor){
4848         this.config[col].editor = editor;
4849     }
4850 });
4851
4852 Roo.grid.ColumnModel.defaultRenderer = function(value){
4853         if(typeof value == "string" && value.length < 1){
4854             return "&#160;";
4855         }
4856         return value;
4857 };
4858
4859 // Alias for backwards compatibility
4860 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4861 /*
4862  * Based on:
4863  * Ext JS Library 1.1.1
4864  * Copyright(c) 2006-2007, Ext JS, LLC.
4865  *
4866  * Originally Released Under LGPL - original licence link has changed is not relivant.
4867  *
4868  * Fork - LGPL
4869  * <script type="text/javascript">
4870  */
4871  
4872 /**
4873  * @class Roo.LoadMask
4874  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4875  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4876  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4877  * element's UpdateManager load indicator and will be destroyed after the initial load.
4878  * @constructor
4879  * Create a new LoadMask
4880  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4881  * @param {Object} config The config object
4882  */
4883 Roo.LoadMask = function(el, config){
4884     this.el = Roo.get(el);
4885     Roo.apply(this, config);
4886     if(this.store){
4887         this.store.on('beforeload', this.onBeforeLoad, this);
4888         this.store.on('load', this.onLoad, this);
4889         this.store.on('loadexception', this.onLoadException, this);
4890         this.removeMask = false;
4891     }else{
4892         var um = this.el.getUpdateManager();
4893         um.showLoadIndicator = false; // disable the default indicator
4894         um.on('beforeupdate', this.onBeforeLoad, this);
4895         um.on('update', this.onLoad, this);
4896         um.on('failure', this.onLoad, this);
4897         this.removeMask = true;
4898     }
4899 };
4900
4901 Roo.LoadMask.prototype = {
4902     /**
4903      * @cfg {Boolean} removeMask
4904      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4905      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4906      */
4907     /**
4908      * @cfg {String} msg
4909      * The text to display in a centered loading message box (defaults to 'Loading...')
4910      */
4911     msg : 'Loading...',
4912     /**
4913      * @cfg {String} msgCls
4914      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4915      */
4916     msgCls : 'x-mask-loading',
4917
4918     /**
4919      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4920      * @type Boolean
4921      */
4922     disabled: false,
4923
4924     /**
4925      * Disables the mask to prevent it from being displayed
4926      */
4927     disable : function(){
4928        this.disabled = true;
4929     },
4930
4931     /**
4932      * Enables the mask so that it can be displayed
4933      */
4934     enable : function(){
4935         this.disabled = false;
4936     },
4937     
4938     onLoadException : function()
4939     {
4940         Roo.log(arguments);
4941         
4942         if (typeof(arguments[3]) != 'undefined') {
4943             Roo.MessageBox.alert("Error loading",arguments[3]);
4944         } 
4945         /*
4946         try {
4947             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4948                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4949             }   
4950         } catch(e) {
4951             
4952         }
4953         */
4954     
4955         
4956         
4957         this.el.unmask(this.removeMask);
4958     },
4959     // private
4960     onLoad : function()
4961     {
4962         this.el.unmask(this.removeMask);
4963     },
4964
4965     // private
4966     onBeforeLoad : function(){
4967         if(!this.disabled){
4968             this.el.mask(this.msg, this.msgCls);
4969         }
4970     },
4971
4972     // private
4973     destroy : function(){
4974         if(this.store){
4975             this.store.un('beforeload', this.onBeforeLoad, this);
4976             this.store.un('load', this.onLoad, this);
4977             this.store.un('loadexception', this.onLoadException, this);
4978         }else{
4979             var um = this.el.getUpdateManager();
4980             um.un('beforeupdate', this.onBeforeLoad, this);
4981             um.un('update', this.onLoad, this);
4982             um.un('failure', this.onLoad, this);
4983         }
4984     }
4985 };/*
4986  * - LGPL
4987  *
4988  * table
4989  * 
4990  */
4991
4992 /**
4993  * @class Roo.bootstrap.Table
4994  * @extends Roo.bootstrap.Component
4995  * Bootstrap Table class
4996  * @cfg {String} cls table class
4997  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4998  * @cfg {String} bgcolor Specifies the background color for a table
4999  * @cfg {Number} border Specifies whether the table cells should have borders or not
5000  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5001  * @cfg {Number} cellspacing Specifies the space between cells
5002  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5003  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5004  * @cfg {String} sortable Specifies that the table should be sortable
5005  * @cfg {String} summary Specifies a summary of the content of a table
5006  * @cfg {Number} width Specifies the width of a table
5007  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5008  * 
5009  * @cfg {boolean} striped Should the rows be alternative striped
5010  * @cfg {boolean} bordered Add borders to the table
5011  * @cfg {boolean} hover Add hover highlighting
5012  * @cfg {boolean} condensed Format condensed
5013  * @cfg {boolean} responsive Format condensed
5014  * @cfg {Boolean} loadMask (true|false) default false
5015  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5016  * @cfg {Boolean} thead (true|false) generate thead, default true
5017  * @cfg {Boolean} RowSelection (true|false) default false
5018  * @cfg {Boolean} CellSelection (true|false) default false
5019  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5020  
5021  * 
5022  * @constructor
5023  * Create a new Table
5024  * @param {Object} config The config object
5025  */
5026
5027 Roo.bootstrap.Table = function(config){
5028     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5029     
5030     if (this.sm) {
5031         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5032         this.sm = this.selModel;
5033         this.sm.xmodule = this.xmodule || false;
5034     }
5035     if (this.cm && typeof(this.cm.config) == 'undefined') {
5036         this.colModel = new Roo.grid.ColumnModel(this.cm);
5037         this.cm = this.colModel;
5038         this.cm.xmodule = this.xmodule || false;
5039     }
5040     if (this.store) {
5041         this.store= Roo.factory(this.store, Roo.data);
5042         this.ds = this.store;
5043         this.ds.xmodule = this.xmodule || false;
5044          
5045     }
5046     if (this.footer && this.store) {
5047         this.footer.dataSource = this.ds;
5048         this.footer = Roo.factory(this.footer);
5049     }
5050     
5051     /** @private */
5052     this.addEvents({
5053         /**
5054          * @event cellclick
5055          * Fires when a cell is clicked
5056          * @param {Roo.bootstrap.Table} this
5057          * @param {Roo.Element} el
5058          * @param {Number} rowIndex
5059          * @param {Number} columnIndex
5060          * @param {Roo.EventObject} e
5061          */
5062         "cellclick" : true,
5063         /**
5064          * @event celldblclick
5065          * Fires when a cell is double clicked
5066          * @param {Roo.bootstrap.Table} this
5067          * @param {Roo.Element} el
5068          * @param {Number} rowIndex
5069          * @param {Number} columnIndex
5070          * @param {Roo.EventObject} e
5071          */
5072         "celldblclick" : true,
5073         /**
5074          * @event rowclick
5075          * Fires when a row is clicked
5076          * @param {Roo.bootstrap.Table} this
5077          * @param {Roo.Element} el
5078          * @param {Number} rowIndex
5079          * @param {Roo.EventObject} e
5080          */
5081         "rowclick" : true,
5082         /**
5083          * @event rowdblclick
5084          * Fires when a row is double clicked
5085          * @param {Roo.bootstrap.Table} this
5086          * @param {Roo.Element} el
5087          * @param {Number} rowIndex
5088          * @param {Roo.EventObject} e
5089          */
5090         "rowdblclick" : true,
5091         /**
5092          * @event mouseover
5093          * Fires when a mouseover occur
5094          * @param {Roo.bootstrap.Table} this
5095          * @param {Roo.Element} el
5096          * @param {Number} rowIndex
5097          * @param {Number} columnIndex
5098          * @param {Roo.EventObject} e
5099          */
5100         "mouseover" : true,
5101         /**
5102          * @event mouseout
5103          * Fires when a mouseout occur
5104          * @param {Roo.bootstrap.Table} this
5105          * @param {Roo.Element} el
5106          * @param {Number} rowIndex
5107          * @param {Number} columnIndex
5108          * @param {Roo.EventObject} e
5109          */
5110         "mouseout" : true,
5111         /**
5112          * @event rowclass
5113          * Fires when a row is rendered, so you can change add a style to it.
5114          * @param {Roo.bootstrap.Table} this
5115          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5116          */
5117         'rowclass' : true,
5118           /**
5119          * @event rowsrendered
5120          * Fires when all the  rows have been rendered
5121          * @param {Roo.bootstrap.Table} this
5122          */
5123         'rowsrendered' : true
5124         
5125     });
5126 };
5127
5128 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5129     
5130     cls: false,
5131     align: false,
5132     bgcolor: false,
5133     border: false,
5134     cellpadding: false,
5135     cellspacing: false,
5136     frame: false,
5137     rules: false,
5138     sortable: false,
5139     summary: false,
5140     width: false,
5141     striped : false,
5142     bordered: false,
5143     hover:  false,
5144     condensed : false,
5145     responsive : false,
5146     sm : false,
5147     cm : false,
5148     store : false,
5149     loadMask : false,
5150     tfoot : true,
5151     thead : true,
5152     RowSelection : false,
5153     CellSelection : false,
5154     layout : false,
5155     
5156     // Roo.Element - the tbody
5157     mainBody: false, 
5158     
5159     getAutoCreate : function(){
5160         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5161         
5162         cfg = {
5163             tag: 'table',
5164             cls : 'table',
5165             cn : []
5166         }
5167             
5168         if (this.striped) {
5169             cfg.cls += ' table-striped';
5170         }
5171         
5172         if (this.hover) {
5173             cfg.cls += ' table-hover';
5174         }
5175         if (this.bordered) {
5176             cfg.cls += ' table-bordered';
5177         }
5178         if (this.condensed) {
5179             cfg.cls += ' table-condensed';
5180         }
5181         if (this.responsive) {
5182             cfg.cls += ' table-responsive';
5183         }
5184         
5185         if (this.cls) {
5186             cfg.cls+=  ' ' +this.cls;
5187         }
5188         
5189         // this lot should be simplifed...
5190         
5191         if (this.align) {
5192             cfg.align=this.align;
5193         }
5194         if (this.bgcolor) {
5195             cfg.bgcolor=this.bgcolor;
5196         }
5197         if (this.border) {
5198             cfg.border=this.border;
5199         }
5200         if (this.cellpadding) {
5201             cfg.cellpadding=this.cellpadding;
5202         }
5203         if (this.cellspacing) {
5204             cfg.cellspacing=this.cellspacing;
5205         }
5206         if (this.frame) {
5207             cfg.frame=this.frame;
5208         }
5209         if (this.rules) {
5210             cfg.rules=this.rules;
5211         }
5212         if (this.sortable) {
5213             cfg.sortable=this.sortable;
5214         }
5215         if (this.summary) {
5216             cfg.summary=this.summary;
5217         }
5218         if (this.width) {
5219             cfg.width=this.width;
5220         }
5221         if (this.layout) {
5222             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5223         }
5224         
5225         if(this.store || this.cm){
5226             if(this.thead){
5227                 cfg.cn.push(this.renderHeader());
5228             }
5229             
5230             cfg.cn.push(this.renderBody());
5231             
5232             if(this.tfoot){
5233                 cfg.cn.push(this.renderFooter());
5234             }
5235             
5236             cfg.cls+=  ' TableGrid';
5237         }
5238         
5239         return { cn : [ cfg ] };
5240     },
5241     
5242     initEvents : function()
5243     {   
5244         if(!this.store || !this.cm){
5245             return;
5246         }
5247         
5248         //Roo.log('initEvents with ds!!!!');
5249         
5250         this.mainBody = this.el.select('tbody', true).first();
5251         
5252         
5253         var _this = this;
5254         
5255         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5256             e.on('click', _this.sort, _this);
5257         });
5258         
5259         this.el.on("click", this.onClick, this);
5260         this.el.on("dblclick", this.onDblClick, this);
5261         
5262         // why is this done????? = it breaks dialogs??
5263         //this.parent().el.setStyle('position', 'relative');
5264         
5265         
5266         if (this.footer) {
5267             this.footer.parentId = this.id;
5268             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5269         }
5270         
5271         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5272         
5273         this.store.on('load', this.onLoad, this);
5274         this.store.on('beforeload', this.onBeforeLoad, this);
5275         this.store.on('update', this.onUpdate, this);
5276         this.store.on('add', this.onAdd, this);
5277         
5278     },
5279     
5280     onMouseover : function(e, el)
5281     {
5282         var cell = Roo.get(el);
5283         
5284         if(!cell){
5285             return;
5286         }
5287         
5288         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5289             cell = cell.findParent('td', false, true);
5290         }
5291         
5292         var row = cell.findParent('tr', false, true);
5293         var cellIndex = cell.dom.cellIndex;
5294         var rowIndex = row.dom.rowIndex - 1; // start from 0
5295         
5296         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5297         
5298     },
5299     
5300     onMouseout : function(e, el)
5301     {
5302         var cell = Roo.get(el);
5303         
5304         if(!cell){
5305             return;
5306         }
5307         
5308         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5309             cell = cell.findParent('td', false, true);
5310         }
5311         
5312         var row = cell.findParent('tr', false, true);
5313         var cellIndex = cell.dom.cellIndex;
5314         var rowIndex = row.dom.rowIndex - 1; // start from 0
5315         
5316         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5317         
5318     },
5319     
5320     onClick : function(e, el)
5321     {
5322         var cell = Roo.get(el);
5323         
5324         if(!cell || (!this.CellSelection && !this.RowSelection)){
5325             return;
5326         }
5327         
5328         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5329             cell = cell.findParent('td', false, true);
5330         }
5331         
5332         if(!cell || typeof(cell) == 'undefined'){
5333             return;
5334         }
5335         
5336         var row = cell.findParent('tr', false, true);
5337         
5338         if(!row || typeof(row) == 'undefined'){
5339             return;
5340         }
5341         
5342         var cellIndex = cell.dom.cellIndex;
5343         var rowIndex = this.getRowIndex(row);
5344         
5345         if(this.CellSelection){
5346             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5347         }
5348         
5349         if(this.RowSelection){
5350             this.fireEvent('rowclick', this, row, rowIndex, e);
5351         }
5352         
5353         
5354     },
5355     
5356     onDblClick : function(e,el)
5357     {
5358         var cell = Roo.get(el);
5359         
5360         if(!cell || (!this.CellSelection && !this.RowSelection)){
5361             return;
5362         }
5363         
5364         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5365             cell = cell.findParent('td', false, true);
5366         }
5367         
5368         if(!cell || typeof(cell) == 'undefined'){
5369             return;
5370         }
5371         
5372         var row = cell.findParent('tr', false, true);
5373         
5374         if(!row || typeof(row) == 'undefined'){
5375             return;
5376         }
5377         
5378         var cellIndex = cell.dom.cellIndex;
5379         var rowIndex = this.getRowIndex(row);
5380         
5381         if(this.CellSelection){
5382             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5383         }
5384         
5385         if(this.RowSelection){
5386             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5387         }
5388     },
5389     
5390     sort : function(e,el)
5391     {
5392         var col = Roo.get(el);
5393         
5394         if(!col.hasClass('sortable')){
5395             return;
5396         }
5397         
5398         var sort = col.attr('sort');
5399         var dir = 'ASC';
5400         
5401         if(col.hasClass('glyphicon-arrow-up')){
5402             dir = 'DESC';
5403         }
5404         
5405         this.store.sortInfo = {field : sort, direction : dir};
5406         
5407         if (this.footer) {
5408             Roo.log("calling footer first");
5409             this.footer.onClick('first');
5410         } else {
5411         
5412             this.store.load({ params : { start : 0 } });
5413         }
5414     },
5415     
5416     renderHeader : function()
5417     {
5418         var header = {
5419             tag: 'thead',
5420             cn : []
5421         };
5422         
5423         var cm = this.cm;
5424         
5425         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5426             
5427             var config = cm.config[i];
5428                     
5429             var c = {
5430                 tag: 'th',
5431                 style : '',
5432                 html: cm.getColumnHeader(i)
5433             };
5434             
5435             if(typeof(config.tooltip) != 'undefined'){
5436                 c.tooltip = config.tooltip;
5437             }
5438             
5439             if(typeof(config.hidden) != 'undefined' && config.hidden){
5440                 c.style += ' display:none;';
5441             }
5442             
5443             if(typeof(config.dataIndex) != 'undefined'){
5444                 c.sort = config.dataIndex;
5445             }
5446             
5447             if(typeof(config.sortable) != 'undefined' && config.sortable){
5448                 c.cls = 'sortable';
5449             }
5450             
5451             if(typeof(config.align) != 'undefined' && config.align.length){
5452                 c.style += ' text-align:' + config.align + ';';
5453             }
5454             
5455             if(typeof(config.width) != 'undefined'){
5456                 c.style += ' width:' + config.width + 'px;';
5457             }
5458             
5459             header.cn.push(c)
5460         }
5461         
5462         return header;
5463     },
5464     
5465     renderBody : function()
5466     {
5467         var body = {
5468             tag: 'tbody',
5469             cn : [
5470                 {
5471                     tag: 'tr',
5472                     cn : [
5473                         {
5474                             tag : 'td',
5475                             colspan :  this.cm.getColumnCount()
5476                         }
5477                     ]
5478                 }
5479             ]
5480         };
5481         
5482         return body;
5483     },
5484     
5485     renderFooter : function()
5486     {
5487         var footer = {
5488             tag: 'tfoot',
5489             cn : [
5490                 {
5491                     tag: 'tr',
5492                     cn : [
5493                         {
5494                             tag : 'td',
5495                             colspan :  this.cm.getColumnCount()
5496                         }
5497                     ]
5498                 }
5499             ]
5500         };
5501         
5502         return footer;
5503     },
5504     
5505     
5506     
5507     onLoad : function()
5508     {
5509         Roo.log('ds onload');
5510         this.clear();
5511         
5512         var _this = this;
5513         var cm = this.cm;
5514         var ds = this.store;
5515         
5516         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5517             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5518             
5519             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5520                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5521             }
5522             
5523             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5524                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5525             }
5526         });
5527         
5528         var tbody =  this.mainBody;
5529               
5530         if(ds.getCount() > 0){
5531             ds.data.each(function(d,rowIndex){
5532                 var row =  this.renderRow(cm, ds, rowIndex);
5533                 
5534                 tbody.createChild(row);
5535                 
5536                 var _this = this;
5537                 
5538                 if(row.cellObjects.length){
5539                     Roo.each(row.cellObjects, function(r){
5540                         _this.renderCellObject(r);
5541                     })
5542                 }
5543                 
5544             }, this);
5545         }
5546         
5547         Roo.each(this.el.select('tbody td', true).elements, function(e){
5548             e.on('mouseover', _this.onMouseover, _this);
5549         });
5550         
5551         Roo.each(this.el.select('tbody td', true).elements, function(e){
5552             e.on('mouseout', _this.onMouseout, _this);
5553         });
5554         this.fireEvent('rowsrendered', this);
5555         //if(this.loadMask){
5556         //    this.maskEl.hide();
5557         //}
5558     },
5559     
5560     
5561     onUpdate : function(ds,record)
5562     {
5563         this.refreshRow(record);
5564     },
5565     
5566     onRemove : function(ds, record, index, isUpdate){
5567         if(isUpdate !== true){
5568             this.fireEvent("beforerowremoved", this, index, record);
5569         }
5570         var bt = this.mainBody.dom;
5571         
5572         var rows = this.el.select('tbody > tr', true).elements;
5573         
5574         if(typeof(rows[index]) != 'undefined'){
5575             bt.removeChild(rows[index].dom);
5576         }
5577         
5578 //        if(bt.rows[index]){
5579 //            bt.removeChild(bt.rows[index]);
5580 //        }
5581         
5582         if(isUpdate !== true){
5583             //this.stripeRows(index);
5584             //this.syncRowHeights(index, index);
5585             //this.layout();
5586             this.fireEvent("rowremoved", this, index, record);
5587         }
5588     },
5589     
5590     onAdd : function(ds, records, rowIndex)
5591     {
5592         //Roo.log('on Add called');
5593         // - note this does not handle multiple adding very well..
5594         var bt = this.mainBody.dom;
5595         for (var i =0 ; i < records.length;i++) {
5596             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5597             //Roo.log(records[i]);
5598             //Roo.log(this.store.getAt(rowIndex+i));
5599             this.insertRow(this.store, rowIndex + i, false);
5600             return;
5601         }
5602         
5603     },
5604     
5605     
5606     refreshRow : function(record){
5607         var ds = this.store, index;
5608         if(typeof record == 'number'){
5609             index = record;
5610             record = ds.getAt(index);
5611         }else{
5612             index = ds.indexOf(record);
5613         }
5614         this.insertRow(ds, index, true);
5615         this.onRemove(ds, record, index+1, true);
5616         //this.syncRowHeights(index, index);
5617         //this.layout();
5618         this.fireEvent("rowupdated", this, index, record);
5619     },
5620     
5621     insertRow : function(dm, rowIndex, isUpdate){
5622         
5623         if(!isUpdate){
5624             this.fireEvent("beforerowsinserted", this, rowIndex);
5625         }
5626             //var s = this.getScrollState();
5627         var row = this.renderRow(this.cm, this.store, rowIndex);
5628         // insert before rowIndex..
5629         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5630         
5631         var _this = this;
5632                 
5633         if(row.cellObjects.length){
5634             Roo.each(row.cellObjects, function(r){
5635                 _this.renderCellObject(r);
5636             })
5637         }
5638             
5639         if(!isUpdate){
5640             this.fireEvent("rowsinserted", this, rowIndex);
5641             //this.syncRowHeights(firstRow, lastRow);
5642             //this.stripeRows(firstRow);
5643             //this.layout();
5644         }
5645         
5646     },
5647     
5648     
5649     getRowDom : function(rowIndex)
5650     {
5651         var rows = this.el.select('tbody > tr', true).elements;
5652         
5653         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5654         
5655     },
5656     // returns the object tree for a tr..
5657   
5658     
5659     renderRow : function(cm, ds, rowIndex) 
5660     {
5661         
5662         var d = ds.getAt(rowIndex);
5663         
5664         var row = {
5665             tag : 'tr',
5666             cn : []
5667         };
5668             
5669         var cellObjects = [];
5670         
5671         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5672             var config = cm.config[i];
5673             
5674             var renderer = cm.getRenderer(i);
5675             var value = '';
5676             var id = false;
5677             
5678             if(typeof(renderer) !== 'undefined'){
5679                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5680             }
5681             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5682             // and are rendered into the cells after the row is rendered - using the id for the element.
5683             
5684             if(typeof(value) === 'object'){
5685                 id = Roo.id();
5686                 cellObjects.push({
5687                     container : id,
5688                     cfg : value 
5689                 })
5690             }
5691             
5692             var rowcfg = {
5693                 record: d,
5694                 rowIndex : rowIndex,
5695                 colIndex : i,
5696                 rowClass : ''
5697             }
5698
5699             this.fireEvent('rowclass', this, rowcfg);
5700             
5701             var td = {
5702                 tag: 'td',
5703                 cls : rowcfg.rowClass,
5704                 style: '',
5705                 html: (typeof(value) === 'object') ? '' : value
5706             };
5707             
5708             if (id) {
5709                 td.id = id;
5710             }
5711             
5712             if(typeof(config.hidden) != 'undefined' && config.hidden){
5713                 td.style += ' display:none;';
5714             }
5715             
5716             if(typeof(config.align) != 'undefined' && config.align.length){
5717                 td.style += ' text-align:' + config.align + ';';
5718             }
5719             
5720             if(typeof(config.width) != 'undefined'){
5721                 td.style += ' width:' +  config.width + 'px;';
5722             }
5723             
5724             if(typeof(config.cursor) != 'undefined'){
5725                 td.style += ' cursor:' +  config.cursor + ';';
5726             }
5727              
5728             row.cn.push(td);
5729            
5730         }
5731         
5732         row.cellObjects = cellObjects;
5733         
5734         return row;
5735           
5736     },
5737     
5738     
5739     
5740     onBeforeLoad : function()
5741     {
5742         //Roo.log('ds onBeforeLoad');
5743         
5744         //this.clear();
5745         
5746         //if(this.loadMask){
5747         //    this.maskEl.show();
5748         //}
5749     },
5750      /**
5751      * Remove all rows
5752      */
5753     clear : function()
5754     {
5755         this.el.select('tbody', true).first().dom.innerHTML = '';
5756     },
5757     /**
5758      * Show or hide a row.
5759      * @param {Number} rowIndex to show or hide
5760      * @param {Boolean} state hide
5761      */
5762     setRowVisibility : function(rowIndex, state)
5763     {
5764         var bt = this.mainBody.dom;
5765         
5766         var rows = this.el.select('tbody > tr', true).elements;
5767         
5768         if(typeof(rows[rowIndex]) == 'undefined'){
5769             return;
5770         }
5771         rows[rowIndex].dom.style.display = state ? '' : 'none';
5772     },
5773     
5774     
5775     getSelectionModel : function(){
5776         if(!this.selModel){
5777             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5778         }
5779         return this.selModel;
5780     },
5781     /*
5782      * Render the Roo.bootstrap object from renderder
5783      */
5784     renderCellObject : function(r)
5785     {
5786         var _this = this;
5787         
5788         var t = r.cfg.render(r.container);
5789         
5790         if(r.cfg.cn){
5791             Roo.each(r.cfg.cn, function(c){
5792                 var child = {
5793                     container: t.getChildContainer(),
5794                     cfg: c
5795                 }
5796                 _this.renderCellObject(child);
5797             })
5798         }
5799     },
5800     
5801     getRowIndex : function(row)
5802     {
5803         var rowIndex = -1;
5804         
5805         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
5806             if(el != row){
5807                 return;
5808             }
5809             
5810             rowIndex = index;
5811         });
5812         
5813         return rowIndex;
5814     }
5815    
5816 });
5817
5818  
5819
5820  /*
5821  * - LGPL
5822  *
5823  * table cell
5824  * 
5825  */
5826
5827 /**
5828  * @class Roo.bootstrap.TableCell
5829  * @extends Roo.bootstrap.Component
5830  * Bootstrap TableCell class
5831  * @cfg {String} html cell contain text
5832  * @cfg {String} cls cell class
5833  * @cfg {String} tag cell tag (td|th) default td
5834  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5835  * @cfg {String} align Aligns the content in a cell
5836  * @cfg {String} axis Categorizes cells
5837  * @cfg {String} bgcolor Specifies the background color of a cell
5838  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5839  * @cfg {Number} colspan Specifies the number of columns a cell should span
5840  * @cfg {String} headers Specifies one or more header cells a cell is related to
5841  * @cfg {Number} height Sets the height of a cell
5842  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5843  * @cfg {Number} rowspan Sets the number of rows a cell should span
5844  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5845  * @cfg {String} valign Vertical aligns the content in a cell
5846  * @cfg {Number} width Specifies the width of a cell
5847  * 
5848  * @constructor
5849  * Create a new TableCell
5850  * @param {Object} config The config object
5851  */
5852
5853 Roo.bootstrap.TableCell = function(config){
5854     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5855 };
5856
5857 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5858     
5859     html: false,
5860     cls: false,
5861     tag: false,
5862     abbr: false,
5863     align: false,
5864     axis: false,
5865     bgcolor: false,
5866     charoff: false,
5867     colspan: false,
5868     headers: false,
5869     height: false,
5870     nowrap: false,
5871     rowspan: false,
5872     scope: false,
5873     valign: false,
5874     width: false,
5875     
5876     
5877     getAutoCreate : function(){
5878         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5879         
5880         cfg = {
5881             tag: 'td'
5882         }
5883         
5884         if(this.tag){
5885             cfg.tag = this.tag;
5886         }
5887         
5888         if (this.html) {
5889             cfg.html=this.html
5890         }
5891         if (this.cls) {
5892             cfg.cls=this.cls
5893         }
5894         if (this.abbr) {
5895             cfg.abbr=this.abbr
5896         }
5897         if (this.align) {
5898             cfg.align=this.align
5899         }
5900         if (this.axis) {
5901             cfg.axis=this.axis
5902         }
5903         if (this.bgcolor) {
5904             cfg.bgcolor=this.bgcolor
5905         }
5906         if (this.charoff) {
5907             cfg.charoff=this.charoff
5908         }
5909         if (this.colspan) {
5910             cfg.colspan=this.colspan
5911         }
5912         if (this.headers) {
5913             cfg.headers=this.headers
5914         }
5915         if (this.height) {
5916             cfg.height=this.height
5917         }
5918         if (this.nowrap) {
5919             cfg.nowrap=this.nowrap
5920         }
5921         if (this.rowspan) {
5922             cfg.rowspan=this.rowspan
5923         }
5924         if (this.scope) {
5925             cfg.scope=this.scope
5926         }
5927         if (this.valign) {
5928             cfg.valign=this.valign
5929         }
5930         if (this.width) {
5931             cfg.width=this.width
5932         }
5933         
5934         
5935         return cfg;
5936     }
5937    
5938 });
5939
5940  
5941
5942  /*
5943  * - LGPL
5944  *
5945  * table row
5946  * 
5947  */
5948
5949 /**
5950  * @class Roo.bootstrap.TableRow
5951  * @extends Roo.bootstrap.Component
5952  * Bootstrap TableRow class
5953  * @cfg {String} cls row class
5954  * @cfg {String} align Aligns the content in a table row
5955  * @cfg {String} bgcolor Specifies a background color for a table row
5956  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5957  * @cfg {String} valign Vertical aligns the content in a table row
5958  * 
5959  * @constructor
5960  * Create a new TableRow
5961  * @param {Object} config The config object
5962  */
5963
5964 Roo.bootstrap.TableRow = function(config){
5965     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5966 };
5967
5968 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5969     
5970     cls: false,
5971     align: false,
5972     bgcolor: false,
5973     charoff: false,
5974     valign: false,
5975     
5976     getAutoCreate : function(){
5977         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5978         
5979         cfg = {
5980             tag: 'tr'
5981         }
5982             
5983         if(this.cls){
5984             cfg.cls = this.cls;
5985         }
5986         if(this.align){
5987             cfg.align = this.align;
5988         }
5989         if(this.bgcolor){
5990             cfg.bgcolor = this.bgcolor;
5991         }
5992         if(this.charoff){
5993             cfg.charoff = this.charoff;
5994         }
5995         if(this.valign){
5996             cfg.valign = this.valign;
5997         }
5998         
5999         return cfg;
6000     }
6001    
6002 });
6003
6004  
6005
6006  /*
6007  * - LGPL
6008  *
6009  * table body
6010  * 
6011  */
6012
6013 /**
6014  * @class Roo.bootstrap.TableBody
6015  * @extends Roo.bootstrap.Component
6016  * Bootstrap TableBody class
6017  * @cfg {String} cls element class
6018  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6019  * @cfg {String} align Aligns the content inside the element
6020  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6021  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6022  * 
6023  * @constructor
6024  * Create a new TableBody
6025  * @param {Object} config The config object
6026  */
6027
6028 Roo.bootstrap.TableBody = function(config){
6029     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6030 };
6031
6032 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6033     
6034     cls: false,
6035     tag: false,
6036     align: false,
6037     charoff: false,
6038     valign: false,
6039     
6040     getAutoCreate : function(){
6041         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6042         
6043         cfg = {
6044             tag: 'tbody'
6045         }
6046             
6047         if (this.cls) {
6048             cfg.cls=this.cls
6049         }
6050         if(this.tag){
6051             cfg.tag = this.tag;
6052         }
6053         
6054         if(this.align){
6055             cfg.align = this.align;
6056         }
6057         if(this.charoff){
6058             cfg.charoff = this.charoff;
6059         }
6060         if(this.valign){
6061             cfg.valign = this.valign;
6062         }
6063         
6064         return cfg;
6065     }
6066     
6067     
6068 //    initEvents : function()
6069 //    {
6070 //        
6071 //        if(!this.store){
6072 //            return;
6073 //        }
6074 //        
6075 //        this.store = Roo.factory(this.store, Roo.data);
6076 //        this.store.on('load', this.onLoad, this);
6077 //        
6078 //        this.store.load();
6079 //        
6080 //    },
6081 //    
6082 //    onLoad: function () 
6083 //    {   
6084 //        this.fireEvent('load', this);
6085 //    }
6086 //    
6087 //   
6088 });
6089
6090  
6091
6092  /*
6093  * Based on:
6094  * Ext JS Library 1.1.1
6095  * Copyright(c) 2006-2007, Ext JS, LLC.
6096  *
6097  * Originally Released Under LGPL - original licence link has changed is not relivant.
6098  *
6099  * Fork - LGPL
6100  * <script type="text/javascript">
6101  */
6102
6103 // as we use this in bootstrap.
6104 Roo.namespace('Roo.form');
6105  /**
6106  * @class Roo.form.Action
6107  * Internal Class used to handle form actions
6108  * @constructor
6109  * @param {Roo.form.BasicForm} el The form element or its id
6110  * @param {Object} config Configuration options
6111  */
6112
6113  
6114  
6115 // define the action interface
6116 Roo.form.Action = function(form, options){
6117     this.form = form;
6118     this.options = options || {};
6119 };
6120 /**
6121  * Client Validation Failed
6122  * @const 
6123  */
6124 Roo.form.Action.CLIENT_INVALID = 'client';
6125 /**
6126  * Server Validation Failed
6127  * @const 
6128  */
6129 Roo.form.Action.SERVER_INVALID = 'server';
6130  /**
6131  * Connect to Server Failed
6132  * @const 
6133  */
6134 Roo.form.Action.CONNECT_FAILURE = 'connect';
6135 /**
6136  * Reading Data from Server Failed
6137  * @const 
6138  */
6139 Roo.form.Action.LOAD_FAILURE = 'load';
6140
6141 Roo.form.Action.prototype = {
6142     type : 'default',
6143     failureType : undefined,
6144     response : undefined,
6145     result : undefined,
6146
6147     // interface method
6148     run : function(options){
6149
6150     },
6151
6152     // interface method
6153     success : function(response){
6154
6155     },
6156
6157     // interface method
6158     handleResponse : function(response){
6159
6160     },
6161
6162     // default connection failure
6163     failure : function(response){
6164         
6165         this.response = response;
6166         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6167         this.form.afterAction(this, false);
6168     },
6169
6170     processResponse : function(response){
6171         this.response = response;
6172         if(!response.responseText){
6173             return true;
6174         }
6175         this.result = this.handleResponse(response);
6176         return this.result;
6177     },
6178
6179     // utility functions used internally
6180     getUrl : function(appendParams){
6181         var url = this.options.url || this.form.url || this.form.el.dom.action;
6182         if(appendParams){
6183             var p = this.getParams();
6184             if(p){
6185                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6186             }
6187         }
6188         return url;
6189     },
6190
6191     getMethod : function(){
6192         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6193     },
6194
6195     getParams : function(){
6196         var bp = this.form.baseParams;
6197         var p = this.options.params;
6198         if(p){
6199             if(typeof p == "object"){
6200                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6201             }else if(typeof p == 'string' && bp){
6202                 p += '&' + Roo.urlEncode(bp);
6203             }
6204         }else if(bp){
6205             p = Roo.urlEncode(bp);
6206         }
6207         return p;
6208     },
6209
6210     createCallback : function(){
6211         return {
6212             success: this.success,
6213             failure: this.failure,
6214             scope: this,
6215             timeout: (this.form.timeout*1000),
6216             upload: this.form.fileUpload ? this.success : undefined
6217         };
6218     }
6219 };
6220
6221 Roo.form.Action.Submit = function(form, options){
6222     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6223 };
6224
6225 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6226     type : 'submit',
6227
6228     haveProgress : false,
6229     uploadComplete : false,
6230     
6231     // uploadProgress indicator.
6232     uploadProgress : function()
6233     {
6234         if (!this.form.progressUrl) {
6235             return;
6236         }
6237         
6238         if (!this.haveProgress) {
6239             Roo.MessageBox.progress("Uploading", "Uploading");
6240         }
6241         if (this.uploadComplete) {
6242            Roo.MessageBox.hide();
6243            return;
6244         }
6245         
6246         this.haveProgress = true;
6247    
6248         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6249         
6250         var c = new Roo.data.Connection();
6251         c.request({
6252             url : this.form.progressUrl,
6253             params: {
6254                 id : uid
6255             },
6256             method: 'GET',
6257             success : function(req){
6258                //console.log(data);
6259                 var rdata = false;
6260                 var edata;
6261                 try  {
6262                    rdata = Roo.decode(req.responseText)
6263                 } catch (e) {
6264                     Roo.log("Invalid data from server..");
6265                     Roo.log(edata);
6266                     return;
6267                 }
6268                 if (!rdata || !rdata.success) {
6269                     Roo.log(rdata);
6270                     Roo.MessageBox.alert(Roo.encode(rdata));
6271                     return;
6272                 }
6273                 var data = rdata.data;
6274                 
6275                 if (this.uploadComplete) {
6276                    Roo.MessageBox.hide();
6277                    return;
6278                 }
6279                    
6280                 if (data){
6281                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6282                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6283                     );
6284                 }
6285                 this.uploadProgress.defer(2000,this);
6286             },
6287        
6288             failure: function(data) {
6289                 Roo.log('progress url failed ');
6290                 Roo.log(data);
6291             },
6292             scope : this
6293         });
6294            
6295     },
6296     
6297     
6298     run : function()
6299     {
6300         // run get Values on the form, so it syncs any secondary forms.
6301         this.form.getValues();
6302         
6303         var o = this.options;
6304         var method = this.getMethod();
6305         var isPost = method == 'POST';
6306         if(o.clientValidation === false || this.form.isValid()){
6307             
6308             if (this.form.progressUrl) {
6309                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6310                     (new Date() * 1) + '' + Math.random());
6311                     
6312             } 
6313             
6314             
6315             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6316                 form:this.form.el.dom,
6317                 url:this.getUrl(!isPost),
6318                 method: method,
6319                 params:isPost ? this.getParams() : null,
6320                 isUpload: this.form.fileUpload
6321             }));
6322             
6323             this.uploadProgress();
6324
6325         }else if (o.clientValidation !== false){ // client validation failed
6326             this.failureType = Roo.form.Action.CLIENT_INVALID;
6327             this.form.afterAction(this, false);
6328         }
6329     },
6330
6331     success : function(response)
6332     {
6333         this.uploadComplete= true;
6334         if (this.haveProgress) {
6335             Roo.MessageBox.hide();
6336         }
6337         
6338         
6339         var result = this.processResponse(response);
6340         if(result === true || result.success){
6341             this.form.afterAction(this, true);
6342             return;
6343         }
6344         if(result.errors){
6345             this.form.markInvalid(result.errors);
6346             this.failureType = Roo.form.Action.SERVER_INVALID;
6347         }
6348         this.form.afterAction(this, false);
6349     },
6350     failure : function(response)
6351     {
6352         this.uploadComplete= true;
6353         if (this.haveProgress) {
6354             Roo.MessageBox.hide();
6355         }
6356         
6357         this.response = response;
6358         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6359         this.form.afterAction(this, false);
6360     },
6361     
6362     handleResponse : function(response){
6363         if(this.form.errorReader){
6364             var rs = this.form.errorReader.read(response);
6365             var errors = [];
6366             if(rs.records){
6367                 for(var i = 0, len = rs.records.length; i < len; i++) {
6368                     var r = rs.records[i];
6369                     errors[i] = r.data;
6370                 }
6371             }
6372             if(errors.length < 1){
6373                 errors = null;
6374             }
6375             return {
6376                 success : rs.success,
6377                 errors : errors
6378             };
6379         }
6380         var ret = false;
6381         try {
6382             ret = Roo.decode(response.responseText);
6383         } catch (e) {
6384             ret = {
6385                 success: false,
6386                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6387                 errors : []
6388             };
6389         }
6390         return ret;
6391         
6392     }
6393 });
6394
6395
6396 Roo.form.Action.Load = function(form, options){
6397     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6398     this.reader = this.form.reader;
6399 };
6400
6401 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6402     type : 'load',
6403
6404     run : function(){
6405         
6406         Roo.Ajax.request(Roo.apply(
6407                 this.createCallback(), {
6408                     method:this.getMethod(),
6409                     url:this.getUrl(false),
6410                     params:this.getParams()
6411         }));
6412     },
6413
6414     success : function(response){
6415         
6416         var result = this.processResponse(response);
6417         if(result === true || !result.success || !result.data){
6418             this.failureType = Roo.form.Action.LOAD_FAILURE;
6419             this.form.afterAction(this, false);
6420             return;
6421         }
6422         this.form.clearInvalid();
6423         this.form.setValues(result.data);
6424         this.form.afterAction(this, true);
6425     },
6426
6427     handleResponse : function(response){
6428         if(this.form.reader){
6429             var rs = this.form.reader.read(response);
6430             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6431             return {
6432                 success : rs.success,
6433                 data : data
6434             };
6435         }
6436         return Roo.decode(response.responseText);
6437     }
6438 });
6439
6440 Roo.form.Action.ACTION_TYPES = {
6441     'load' : Roo.form.Action.Load,
6442     'submit' : Roo.form.Action.Submit
6443 };/*
6444  * - LGPL
6445  *
6446  * form
6447  * 
6448  */
6449
6450 /**
6451  * @class Roo.bootstrap.Form
6452  * @extends Roo.bootstrap.Component
6453  * Bootstrap Form class
6454  * @cfg {String} method  GET | POST (default POST)
6455  * @cfg {String} labelAlign top | left (default top)
6456  * @cfg {String} align left  | right - for navbars
6457  * @cfg {Boolean} loadMask load mask when submit (default true)
6458
6459  * 
6460  * @constructor
6461  * Create a new Form
6462  * @param {Object} config The config object
6463  */
6464
6465
6466 Roo.bootstrap.Form = function(config){
6467     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6468     this.addEvents({
6469         /**
6470          * @event clientvalidation
6471          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6472          * @param {Form} this
6473          * @param {Boolean} valid true if the form has passed client-side validation
6474          */
6475         clientvalidation: true,
6476         /**
6477          * @event beforeaction
6478          * Fires before any action is performed. Return false to cancel the action.
6479          * @param {Form} this
6480          * @param {Action} action The action to be performed
6481          */
6482         beforeaction: true,
6483         /**
6484          * @event actionfailed
6485          * Fires when an action fails.
6486          * @param {Form} this
6487          * @param {Action} action The action that failed
6488          */
6489         actionfailed : true,
6490         /**
6491          * @event actioncomplete
6492          * Fires when an action is completed.
6493          * @param {Form} this
6494          * @param {Action} action The action that completed
6495          */
6496         actioncomplete : true
6497     });
6498     
6499 };
6500
6501 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6502       
6503      /**
6504      * @cfg {String} method
6505      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6506      */
6507     method : 'POST',
6508     /**
6509      * @cfg {String} url
6510      * The URL to use for form actions if one isn't supplied in the action options.
6511      */
6512     /**
6513      * @cfg {Boolean} fileUpload
6514      * Set to true if this form is a file upload.
6515      */
6516      
6517     /**
6518      * @cfg {Object} baseParams
6519      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6520      */
6521       
6522     /**
6523      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6524      */
6525     timeout: 30,
6526     /**
6527      * @cfg {Sting} align (left|right) for navbar forms
6528      */
6529     align : 'left',
6530
6531     // private
6532     activeAction : null,
6533  
6534     /**
6535      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6536      * element by passing it or its id or mask the form itself by passing in true.
6537      * @type Mixed
6538      */
6539     waitMsgTarget : false,
6540     
6541     loadMask : true,
6542     
6543     getAutoCreate : function(){
6544         
6545         var cfg = {
6546             tag: 'form',
6547             method : this.method || 'POST',
6548             id : this.id || Roo.id(),
6549             cls : ''
6550         }
6551         if (this.parent().xtype.match(/^Nav/)) {
6552             cfg.cls = 'navbar-form navbar-' + this.align;
6553             
6554         }
6555         
6556         if (this.labelAlign == 'left' ) {
6557             cfg.cls += ' form-horizontal';
6558         }
6559         
6560         
6561         return cfg;
6562     },
6563     initEvents : function()
6564     {
6565         this.el.on('submit', this.onSubmit, this);
6566         // this was added as random key presses on the form where triggering form submit.
6567         this.el.on('keypress', function(e) {
6568             if (e.getCharCode() != 13) {
6569                 return true;
6570             }
6571             // we might need to allow it for textareas.. and some other items.
6572             // check e.getTarget().
6573             
6574             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6575                 return true;
6576             }
6577         
6578             Roo.log("keypress blocked");
6579             
6580             e.preventDefault();
6581             return false;
6582         });
6583         
6584     },
6585     // private
6586     onSubmit : function(e){
6587         e.stopEvent();
6588     },
6589     
6590      /**
6591      * Returns true if client-side validation on the form is successful.
6592      * @return Boolean
6593      */
6594     isValid : function(){
6595         var items = this.getItems();
6596         var valid = true;
6597         items.each(function(f){
6598            if(!f.validate()){
6599                valid = false;
6600                
6601            }
6602         });
6603         return valid;
6604     },
6605     /**
6606      * Returns true if any fields in this form have changed since their original load.
6607      * @return Boolean
6608      */
6609     isDirty : function(){
6610         var dirty = false;
6611         var items = this.getItems();
6612         items.each(function(f){
6613            if(f.isDirty()){
6614                dirty = true;
6615                return false;
6616            }
6617            return true;
6618         });
6619         return dirty;
6620     },
6621      /**
6622      * Performs a predefined action (submit or load) or custom actions you define on this form.
6623      * @param {String} actionName The name of the action type
6624      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6625      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6626      * accept other config options):
6627      * <pre>
6628 Property          Type             Description
6629 ----------------  ---------------  ----------------------------------------------------------------------------------
6630 url               String           The url for the action (defaults to the form's url)
6631 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6632 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6633 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6634                                    validate the form on the client (defaults to false)
6635      * </pre>
6636      * @return {BasicForm} this
6637      */
6638     doAction : function(action, options){
6639         if(typeof action == 'string'){
6640             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6641         }
6642         if(this.fireEvent('beforeaction', this, action) !== false){
6643             this.beforeAction(action);
6644             action.run.defer(100, action);
6645         }
6646         return this;
6647     },
6648     
6649     // private
6650     beforeAction : function(action){
6651         var o = action.options;
6652         
6653         if(this.loadMask){
6654             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6655         }
6656         // not really supported yet.. ??
6657         
6658         //if(this.waitMsgTarget === true){
6659         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6660         //}else if(this.waitMsgTarget){
6661         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6662         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6663         //}else {
6664         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6665        // }
6666          
6667     },
6668
6669     // private
6670     afterAction : function(action, success){
6671         this.activeAction = null;
6672         var o = action.options;
6673         
6674         //if(this.waitMsgTarget === true){
6675             this.el.unmask();
6676         //}else if(this.waitMsgTarget){
6677         //    this.waitMsgTarget.unmask();
6678         //}else{
6679         //    Roo.MessageBox.updateProgress(1);
6680         //    Roo.MessageBox.hide();
6681        // }
6682         // 
6683         if(success){
6684             if(o.reset){
6685                 this.reset();
6686             }
6687             Roo.callback(o.success, o.scope, [this, action]);
6688             this.fireEvent('actioncomplete', this, action);
6689             
6690         }else{
6691             
6692             // failure condition..
6693             // we have a scenario where updates need confirming.
6694             // eg. if a locking scenario exists..
6695             // we look for { errors : { needs_confirm : true }} in the response.
6696             if (
6697                 (typeof(action.result) != 'undefined')  &&
6698                 (typeof(action.result.errors) != 'undefined')  &&
6699                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6700            ){
6701                 var _t = this;
6702                 Roo.log("not supported yet");
6703                  /*
6704                 
6705                 Roo.MessageBox.confirm(
6706                     "Change requires confirmation",
6707                     action.result.errorMsg,
6708                     function(r) {
6709                         if (r != 'yes') {
6710                             return;
6711                         }
6712                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6713                     }
6714                     
6715                 );
6716                 */
6717                 
6718                 
6719                 return;
6720             }
6721             
6722             Roo.callback(o.failure, o.scope, [this, action]);
6723             // show an error message if no failed handler is set..
6724             if (!this.hasListener('actionfailed')) {
6725                 Roo.log("need to add dialog support");
6726                 /*
6727                 Roo.MessageBox.alert("Error",
6728                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6729                         action.result.errorMsg :
6730                         "Saving Failed, please check your entries or try again"
6731                 );
6732                 */
6733             }
6734             
6735             this.fireEvent('actionfailed', this, action);
6736         }
6737         
6738     },
6739     /**
6740      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6741      * @param {String} id The value to search for
6742      * @return Field
6743      */
6744     findField : function(id){
6745         var items = this.getItems();
6746         var field = items.get(id);
6747         if(!field){
6748              items.each(function(f){
6749                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6750                     field = f;
6751                     return false;
6752                 }
6753                 return true;
6754             });
6755         }
6756         return field || null;
6757     },
6758      /**
6759      * Mark fields in this form invalid in bulk.
6760      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6761      * @return {BasicForm} this
6762      */
6763     markInvalid : function(errors){
6764         if(errors instanceof Array){
6765             for(var i = 0, len = errors.length; i < len; i++){
6766                 var fieldError = errors[i];
6767                 var f = this.findField(fieldError.id);
6768                 if(f){
6769                     f.markInvalid(fieldError.msg);
6770                 }
6771             }
6772         }else{
6773             var field, id;
6774             for(id in errors){
6775                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6776                     field.markInvalid(errors[id]);
6777                 }
6778             }
6779         }
6780         //Roo.each(this.childForms || [], function (f) {
6781         //    f.markInvalid(errors);
6782         //});
6783         
6784         return this;
6785     },
6786
6787     /**
6788      * Set values for fields in this form in bulk.
6789      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6790      * @return {BasicForm} this
6791      */
6792     setValues : function(values){
6793         if(values instanceof Array){ // array of objects
6794             for(var i = 0, len = values.length; i < len; i++){
6795                 var v = values[i];
6796                 var f = this.findField(v.id);
6797                 if(f){
6798                     f.setValue(v.value);
6799                     if(this.trackResetOnLoad){
6800                         f.originalValue = f.getValue();
6801                     }
6802                 }
6803             }
6804         }else{ // object hash
6805             var field, id;
6806             for(id in values){
6807                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6808                     
6809                     if (field.setFromData && 
6810                         field.valueField && 
6811                         field.displayField &&
6812                         // combos' with local stores can 
6813                         // be queried via setValue()
6814                         // to set their value..
6815                         (field.store && !field.store.isLocal)
6816                         ) {
6817                         // it's a combo
6818                         var sd = { };
6819                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6820                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6821                         field.setFromData(sd);
6822                         
6823                     } else {
6824                         field.setValue(values[id]);
6825                     }
6826                     
6827                     
6828                     if(this.trackResetOnLoad){
6829                         field.originalValue = field.getValue();
6830                     }
6831                 }
6832             }
6833         }
6834          
6835         //Roo.each(this.childForms || [], function (f) {
6836         //    f.setValues(values);
6837         //});
6838                 
6839         return this;
6840     },
6841
6842     /**
6843      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6844      * they are returned as an array.
6845      * @param {Boolean} asString
6846      * @return {Object}
6847      */
6848     getValues : function(asString){
6849         //if (this.childForms) {
6850             // copy values from the child forms
6851         //    Roo.each(this.childForms, function (f) {
6852         //        this.setValues(f.getValues());
6853         //    }, this);
6854         //}
6855         
6856         
6857         
6858         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6859         if(asString === true){
6860             return fs;
6861         }
6862         return Roo.urlDecode(fs);
6863     },
6864     
6865     /**
6866      * Returns the fields in this form as an object with key/value pairs. 
6867      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6868      * @return {Object}
6869      */
6870     getFieldValues : function(with_hidden)
6871     {
6872         var items = this.getItems();
6873         var ret = {};
6874         items.each(function(f){
6875             if (!f.getName()) {
6876                 return;
6877             }
6878             var v = f.getValue();
6879             if (f.inputType =='radio') {
6880                 if (typeof(ret[f.getName()]) == 'undefined') {
6881                     ret[f.getName()] = ''; // empty..
6882                 }
6883                 
6884                 if (!f.el.dom.checked) {
6885                     return;
6886                     
6887                 }
6888                 v = f.el.dom.value;
6889                 
6890             }
6891             
6892             // not sure if this supported any more..
6893             if ((typeof(v) == 'object') && f.getRawValue) {
6894                 v = f.getRawValue() ; // dates..
6895             }
6896             // combo boxes where name != hiddenName...
6897             if (f.name != f.getName()) {
6898                 ret[f.name] = f.getRawValue();
6899             }
6900             ret[f.getName()] = v;
6901         });
6902         
6903         return ret;
6904     },
6905
6906     /**
6907      * Clears all invalid messages in this form.
6908      * @return {BasicForm} this
6909      */
6910     clearInvalid : function(){
6911         var items = this.getItems();
6912         
6913         items.each(function(f){
6914            f.clearInvalid();
6915         });
6916         
6917         
6918         
6919         return this;
6920     },
6921
6922     /**
6923      * Resets this form.
6924      * @return {BasicForm} this
6925      */
6926     reset : function(){
6927         var items = this.getItems();
6928         items.each(function(f){
6929             f.reset();
6930         });
6931         
6932         Roo.each(this.childForms || [], function (f) {
6933             f.reset();
6934         });
6935        
6936         
6937         return this;
6938     },
6939     getItems : function()
6940     {
6941         var r=new Roo.util.MixedCollection(false, function(o){
6942             return o.id || (o.id = Roo.id());
6943         });
6944         var iter = function(el) {
6945             if (el.inputEl) {
6946                 r.add(el);
6947             }
6948             if (!el.items) {
6949                 return;
6950             }
6951             Roo.each(el.items,function(e) {
6952                 iter(e);
6953             });
6954             
6955             
6956         };
6957         
6958         iter(this);
6959         return r;
6960         
6961         
6962         
6963         
6964     }
6965     
6966 });
6967
6968  
6969 /*
6970  * Based on:
6971  * Ext JS Library 1.1.1
6972  * Copyright(c) 2006-2007, Ext JS, LLC.
6973  *
6974  * Originally Released Under LGPL - original licence link has changed is not relivant.
6975  *
6976  * Fork - LGPL
6977  * <script type="text/javascript">
6978  */
6979 /**
6980  * @class Roo.form.VTypes
6981  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6982  * @singleton
6983  */
6984 Roo.form.VTypes = function(){
6985     // closure these in so they are only created once.
6986     var alpha = /^[a-zA-Z_]+$/;
6987     var alphanum = /^[a-zA-Z0-9_]+$/;
6988     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6989     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6990
6991     // All these messages and functions are configurable
6992     return {
6993         /**
6994          * The function used to validate email addresses
6995          * @param {String} value The email address
6996          */
6997         'email' : function(v){
6998             return email.test(v);
6999         },
7000         /**
7001          * The error text to display when the email validation function returns false
7002          * @type String
7003          */
7004         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7005         /**
7006          * The keystroke filter mask to be applied on email input
7007          * @type RegExp
7008          */
7009         'emailMask' : /[a-z0-9_\.\-@]/i,
7010
7011         /**
7012          * The function used to validate URLs
7013          * @param {String} value The URL
7014          */
7015         'url' : function(v){
7016             return url.test(v);
7017         },
7018         /**
7019          * The error text to display when the url validation function returns false
7020          * @type String
7021          */
7022         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7023         
7024         /**
7025          * The function used to validate alpha values
7026          * @param {String} value The value
7027          */
7028         'alpha' : function(v){
7029             return alpha.test(v);
7030         },
7031         /**
7032          * The error text to display when the alpha validation function returns false
7033          * @type String
7034          */
7035         'alphaText' : 'This field should only contain letters and _',
7036         /**
7037          * The keystroke filter mask to be applied on alpha input
7038          * @type RegExp
7039          */
7040         'alphaMask' : /[a-z_]/i,
7041
7042         /**
7043          * The function used to validate alphanumeric values
7044          * @param {String} value The value
7045          */
7046         'alphanum' : function(v){
7047             return alphanum.test(v);
7048         },
7049         /**
7050          * The error text to display when the alphanumeric validation function returns false
7051          * @type String
7052          */
7053         'alphanumText' : 'This field should only contain letters, numbers and _',
7054         /**
7055          * The keystroke filter mask to be applied on alphanumeric input
7056          * @type RegExp
7057          */
7058         'alphanumMask' : /[a-z0-9_]/i
7059     };
7060 }();/*
7061  * - LGPL
7062  *
7063  * Input
7064  * 
7065  */
7066
7067 /**
7068  * @class Roo.bootstrap.Input
7069  * @extends Roo.bootstrap.Component
7070  * Bootstrap Input class
7071  * @cfg {Boolean} disabled is it disabled
7072  * @cfg {String} fieldLabel - the label associated
7073  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7074  * @cfg {String} name name of the input
7075  * @cfg {string} fieldLabel - the label associated
7076  * @cfg {string}  inputType - input / file submit ...
7077  * @cfg {string} placeholder - placeholder to put in text.
7078  * @cfg {string}  before - input group add on before
7079  * @cfg {string} after - input group add on after
7080  * @cfg {string} size - (lg|sm) or leave empty..
7081  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7082  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7083  * @cfg {Number} md colspan out of 12 for computer-sized screens
7084  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7085  * @cfg {string} value default value of the input
7086  * @cfg {Number} labelWidth set the width of label (0-12)
7087  * @cfg {String} labelAlign (top|left)
7088  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7089  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7090
7091  * @cfg {String} align (left|center|right) Default left
7092  * 
7093  * 
7094  * 
7095  * @constructor
7096  * Create a new Input
7097  * @param {Object} config The config object
7098  */
7099
7100 Roo.bootstrap.Input = function(config){
7101     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7102    
7103         this.addEvents({
7104             /**
7105              * @event focus
7106              * Fires when this field receives input focus.
7107              * @param {Roo.form.Field} this
7108              */
7109             focus : true,
7110             /**
7111              * @event blur
7112              * Fires when this field loses input focus.
7113              * @param {Roo.form.Field} this
7114              */
7115             blur : true,
7116             /**
7117              * @event specialkey
7118              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7119              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7120              * @param {Roo.form.Field} this
7121              * @param {Roo.EventObject} e The event object
7122              */
7123             specialkey : true,
7124             /**
7125              * @event change
7126              * Fires just before the field blurs if the field value has changed.
7127              * @param {Roo.form.Field} this
7128              * @param {Mixed} newValue The new value
7129              * @param {Mixed} oldValue The original value
7130              */
7131             change : true,
7132             /**
7133              * @event invalid
7134              * Fires after the field has been marked as invalid.
7135              * @param {Roo.form.Field} this
7136              * @param {String} msg The validation message
7137              */
7138             invalid : true,
7139             /**
7140              * @event valid
7141              * Fires after the field has been validated with no errors.
7142              * @param {Roo.form.Field} this
7143              */
7144             valid : true,
7145              /**
7146              * @event keyup
7147              * Fires after the key up
7148              * @param {Roo.form.Field} this
7149              * @param {Roo.EventObject}  e The event Object
7150              */
7151             keyup : true
7152         });
7153 };
7154
7155 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7156      /**
7157      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7158       automatic validation (defaults to "keyup").
7159      */
7160     validationEvent : "keyup",
7161      /**
7162      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7163      */
7164     validateOnBlur : true,
7165     /**
7166      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7167      */
7168     validationDelay : 250,
7169      /**
7170      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7171      */
7172     focusClass : "x-form-focus",  // not needed???
7173     
7174        
7175     /**
7176      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7177      */
7178     invalidClass : "has-warning",
7179     
7180     /**
7181      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7182      */
7183     validClass : "has-success",
7184     
7185     /**
7186      * @cfg {Boolean} hasFeedback (true|false) default true
7187      */
7188     hasFeedback : true,
7189     
7190     /**
7191      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7192      */
7193     invalidFeedbackClass : "glyphicon-warning-sign",
7194     
7195     /**
7196      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7197      */
7198     validFeedbackClass : "glyphicon-ok",
7199     
7200     /**
7201      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7202      */
7203     selectOnFocus : false,
7204     
7205      /**
7206      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7207      */
7208     maskRe : null,
7209        /**
7210      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7211      */
7212     vtype : null,
7213     
7214       /**
7215      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7216      */
7217     disableKeyFilter : false,
7218     
7219        /**
7220      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7221      */
7222     disabled : false,
7223      /**
7224      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7225      */
7226     allowBlank : true,
7227     /**
7228      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7229      */
7230     blankText : "This field is required",
7231     
7232      /**
7233      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7234      */
7235     minLength : 0,
7236     /**
7237      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7238      */
7239     maxLength : Number.MAX_VALUE,
7240     /**
7241      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7242      */
7243     minLengthText : "The minimum length for this field is {0}",
7244     /**
7245      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7246      */
7247     maxLengthText : "The maximum length for this field is {0}",
7248   
7249     
7250     /**
7251      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7252      * If available, this function will be called only after the basic validators all return true, and will be passed the
7253      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7254      */
7255     validator : null,
7256     /**
7257      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7258      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7259      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7260      */
7261     regex : null,
7262     /**
7263      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7264      */
7265     regexText : "",
7266     
7267     autocomplete: false,
7268     
7269     
7270     fieldLabel : '',
7271     inputType : 'text',
7272     
7273     name : false,
7274     placeholder: false,
7275     before : false,
7276     after : false,
7277     size : false,
7278     hasFocus : false,
7279     preventMark: false,
7280     isFormField : true,
7281     value : '',
7282     labelWidth : 2,
7283     labelAlign : false,
7284     readOnly : false,
7285     align : false,
7286     formatedValue : false,
7287     
7288     parentLabelAlign : function()
7289     {
7290         var parent = this;
7291         while (parent.parent()) {
7292             parent = parent.parent();
7293             if (typeof(parent.labelAlign) !='undefined') {
7294                 return parent.labelAlign;
7295             }
7296         }
7297         return 'left';
7298         
7299     },
7300     
7301     getAutoCreate : function(){
7302         
7303         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7304         
7305         var id = Roo.id();
7306         
7307         var cfg = {};
7308         
7309         if(this.inputType != 'hidden'){
7310             cfg.cls = 'form-group' //input-group
7311         }
7312         
7313         var input =  {
7314             tag: 'input',
7315             id : id,
7316             type : this.inputType,
7317             value : this.value,
7318             cls : 'form-control',
7319             placeholder : this.placeholder || '',
7320             autocomplete : this.autocomplete || 'new-password'
7321         };
7322         
7323         
7324         if(this.align){
7325             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7326         }
7327         
7328         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7329             input.maxLength = this.maxLength;
7330         }
7331         
7332         if (this.disabled) {
7333             input.disabled=true;
7334         }
7335         
7336         if (this.readOnly) {
7337             input.readonly=true;
7338         }
7339         
7340         if (this.name) {
7341             input.name = this.name;
7342         }
7343         if (this.size) {
7344             input.cls += ' input-' + this.size;
7345         }
7346         var settings=this;
7347         ['xs','sm','md','lg'].map(function(size){
7348             if (settings[size]) {
7349                 cfg.cls += ' col-' + size + '-' + settings[size];
7350             }
7351         });
7352         
7353         var inputblock = input;
7354         
7355         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7356             
7357             var feedback = {
7358                 tag: 'span',
7359                 cls: 'glyphicon form-control-feedback'
7360             };
7361
7362             inputblock = {
7363                 cls : 'has-feedback',
7364                 cn :  [
7365                     input,
7366                     feedback
7367                 ] 
7368             };  
7369         }
7370          
7371 //        var inputblock = input;
7372         
7373         if (this.before || this.after) {
7374             
7375             inputblock = {
7376                 cls : 'input-group',
7377                 cn :  [] 
7378             };
7379             
7380             if (this.before && typeof(this.before) == 'string') {
7381                 
7382                 inputblock.cn.push({
7383                     tag :'span',
7384                     cls : 'roo-input-before input-group-addon',
7385                     html : this.before
7386                 });
7387             }
7388             if (this.before && typeof(this.before) == 'object') {
7389                 this.before = Roo.factory(this.before);
7390                 Roo.log(this.before);
7391                 inputblock.cn.push({
7392                     tag :'span',
7393                     cls : 'roo-input-before input-group-' +
7394                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7395                 });
7396             }
7397             
7398             inputblock.cn.push(input);
7399             
7400             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7401                 inputblock.cls += ' has-feedback';
7402                 inputblock.cn.push(feedback);
7403             }
7404             
7405             if (this.after && typeof(this.after) == 'string') {
7406                 inputblock.cn.push({
7407                     tag :'span',
7408                     cls : 'roo-input-after input-group-addon',
7409                     html : this.after
7410                 });
7411             }
7412             if (this.after && typeof(this.after) == 'object') {
7413                 this.after = Roo.factory(this.after);
7414                 Roo.log(this.after);
7415                 inputblock.cn.push({
7416                     tag :'span',
7417                     cls : 'roo-input-after input-group-' +
7418                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7419                 });
7420             }
7421         };
7422         
7423         if (align ==='left' && this.fieldLabel.length) {
7424                 Roo.log("left and has label");
7425                 cfg.cn = [
7426                     
7427                     {
7428                         tag: 'label',
7429                         'for' :  id,
7430                         cls : 'control-label col-sm-' + this.labelWidth,
7431                         html : this.fieldLabel
7432                         
7433                     },
7434                     {
7435                         cls : "col-sm-" + (12 - this.labelWidth), 
7436                         cn: [
7437                             inputblock
7438                         ]
7439                     }
7440                     
7441                 ];
7442         } else if ( this.fieldLabel.length) {
7443                 Roo.log(" label");
7444                  cfg.cn = [
7445                    
7446                     {
7447                         tag: 'label',
7448                         //cls : 'input-group-addon',
7449                         html : this.fieldLabel
7450                         
7451                     },
7452                     
7453                     inputblock
7454                     
7455                 ];
7456
7457         } else {
7458             
7459                 Roo.log(" no label && no align");
7460                 cfg.cn = [
7461                     
7462                         inputblock
7463                     
7464                 ];
7465                 
7466                 
7467         };
7468         Roo.log('input-parentType: ' + this.parentType);
7469         
7470         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7471            cfg.cls += ' navbar-form';
7472            Roo.log(cfg);
7473         }
7474         
7475         return cfg;
7476         
7477     },
7478     /**
7479      * return the real input element.
7480      */
7481     inputEl: function ()
7482     {
7483         return this.el.select('input.form-control',true).first();
7484     },
7485     
7486     tooltipEl : function()
7487     {
7488         return this.inputEl();
7489     },
7490     
7491     setDisabled : function(v)
7492     {
7493         var i  = this.inputEl().dom;
7494         if (!v) {
7495             i.removeAttribute('disabled');
7496             return;
7497             
7498         }
7499         i.setAttribute('disabled','true');
7500     },
7501     initEvents : function()
7502     {
7503           
7504         this.inputEl().on("keydown" , this.fireKey,  this);
7505         this.inputEl().on("focus", this.onFocus,  this);
7506         this.inputEl().on("blur", this.onBlur,  this);
7507         
7508         this.inputEl().relayEvent('keyup', this);
7509
7510         // reference to original value for reset
7511         this.originalValue = this.getValue();
7512         //Roo.form.TextField.superclass.initEvents.call(this);
7513         if(this.validationEvent == 'keyup'){
7514             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7515             this.inputEl().on('keyup', this.filterValidation, this);
7516         }
7517         else if(this.validationEvent !== false){
7518             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7519         }
7520         
7521         if(this.selectOnFocus){
7522             this.on("focus", this.preFocus, this);
7523             
7524         }
7525         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7526             this.inputEl().on("keypress", this.filterKeys, this);
7527         }
7528        /* if(this.grow){
7529             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7530             this.el.on("click", this.autoSize,  this);
7531         }
7532         */
7533         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7534             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7535         }
7536         
7537         if (typeof(this.before) == 'object') {
7538             this.before.render(this.el.select('.roo-input-before',true).first());
7539         }
7540         if (typeof(this.after) == 'object') {
7541             this.after.render(this.el.select('.roo-input-after',true).first());
7542         }
7543         
7544         
7545     },
7546     filterValidation : function(e){
7547         if(!e.isNavKeyPress()){
7548             this.validationTask.delay(this.validationDelay);
7549         }
7550     },
7551      /**
7552      * Validates the field value
7553      * @return {Boolean} True if the value is valid, else false
7554      */
7555     validate : function(){
7556         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7557         if(this.disabled || this.validateValue(this.getRawValue())){
7558             this.markValid();
7559             return true;
7560         }
7561         
7562         this.markInvalid();
7563         return false;
7564     },
7565     
7566     
7567     /**
7568      * Validates a value according to the field's validation rules and marks the field as invalid
7569      * if the validation fails
7570      * @param {Mixed} value The value to validate
7571      * @return {Boolean} True if the value is valid, else false
7572      */
7573     validateValue : function(value){
7574         if(value.length < 1)  { // if it's blank
7575             if(this.allowBlank){
7576                 return true;
7577             }
7578             return false;
7579         }
7580         
7581         if(value.length < this.minLength){
7582             return false;
7583         }
7584         if(value.length > this.maxLength){
7585             return false;
7586         }
7587         if(this.vtype){
7588             var vt = Roo.form.VTypes;
7589             if(!vt[this.vtype](value, this)){
7590                 return false;
7591             }
7592         }
7593         if(typeof this.validator == "function"){
7594             var msg = this.validator(value);
7595             if(msg !== true){
7596                 return false;
7597             }
7598         }
7599         
7600         if(this.regex && !this.regex.test(value)){
7601             return false;
7602         }
7603         
7604         return true;
7605     },
7606
7607     
7608     
7609      // private
7610     fireKey : function(e){
7611         //Roo.log('field ' + e.getKey());
7612         if(e.isNavKeyPress()){
7613             this.fireEvent("specialkey", this, e);
7614         }
7615     },
7616     focus : function (selectText){
7617         if(this.rendered){
7618             this.inputEl().focus();
7619             if(selectText === true){
7620                 this.inputEl().dom.select();
7621             }
7622         }
7623         return this;
7624     } ,
7625     
7626     onFocus : function(){
7627         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7628            // this.el.addClass(this.focusClass);
7629         }
7630         if(!this.hasFocus){
7631             this.hasFocus = true;
7632             this.startValue = this.getValue();
7633             this.fireEvent("focus", this);
7634         }
7635     },
7636     
7637     beforeBlur : Roo.emptyFn,
7638
7639     
7640     // private
7641     onBlur : function(){
7642         this.beforeBlur();
7643         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7644             //this.el.removeClass(this.focusClass);
7645         }
7646         this.hasFocus = false;
7647         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7648             this.validate();
7649         }
7650         var v = this.getValue();
7651         if(String(v) !== String(this.startValue)){
7652             this.fireEvent('change', this, v, this.startValue);
7653         }
7654         this.fireEvent("blur", this);
7655     },
7656     
7657     /**
7658      * Resets the current field value to the originally loaded value and clears any validation messages
7659      */
7660     reset : function(){
7661         this.setValue(this.originalValue);
7662         this.validate();
7663     },
7664      /**
7665      * Returns the name of the field
7666      * @return {Mixed} name The name field
7667      */
7668     getName: function(){
7669         return this.name;
7670     },
7671      /**
7672      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7673      * @return {Mixed} value The field value
7674      */
7675     getValue : function(){
7676         
7677         var v = this.inputEl().getValue();
7678         
7679         return v;
7680     },
7681     /**
7682      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7683      * @return {Mixed} value The field value
7684      */
7685     getRawValue : function(){
7686         var v = this.inputEl().getValue();
7687         
7688         return v;
7689     },
7690     
7691     /**
7692      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7693      * @param {Mixed} value The value to set
7694      */
7695     setRawValue : function(v){
7696         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7697     },
7698     
7699     selectText : function(start, end){
7700         var v = this.getRawValue();
7701         if(v.length > 0){
7702             start = start === undefined ? 0 : start;
7703             end = end === undefined ? v.length : end;
7704             var d = this.inputEl().dom;
7705             if(d.setSelectionRange){
7706                 d.setSelectionRange(start, end);
7707             }else if(d.createTextRange){
7708                 var range = d.createTextRange();
7709                 range.moveStart("character", start);
7710                 range.moveEnd("character", v.length-end);
7711                 range.select();
7712             }
7713         }
7714     },
7715     
7716     /**
7717      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7718      * @param {Mixed} value The value to set
7719      */
7720     setValue : function(v){
7721         this.value = v;
7722         if(this.rendered){
7723             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7724             this.validate();
7725         }
7726     },
7727     
7728     /*
7729     processValue : function(value){
7730         if(this.stripCharsRe){
7731             var newValue = value.replace(this.stripCharsRe, '');
7732             if(newValue !== value){
7733                 this.setRawValue(newValue);
7734                 return newValue;
7735             }
7736         }
7737         return value;
7738     },
7739   */
7740     preFocus : function(){
7741         
7742         if(this.selectOnFocus){
7743             this.inputEl().dom.select();
7744         }
7745     },
7746     filterKeys : function(e){
7747         var k = e.getKey();
7748         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7749             return;
7750         }
7751         var c = e.getCharCode(), cc = String.fromCharCode(c);
7752         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7753             return;
7754         }
7755         if(!this.maskRe.test(cc)){
7756             e.stopEvent();
7757         }
7758     },
7759      /**
7760      * Clear any invalid styles/messages for this field
7761      */
7762     clearInvalid : function(){
7763         
7764         if(!this.el || this.preventMark){ // not rendered
7765             return;
7766         }
7767         this.el.removeClass(this.invalidClass);
7768         
7769         this.fireEvent('valid', this);
7770     },
7771     
7772      /**
7773      * Mark this field as valid
7774      */
7775     markValid : function(){
7776         if(!this.el  || this.preventMark){ // not rendered
7777             return;
7778         }
7779         
7780         this.el.removeClass([this.invalidClass, this.validClass]);
7781         
7782         if(this.disabled || this.allowBlank){
7783             return;
7784         }
7785         
7786         this.el.addClass(this.validClass);
7787         
7788         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
7789             
7790             var feedback = this.el.select('.form-control-feedback', true).first();
7791             
7792             if(feedback){
7793                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7794                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
7795             }
7796             
7797         }
7798         
7799         this.fireEvent('valid', this);
7800     },
7801     
7802      /**
7803      * Mark this field as invalid
7804      * @param {String} msg The validation message
7805      */
7806     markInvalid : function(msg){
7807         if(!this.el  || this.preventMark){ // not rendered
7808             return;
7809         }
7810         
7811         this.el.removeClass([this.invalidClass, this.validClass]);
7812         
7813         if(this.disabled || this.allowBlank){
7814             return;
7815         }
7816         
7817         this.el.addClass(this.invalidClass);
7818         
7819         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7820             
7821             var feedback = this.el.select('.form-control-feedback', true).first();
7822             
7823             if(feedback){
7824                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7825                 
7826                 if(this.getValue().length){
7827                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
7828                 }
7829                 
7830             }
7831             
7832         }
7833         
7834         this.fireEvent('invalid', this, msg);
7835     },
7836     // private
7837     SafariOnKeyDown : function(event)
7838     {
7839         // this is a workaround for a password hang bug on chrome/ webkit.
7840         
7841         var isSelectAll = false;
7842         
7843         if(this.inputEl().dom.selectionEnd > 0){
7844             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7845         }
7846         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7847             event.preventDefault();
7848             this.setValue('');
7849             return;
7850         }
7851         
7852         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
7853             
7854             event.preventDefault();
7855             // this is very hacky as keydown always get's upper case.
7856             //
7857             var cc = String.fromCharCode(event.getCharCode());
7858             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7859             
7860         }
7861     },
7862     adjustWidth : function(tag, w){
7863         tag = tag.toLowerCase();
7864         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7865             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7866                 if(tag == 'input'){
7867                     return w + 2;
7868                 }
7869                 if(tag == 'textarea'){
7870                     return w-2;
7871                 }
7872             }else if(Roo.isOpera){
7873                 if(tag == 'input'){
7874                     return w + 2;
7875                 }
7876                 if(tag == 'textarea'){
7877                     return w-2;
7878                 }
7879             }
7880         }
7881         return w;
7882     }
7883     
7884 });
7885
7886  
7887 /*
7888  * - LGPL
7889  *
7890  * Input
7891  * 
7892  */
7893
7894 /**
7895  * @class Roo.bootstrap.TextArea
7896  * @extends Roo.bootstrap.Input
7897  * Bootstrap TextArea class
7898  * @cfg {Number} cols Specifies the visible width of a text area
7899  * @cfg {Number} rows Specifies the visible number of lines in a text area
7900  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7901  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7902  * @cfg {string} html text
7903  * 
7904  * @constructor
7905  * Create a new TextArea
7906  * @param {Object} config The config object
7907  */
7908
7909 Roo.bootstrap.TextArea = function(config){
7910     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7911    
7912 };
7913
7914 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7915      
7916     cols : false,
7917     rows : 5,
7918     readOnly : false,
7919     warp : 'soft',
7920     resize : false,
7921     value: false,
7922     html: false,
7923     
7924     getAutoCreate : function(){
7925         
7926         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7927         
7928         var id = Roo.id();
7929         
7930         var cfg = {};
7931         
7932         var input =  {
7933             tag: 'textarea',
7934             id : id,
7935             warp : this.warp,
7936             rows : this.rows,
7937             value : this.value || '',
7938             html: this.html || '',
7939             cls : 'form-control',
7940             placeholder : this.placeholder || '' 
7941             
7942         };
7943         
7944         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7945             input.maxLength = this.maxLength;
7946         }
7947         
7948         if(this.resize){
7949             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7950         }
7951         
7952         if(this.cols){
7953             input.cols = this.cols;
7954         }
7955         
7956         if (this.readOnly) {
7957             input.readonly = true;
7958         }
7959         
7960         if (this.name) {
7961             input.name = this.name;
7962         }
7963         
7964         if (this.size) {
7965             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7966         }
7967         
7968         var settings=this;
7969         ['xs','sm','md','lg'].map(function(size){
7970             if (settings[size]) {
7971                 cfg.cls += ' col-' + size + '-' + settings[size];
7972             }
7973         });
7974         
7975         var inputblock = input;
7976         
7977         if(this.hasFeedback && !this.allowBlank){
7978             
7979             var feedback = {
7980                 tag: 'span',
7981                 cls: 'glyphicon form-control-feedback'
7982             };
7983
7984             inputblock = {
7985                 cls : 'has-feedback',
7986                 cn :  [
7987                     input,
7988                     feedback
7989                 ] 
7990             };  
7991         }
7992         
7993         
7994         if (this.before || this.after) {
7995             
7996             inputblock = {
7997                 cls : 'input-group',
7998                 cn :  [] 
7999             };
8000             if (this.before) {
8001                 inputblock.cn.push({
8002                     tag :'span',
8003                     cls : 'input-group-addon',
8004                     html : this.before
8005                 });
8006             }
8007             
8008             inputblock.cn.push(input);
8009             
8010             if(this.hasFeedback && !this.allowBlank){
8011                 inputblock.cls += ' has-feedback';
8012                 inputblock.cn.push(feedback);
8013             }
8014             
8015             if (this.after) {
8016                 inputblock.cn.push({
8017                     tag :'span',
8018                     cls : 'input-group-addon',
8019                     html : this.after
8020                 });
8021             }
8022             
8023         }
8024         
8025         if (align ==='left' && this.fieldLabel.length) {
8026                 Roo.log("left and has label");
8027                 cfg.cn = [
8028                     
8029                     {
8030                         tag: 'label',
8031                         'for' :  id,
8032                         cls : 'control-label col-sm-' + this.labelWidth,
8033                         html : this.fieldLabel
8034                         
8035                     },
8036                     {
8037                         cls : "col-sm-" + (12 - this.labelWidth), 
8038                         cn: [
8039                             inputblock
8040                         ]
8041                     }
8042                     
8043                 ];
8044         } else if ( this.fieldLabel.length) {
8045                 Roo.log(" label");
8046                  cfg.cn = [
8047                    
8048                     {
8049                         tag: 'label',
8050                         //cls : 'input-group-addon',
8051                         html : this.fieldLabel
8052                         
8053                     },
8054                     
8055                     inputblock
8056                     
8057                 ];
8058
8059         } else {
8060             
8061                    Roo.log(" no label && no align");
8062                 cfg.cn = [
8063                     
8064                         inputblock
8065                     
8066                 ];
8067                 
8068                 
8069         }
8070         
8071         if (this.disabled) {
8072             input.disabled=true;
8073         }
8074         
8075         return cfg;
8076         
8077     },
8078     /**
8079      * return the real textarea element.
8080      */
8081     inputEl: function ()
8082     {
8083         return this.el.select('textarea.form-control',true).first();
8084     }
8085 });
8086
8087  
8088 /*
8089  * - LGPL
8090  *
8091  * trigger field - base class for combo..
8092  * 
8093  */
8094  
8095 /**
8096  * @class Roo.bootstrap.TriggerField
8097  * @extends Roo.bootstrap.Input
8098  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8099  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8100  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8101  * for which you can provide a custom implementation.  For example:
8102  * <pre><code>
8103 var trigger = new Roo.bootstrap.TriggerField();
8104 trigger.onTriggerClick = myTriggerFn;
8105 trigger.applyTo('my-field');
8106 </code></pre>
8107  *
8108  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8109  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8110  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8111  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8112  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8113
8114  * @constructor
8115  * Create a new TriggerField.
8116  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8117  * to the base TextField)
8118  */
8119 Roo.bootstrap.TriggerField = function(config){
8120     this.mimicing = false;
8121     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8122 };
8123
8124 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8125     /**
8126      * @cfg {String} triggerClass A CSS class to apply to the trigger
8127      */
8128      /**
8129      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8130      */
8131     hideTrigger:false,
8132
8133     /** @cfg {Boolean} grow @hide */
8134     /** @cfg {Number} growMin @hide */
8135     /** @cfg {Number} growMax @hide */
8136
8137     /**
8138      * @hide 
8139      * @method
8140      */
8141     autoSize: Roo.emptyFn,
8142     // private
8143     monitorTab : true,
8144     // private
8145     deferHeight : true,
8146
8147     
8148     actionMode : 'wrap',
8149     
8150     caret : false,
8151     
8152     
8153     getAutoCreate : function(){
8154        
8155         var align = this.labelAlign || this.parentLabelAlign();
8156         
8157         var id = Roo.id();
8158         
8159         var cfg = {
8160             cls: 'form-group' //input-group
8161         };
8162         
8163         
8164         var input =  {
8165             tag: 'input',
8166             id : id,
8167             type : this.inputType,
8168             cls : 'form-control',
8169             autocomplete: 'new-password',
8170             placeholder : this.placeholder || '' 
8171             
8172         };
8173         if (this.name) {
8174             input.name = this.name;
8175         }
8176         if (this.size) {
8177             input.cls += ' input-' + this.size;
8178         }
8179         
8180         if (this.disabled) {
8181             input.disabled=true;
8182         }
8183         
8184         var inputblock = input;
8185         
8186         if(this.hasFeedback && !this.allowBlank){
8187             
8188             var feedback = {
8189                 tag: 'span',
8190                 cls: 'glyphicon form-control-feedback'
8191             };
8192
8193             inputblock = {
8194                 cls : 'has-feedback',
8195                 cn :  [
8196                     input,
8197                     feedback
8198                 ] 
8199             };  
8200         }
8201         
8202         if (this.before || this.after) {
8203             
8204             inputblock = {
8205                 cls : 'input-group',
8206                 cn :  [] 
8207             };
8208             if (this.before) {
8209                 inputblock.cn.push({
8210                     tag :'span',
8211                     cls : 'input-group-addon',
8212                     html : this.before
8213                 });
8214             }
8215             
8216             inputblock.cn.push(input);
8217             
8218             if(this.hasFeedback && !this.allowBlank){
8219                 inputblock.cls += ' has-feedback';
8220                 inputblock.cn.push(feedback);
8221             }
8222             
8223             if (this.after) {
8224                 inputblock.cn.push({
8225                     tag :'span',
8226                     cls : 'input-group-addon',
8227                     html : this.after
8228                 });
8229             }
8230             
8231         };
8232         
8233         var box = {
8234             tag: 'div',
8235             cn: [
8236                 {
8237                     tag: 'input',
8238                     type : 'hidden',
8239                     cls: 'form-hidden-field'
8240                 },
8241                 inputblock
8242             ]
8243             
8244         };
8245         
8246         if(this.multiple){
8247             Roo.log('multiple');
8248             
8249             box = {
8250                 tag: 'div',
8251                 cn: [
8252                     {
8253                         tag: 'input',
8254                         type : 'hidden',
8255                         cls: 'form-hidden-field'
8256                     },
8257                     {
8258                         tag: 'ul',
8259                         cls: 'select2-choices',
8260                         cn:[
8261                             {
8262                                 tag: 'li',
8263                                 cls: 'select2-search-field',
8264                                 cn: [
8265
8266                                     inputblock
8267                                 ]
8268                             }
8269                         ]
8270                     }
8271                 ]
8272             }
8273         };
8274         
8275         var combobox = {
8276             cls: 'select2-container input-group',
8277             cn: [
8278                 box
8279 //                {
8280 //                    tag: 'ul',
8281 //                    cls: 'typeahead typeahead-long dropdown-menu',
8282 //                    style: 'display:none'
8283 //                }
8284             ]
8285         };
8286         
8287         if(!this.multiple && this.showToggleBtn){
8288             
8289             var caret = {
8290                         tag: 'span',
8291                         cls: 'caret'
8292              };
8293             if (this.caret != false) {
8294                 caret = {
8295                      tag: 'i',
8296                      cls: 'fa fa-' + this.caret
8297                 };
8298                 
8299             }
8300             
8301             combobox.cn.push({
8302                 tag :'span',
8303                 cls : 'input-group-addon btn dropdown-toggle',
8304                 cn : [
8305                     caret,
8306                     {
8307                         tag: 'span',
8308                         cls: 'combobox-clear',
8309                         cn  : [
8310                             {
8311                                 tag : 'i',
8312                                 cls: 'icon-remove'
8313                             }
8314                         ]
8315                     }
8316                 ]
8317
8318             })
8319         }
8320         
8321         if(this.multiple){
8322             combobox.cls += ' select2-container-multi';
8323         }
8324         
8325         if (align ==='left' && this.fieldLabel.length) {
8326             
8327                 Roo.log("left and has label");
8328                 cfg.cn = [
8329                     
8330                     {
8331                         tag: 'label',
8332                         'for' :  id,
8333                         cls : 'control-label col-sm-' + this.labelWidth,
8334                         html : this.fieldLabel
8335                         
8336                     },
8337                     {
8338                         cls : "col-sm-" + (12 - this.labelWidth), 
8339                         cn: [
8340                             combobox
8341                         ]
8342                     }
8343                     
8344                 ];
8345         } else if ( this.fieldLabel.length) {
8346                 Roo.log(" label");
8347                  cfg.cn = [
8348                    
8349                     {
8350                         tag: 'label',
8351                         //cls : 'input-group-addon',
8352                         html : this.fieldLabel
8353                         
8354                     },
8355                     
8356                     combobox
8357                     
8358                 ];
8359
8360         } else {
8361             
8362                 Roo.log(" no label && no align");
8363                 cfg = combobox
8364                      
8365                 
8366         }
8367          
8368         var settings=this;
8369         ['xs','sm','md','lg'].map(function(size){
8370             if (settings[size]) {
8371                 cfg.cls += ' col-' + size + '-' + settings[size];
8372             }
8373         });
8374         
8375         return cfg;
8376         
8377     },
8378     
8379     
8380     
8381     // private
8382     onResize : function(w, h){
8383 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8384 //        if(typeof w == 'number'){
8385 //            var x = w - this.trigger.getWidth();
8386 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8387 //            this.trigger.setStyle('left', x+'px');
8388 //        }
8389     },
8390
8391     // private
8392     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8393
8394     // private
8395     getResizeEl : function(){
8396         return this.inputEl();
8397     },
8398
8399     // private
8400     getPositionEl : function(){
8401         return this.inputEl();
8402     },
8403
8404     // private
8405     alignErrorIcon : function(){
8406         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8407     },
8408
8409     // private
8410     initEvents : function(){
8411         
8412         this.createList();
8413         
8414         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8415         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8416         if(!this.multiple && this.showToggleBtn){
8417             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8418             if(this.hideTrigger){
8419                 this.trigger.setDisplayed(false);
8420             }
8421             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8422         }
8423         
8424         if(this.multiple){
8425             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8426         }
8427         
8428         //this.trigger.addClassOnOver('x-form-trigger-over');
8429         //this.trigger.addClassOnClick('x-form-trigger-click');
8430         
8431         //if(!this.width){
8432         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8433         //}
8434     },
8435     
8436     createList : function()
8437     {
8438         this.list = Roo.get(document.body).createChild({
8439             tag: 'ul',
8440             cls: 'typeahead typeahead-long dropdown-menu',
8441             style: 'display:none'
8442         });
8443         
8444         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8445         
8446     },
8447
8448     // private
8449     initTrigger : function(){
8450        
8451     },
8452
8453     // private
8454     onDestroy : function(){
8455         if(this.trigger){
8456             this.trigger.removeAllListeners();
8457           //  this.trigger.remove();
8458         }
8459         //if(this.wrap){
8460         //    this.wrap.remove();
8461         //}
8462         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8463     },
8464
8465     // private
8466     onFocus : function(){
8467         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8468         /*
8469         if(!this.mimicing){
8470             this.wrap.addClass('x-trigger-wrap-focus');
8471             this.mimicing = true;
8472             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8473             if(this.monitorTab){
8474                 this.el.on("keydown", this.checkTab, this);
8475             }
8476         }
8477         */
8478     },
8479
8480     // private
8481     checkTab : function(e){
8482         if(e.getKey() == e.TAB){
8483             this.triggerBlur();
8484         }
8485     },
8486
8487     // private
8488     onBlur : function(){
8489         // do nothing
8490     },
8491
8492     // private
8493     mimicBlur : function(e, t){
8494         /*
8495         if(!this.wrap.contains(t) && this.validateBlur()){
8496             this.triggerBlur();
8497         }
8498         */
8499     },
8500
8501     // private
8502     triggerBlur : function(){
8503         this.mimicing = false;
8504         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8505         if(this.monitorTab){
8506             this.el.un("keydown", this.checkTab, this);
8507         }
8508         //this.wrap.removeClass('x-trigger-wrap-focus');
8509         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8510     },
8511
8512     // private
8513     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8514     validateBlur : function(e, t){
8515         return true;
8516     },
8517
8518     // private
8519     onDisable : function(){
8520         this.inputEl().dom.disabled = true;
8521         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8522         //if(this.wrap){
8523         //    this.wrap.addClass('x-item-disabled');
8524         //}
8525     },
8526
8527     // private
8528     onEnable : function(){
8529         this.inputEl().dom.disabled = false;
8530         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8531         //if(this.wrap){
8532         //    this.el.removeClass('x-item-disabled');
8533         //}
8534     },
8535
8536     // private
8537     onShow : function(){
8538         var ae = this.getActionEl();
8539         
8540         if(ae){
8541             ae.dom.style.display = '';
8542             ae.dom.style.visibility = 'visible';
8543         }
8544     },
8545
8546     // private
8547     
8548     onHide : function(){
8549         var ae = this.getActionEl();
8550         ae.dom.style.display = 'none';
8551     },
8552
8553     /**
8554      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8555      * by an implementing function.
8556      * @method
8557      * @param {EventObject} e
8558      */
8559     onTriggerClick : Roo.emptyFn
8560 });
8561  /*
8562  * Based on:
8563  * Ext JS Library 1.1.1
8564  * Copyright(c) 2006-2007, Ext JS, LLC.
8565  *
8566  * Originally Released Under LGPL - original licence link has changed is not relivant.
8567  *
8568  * Fork - LGPL
8569  * <script type="text/javascript">
8570  */
8571
8572
8573 /**
8574  * @class Roo.data.SortTypes
8575  * @singleton
8576  * Defines the default sorting (casting?) comparison functions used when sorting data.
8577  */
8578 Roo.data.SortTypes = {
8579     /**
8580      * Default sort that does nothing
8581      * @param {Mixed} s The value being converted
8582      * @return {Mixed} The comparison value
8583      */
8584     none : function(s){
8585         return s;
8586     },
8587     
8588     /**
8589      * The regular expression used to strip tags
8590      * @type {RegExp}
8591      * @property
8592      */
8593     stripTagsRE : /<\/?[^>]+>/gi,
8594     
8595     /**
8596      * Strips all HTML tags to sort on text only
8597      * @param {Mixed} s The value being converted
8598      * @return {String} The comparison value
8599      */
8600     asText : function(s){
8601         return String(s).replace(this.stripTagsRE, "");
8602     },
8603     
8604     /**
8605      * Strips all HTML tags to sort on text only - Case insensitive
8606      * @param {Mixed} s The value being converted
8607      * @return {String} The comparison value
8608      */
8609     asUCText : function(s){
8610         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8611     },
8612     
8613     /**
8614      * Case insensitive string
8615      * @param {Mixed} s The value being converted
8616      * @return {String} The comparison value
8617      */
8618     asUCString : function(s) {
8619         return String(s).toUpperCase();
8620     },
8621     
8622     /**
8623      * Date sorting
8624      * @param {Mixed} s The value being converted
8625      * @return {Number} The comparison value
8626      */
8627     asDate : function(s) {
8628         if(!s){
8629             return 0;
8630         }
8631         if(s instanceof Date){
8632             return s.getTime();
8633         }
8634         return Date.parse(String(s));
8635     },
8636     
8637     /**
8638      * Float sorting
8639      * @param {Mixed} s The value being converted
8640      * @return {Float} The comparison value
8641      */
8642     asFloat : function(s) {
8643         var val = parseFloat(String(s).replace(/,/g, ""));
8644         if(isNaN(val)) val = 0;
8645         return val;
8646     },
8647     
8648     /**
8649      * Integer sorting
8650      * @param {Mixed} s The value being converted
8651      * @return {Number} The comparison value
8652      */
8653     asInt : function(s) {
8654         var val = parseInt(String(s).replace(/,/g, ""));
8655         if(isNaN(val)) val = 0;
8656         return val;
8657     }
8658 };/*
8659  * Based on:
8660  * Ext JS Library 1.1.1
8661  * Copyright(c) 2006-2007, Ext JS, LLC.
8662  *
8663  * Originally Released Under LGPL - original licence link has changed is not relivant.
8664  *
8665  * Fork - LGPL
8666  * <script type="text/javascript">
8667  */
8668
8669 /**
8670 * @class Roo.data.Record
8671  * Instances of this class encapsulate both record <em>definition</em> information, and record
8672  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8673  * to access Records cached in an {@link Roo.data.Store} object.<br>
8674  * <p>
8675  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8676  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8677  * objects.<br>
8678  * <p>
8679  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8680  * @constructor
8681  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8682  * {@link #create}. The parameters are the same.
8683  * @param {Array} data An associative Array of data values keyed by the field name.
8684  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8685  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8686  * not specified an integer id is generated.
8687  */
8688 Roo.data.Record = function(data, id){
8689     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8690     this.data = data;
8691 };
8692
8693 /**
8694  * Generate a constructor for a specific record layout.
8695  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8696  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8697  * Each field definition object may contain the following properties: <ul>
8698  * <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,
8699  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8700  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8701  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8702  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8703  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8704  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8705  * this may be omitted.</p></li>
8706  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8707  * <ul><li>auto (Default, implies no conversion)</li>
8708  * <li>string</li>
8709  * <li>int</li>
8710  * <li>float</li>
8711  * <li>boolean</li>
8712  * <li>date</li></ul></p></li>
8713  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8714  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8715  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8716  * by the Reader into an object that will be stored in the Record. It is passed the
8717  * following parameters:<ul>
8718  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8719  * </ul></p></li>
8720  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8721  * </ul>
8722  * <br>usage:<br><pre><code>
8723 var TopicRecord = Roo.data.Record.create(
8724     {name: 'title', mapping: 'topic_title'},
8725     {name: 'author', mapping: 'username'},
8726     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8727     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8728     {name: 'lastPoster', mapping: 'user2'},
8729     {name: 'excerpt', mapping: 'post_text'}
8730 );
8731
8732 var myNewRecord = new TopicRecord({
8733     title: 'Do my job please',
8734     author: 'noobie',
8735     totalPosts: 1,
8736     lastPost: new Date(),
8737     lastPoster: 'Animal',
8738     excerpt: 'No way dude!'
8739 });
8740 myStore.add(myNewRecord);
8741 </code></pre>
8742  * @method create
8743  * @static
8744  */
8745 Roo.data.Record.create = function(o){
8746     var f = function(){
8747         f.superclass.constructor.apply(this, arguments);
8748     };
8749     Roo.extend(f, Roo.data.Record);
8750     var p = f.prototype;
8751     p.fields = new Roo.util.MixedCollection(false, function(field){
8752         return field.name;
8753     });
8754     for(var i = 0, len = o.length; i < len; i++){
8755         p.fields.add(new Roo.data.Field(o[i]));
8756     }
8757     f.getField = function(name){
8758         return p.fields.get(name);  
8759     };
8760     return f;
8761 };
8762
8763 Roo.data.Record.AUTO_ID = 1000;
8764 Roo.data.Record.EDIT = 'edit';
8765 Roo.data.Record.REJECT = 'reject';
8766 Roo.data.Record.COMMIT = 'commit';
8767
8768 Roo.data.Record.prototype = {
8769     /**
8770      * Readonly flag - true if this record has been modified.
8771      * @type Boolean
8772      */
8773     dirty : false,
8774     editing : false,
8775     error: null,
8776     modified: null,
8777
8778     // private
8779     join : function(store){
8780         this.store = store;
8781     },
8782
8783     /**
8784      * Set the named field to the specified value.
8785      * @param {String} name The name of the field to set.
8786      * @param {Object} value The value to set the field to.
8787      */
8788     set : function(name, value){
8789         if(this.data[name] == value){
8790             return;
8791         }
8792         this.dirty = true;
8793         if(!this.modified){
8794             this.modified = {};
8795         }
8796         if(typeof this.modified[name] == 'undefined'){
8797             this.modified[name] = this.data[name];
8798         }
8799         this.data[name] = value;
8800         if(!this.editing && this.store){
8801             this.store.afterEdit(this);
8802         }       
8803     },
8804
8805     /**
8806      * Get the value of the named field.
8807      * @param {String} name The name of the field to get the value of.
8808      * @return {Object} The value of the field.
8809      */
8810     get : function(name){
8811         return this.data[name]; 
8812     },
8813
8814     // private
8815     beginEdit : function(){
8816         this.editing = true;
8817         this.modified = {}; 
8818     },
8819
8820     // private
8821     cancelEdit : function(){
8822         this.editing = false;
8823         delete this.modified;
8824     },
8825
8826     // private
8827     endEdit : function(){
8828         this.editing = false;
8829         if(this.dirty && this.store){
8830             this.store.afterEdit(this);
8831         }
8832     },
8833
8834     /**
8835      * Usually called by the {@link Roo.data.Store} which owns the Record.
8836      * Rejects all changes made to the Record since either creation, or the last commit operation.
8837      * Modified fields are reverted to their original values.
8838      * <p>
8839      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8840      * of reject operations.
8841      */
8842     reject : function(){
8843         var m = this.modified;
8844         for(var n in m){
8845             if(typeof m[n] != "function"){
8846                 this.data[n] = m[n];
8847             }
8848         }
8849         this.dirty = false;
8850         delete this.modified;
8851         this.editing = false;
8852         if(this.store){
8853             this.store.afterReject(this);
8854         }
8855     },
8856
8857     /**
8858      * Usually called by the {@link Roo.data.Store} which owns the Record.
8859      * Commits all changes made to the Record since either creation, or the last commit operation.
8860      * <p>
8861      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8862      * of commit operations.
8863      */
8864     commit : function(){
8865         this.dirty = false;
8866         delete this.modified;
8867         this.editing = false;
8868         if(this.store){
8869             this.store.afterCommit(this);
8870         }
8871     },
8872
8873     // private
8874     hasError : function(){
8875         return this.error != null;
8876     },
8877
8878     // private
8879     clearError : function(){
8880         this.error = null;
8881     },
8882
8883     /**
8884      * Creates a copy of this record.
8885      * @param {String} id (optional) A new record id if you don't want to use this record's id
8886      * @return {Record}
8887      */
8888     copy : function(newId) {
8889         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8890     }
8891 };/*
8892  * Based on:
8893  * Ext JS Library 1.1.1
8894  * Copyright(c) 2006-2007, Ext JS, LLC.
8895  *
8896  * Originally Released Under LGPL - original licence link has changed is not relivant.
8897  *
8898  * Fork - LGPL
8899  * <script type="text/javascript">
8900  */
8901
8902
8903
8904 /**
8905  * @class Roo.data.Store
8906  * @extends Roo.util.Observable
8907  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8908  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8909  * <p>
8910  * 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
8911  * has no knowledge of the format of the data returned by the Proxy.<br>
8912  * <p>
8913  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8914  * instances from the data object. These records are cached and made available through accessor functions.
8915  * @constructor
8916  * Creates a new Store.
8917  * @param {Object} config A config object containing the objects needed for the Store to access data,
8918  * and read the data into Records.
8919  */
8920 Roo.data.Store = function(config){
8921     this.data = new Roo.util.MixedCollection(false);
8922     this.data.getKey = function(o){
8923         return o.id;
8924     };
8925     this.baseParams = {};
8926     // private
8927     this.paramNames = {
8928         "start" : "start",
8929         "limit" : "limit",
8930         "sort" : "sort",
8931         "dir" : "dir",
8932         "multisort" : "_multisort"
8933     };
8934
8935     if(config && config.data){
8936         this.inlineData = config.data;
8937         delete config.data;
8938     }
8939
8940     Roo.apply(this, config);
8941     
8942     if(this.reader){ // reader passed
8943         this.reader = Roo.factory(this.reader, Roo.data);
8944         this.reader.xmodule = this.xmodule || false;
8945         if(!this.recordType){
8946             this.recordType = this.reader.recordType;
8947         }
8948         if(this.reader.onMetaChange){
8949             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8950         }
8951     }
8952
8953     if(this.recordType){
8954         this.fields = this.recordType.prototype.fields;
8955     }
8956     this.modified = [];
8957
8958     this.addEvents({
8959         /**
8960          * @event datachanged
8961          * Fires when the data cache has changed, and a widget which is using this Store
8962          * as a Record cache should refresh its view.
8963          * @param {Store} this
8964          */
8965         datachanged : true,
8966         /**
8967          * @event metachange
8968          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8969          * @param {Store} this
8970          * @param {Object} meta The JSON metadata
8971          */
8972         metachange : true,
8973         /**
8974          * @event add
8975          * Fires when Records have been added to the Store
8976          * @param {Store} this
8977          * @param {Roo.data.Record[]} records The array of Records added
8978          * @param {Number} index The index at which the record(s) were added
8979          */
8980         add : true,
8981         /**
8982          * @event remove
8983          * Fires when a Record has been removed from the Store
8984          * @param {Store} this
8985          * @param {Roo.data.Record} record The Record that was removed
8986          * @param {Number} index The index at which the record was removed
8987          */
8988         remove : true,
8989         /**
8990          * @event update
8991          * Fires when a Record has been updated
8992          * @param {Store} this
8993          * @param {Roo.data.Record} record The Record that was updated
8994          * @param {String} operation The update operation being performed.  Value may be one of:
8995          * <pre><code>
8996  Roo.data.Record.EDIT
8997  Roo.data.Record.REJECT
8998  Roo.data.Record.COMMIT
8999          * </code></pre>
9000          */
9001         update : true,
9002         /**
9003          * @event clear
9004          * Fires when the data cache has been cleared.
9005          * @param {Store} this
9006          */
9007         clear : true,
9008         /**
9009          * @event beforeload
9010          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9011          * the load action will be canceled.
9012          * @param {Store} this
9013          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9014          */
9015         beforeload : true,
9016         /**
9017          * @event beforeloadadd
9018          * Fires after a new set of Records has been loaded.
9019          * @param {Store} this
9020          * @param {Roo.data.Record[]} records The Records that were loaded
9021          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9022          */
9023         beforeloadadd : true,
9024         /**
9025          * @event load
9026          * Fires after a new set of Records has been loaded, before they are added to the store.
9027          * @param {Store} this
9028          * @param {Roo.data.Record[]} records The Records that were loaded
9029          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9030          * @params {Object} return from reader
9031          */
9032         load : true,
9033         /**
9034          * @event loadexception
9035          * Fires if an exception occurs in the Proxy during loading.
9036          * Called with the signature of the Proxy's "loadexception" event.
9037          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9038          * 
9039          * @param {Proxy} 
9040          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9041          * @param {Object} load options 
9042          * @param {Object} jsonData from your request (normally this contains the Exception)
9043          */
9044         loadexception : true
9045     });
9046     
9047     if(this.proxy){
9048         this.proxy = Roo.factory(this.proxy, Roo.data);
9049         this.proxy.xmodule = this.xmodule || false;
9050         this.relayEvents(this.proxy,  ["loadexception"]);
9051     }
9052     this.sortToggle = {};
9053     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9054
9055     Roo.data.Store.superclass.constructor.call(this);
9056
9057     if(this.inlineData){
9058         this.loadData(this.inlineData);
9059         delete this.inlineData;
9060     }
9061 };
9062
9063 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9064      /**
9065     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9066     * without a remote query - used by combo/forms at present.
9067     */
9068     
9069     /**
9070     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9071     */
9072     /**
9073     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9074     */
9075     /**
9076     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9077     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9078     */
9079     /**
9080     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9081     * on any HTTP request
9082     */
9083     /**
9084     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9085     */
9086     /**
9087     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9088     */
9089     multiSort: false,
9090     /**
9091     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9092     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9093     */
9094     remoteSort : false,
9095
9096     /**
9097     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9098      * loaded or when a record is removed. (defaults to false).
9099     */
9100     pruneModifiedRecords : false,
9101
9102     // private
9103     lastOptions : null,
9104
9105     /**
9106      * Add Records to the Store and fires the add event.
9107      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9108      */
9109     add : function(records){
9110         records = [].concat(records);
9111         for(var i = 0, len = records.length; i < len; i++){
9112             records[i].join(this);
9113         }
9114         var index = this.data.length;
9115         this.data.addAll(records);
9116         this.fireEvent("add", this, records, index);
9117     },
9118
9119     /**
9120      * Remove a Record from the Store and fires the remove event.
9121      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9122      */
9123     remove : function(record){
9124         var index = this.data.indexOf(record);
9125         this.data.removeAt(index);
9126         if(this.pruneModifiedRecords){
9127             this.modified.remove(record);
9128         }
9129         this.fireEvent("remove", this, record, index);
9130     },
9131
9132     /**
9133      * Remove all Records from the Store and fires the clear event.
9134      */
9135     removeAll : function(){
9136         this.data.clear();
9137         if(this.pruneModifiedRecords){
9138             this.modified = [];
9139         }
9140         this.fireEvent("clear", this);
9141     },
9142
9143     /**
9144      * Inserts Records to the Store at the given index and fires the add event.
9145      * @param {Number} index The start index at which to insert the passed Records.
9146      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9147      */
9148     insert : function(index, records){
9149         records = [].concat(records);
9150         for(var i = 0, len = records.length; i < len; i++){
9151             this.data.insert(index, records[i]);
9152             records[i].join(this);
9153         }
9154         this.fireEvent("add", this, records, index);
9155     },
9156
9157     /**
9158      * Get the index within the cache of the passed Record.
9159      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9160      * @return {Number} The index of the passed Record. Returns -1 if not found.
9161      */
9162     indexOf : function(record){
9163         return this.data.indexOf(record);
9164     },
9165
9166     /**
9167      * Get the index within the cache of the Record with the passed id.
9168      * @param {String} id The id of the Record to find.
9169      * @return {Number} The index of the Record. Returns -1 if not found.
9170      */
9171     indexOfId : function(id){
9172         return this.data.indexOfKey(id);
9173     },
9174
9175     /**
9176      * Get the Record with the specified id.
9177      * @param {String} id The id of the Record to find.
9178      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9179      */
9180     getById : function(id){
9181         return this.data.key(id);
9182     },
9183
9184     /**
9185      * Get the Record at the specified index.
9186      * @param {Number} index The index of the Record to find.
9187      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9188      */
9189     getAt : function(index){
9190         return this.data.itemAt(index);
9191     },
9192
9193     /**
9194      * Returns a range of Records between specified indices.
9195      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9196      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9197      * @return {Roo.data.Record[]} An array of Records
9198      */
9199     getRange : function(start, end){
9200         return this.data.getRange(start, end);
9201     },
9202
9203     // private
9204     storeOptions : function(o){
9205         o = Roo.apply({}, o);
9206         delete o.callback;
9207         delete o.scope;
9208         this.lastOptions = o;
9209     },
9210
9211     /**
9212      * Loads the Record cache from the configured Proxy using the configured Reader.
9213      * <p>
9214      * If using remote paging, then the first load call must specify the <em>start</em>
9215      * and <em>limit</em> properties in the options.params property to establish the initial
9216      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9217      * <p>
9218      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9219      * and this call will return before the new data has been loaded. Perform any post-processing
9220      * in a callback function, or in a "load" event handler.</strong>
9221      * <p>
9222      * @param {Object} options An object containing properties which control loading options:<ul>
9223      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9224      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9225      * passed the following arguments:<ul>
9226      * <li>r : Roo.data.Record[]</li>
9227      * <li>options: Options object from the load call</li>
9228      * <li>success: Boolean success indicator</li></ul></li>
9229      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9230      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9231      * </ul>
9232      */
9233     load : function(options){
9234         options = options || {};
9235         if(this.fireEvent("beforeload", this, options) !== false){
9236             this.storeOptions(options);
9237             var p = Roo.apply(options.params || {}, this.baseParams);
9238             // if meta was not loaded from remote source.. try requesting it.
9239             if (!this.reader.metaFromRemote) {
9240                 p._requestMeta = 1;
9241             }
9242             if(this.sortInfo && this.remoteSort){
9243                 var pn = this.paramNames;
9244                 p[pn["sort"]] = this.sortInfo.field;
9245                 p[pn["dir"]] = this.sortInfo.direction;
9246             }
9247             if (this.multiSort) {
9248                 var pn = this.paramNames;
9249                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9250             }
9251             
9252             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9253         }
9254     },
9255
9256     /**
9257      * Reloads the Record cache from the configured Proxy using the configured Reader and
9258      * the options from the last load operation performed.
9259      * @param {Object} options (optional) An object containing properties which may override the options
9260      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9261      * the most recently used options are reused).
9262      */
9263     reload : function(options){
9264         this.load(Roo.applyIf(options||{}, this.lastOptions));
9265     },
9266
9267     // private
9268     // Called as a callback by the Reader during a load operation.
9269     loadRecords : function(o, options, success){
9270         if(!o || success === false){
9271             if(success !== false){
9272                 this.fireEvent("load", this, [], options, o);
9273             }
9274             if(options.callback){
9275                 options.callback.call(options.scope || this, [], options, false);
9276             }
9277             return;
9278         }
9279         // if data returned failure - throw an exception.
9280         if (o.success === false) {
9281             // show a message if no listener is registered.
9282             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9283                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9284             }
9285             // loadmask wil be hooked into this..
9286             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9287             return;
9288         }
9289         var r = o.records, t = o.totalRecords || r.length;
9290         
9291         this.fireEvent("beforeloadadd", this, r, options, o);
9292         
9293         if(!options || options.add !== true){
9294             if(this.pruneModifiedRecords){
9295                 this.modified = [];
9296             }
9297             for(var i = 0, len = r.length; i < len; i++){
9298                 r[i].join(this);
9299             }
9300             if(this.snapshot){
9301                 this.data = this.snapshot;
9302                 delete this.snapshot;
9303             }
9304             this.data.clear();
9305             this.data.addAll(r);
9306             this.totalLength = t;
9307             this.applySort();
9308             this.fireEvent("datachanged", this);
9309         }else{
9310             this.totalLength = Math.max(t, this.data.length+r.length);
9311             this.add(r);
9312         }
9313         this.fireEvent("load", this, r, options, o);
9314         if(options.callback){
9315             options.callback.call(options.scope || this, r, options, true);
9316         }
9317     },
9318
9319
9320     /**
9321      * Loads data from a passed data block. A Reader which understands the format of the data
9322      * must have been configured in the constructor.
9323      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9324      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9325      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9326      */
9327     loadData : function(o, append){
9328         var r = this.reader.readRecords(o);
9329         this.loadRecords(r, {add: append}, true);
9330     },
9331
9332     /**
9333      * Gets the number of cached records.
9334      * <p>
9335      * <em>If using paging, this may not be the total size of the dataset. If the data object
9336      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9337      * the data set size</em>
9338      */
9339     getCount : function(){
9340         return this.data.length || 0;
9341     },
9342
9343     /**
9344      * Gets the total number of records in the dataset as returned by the server.
9345      * <p>
9346      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9347      * the dataset size</em>
9348      */
9349     getTotalCount : function(){
9350         return this.totalLength || 0;
9351     },
9352
9353     /**
9354      * Returns the sort state of the Store as an object with two properties:
9355      * <pre><code>
9356  field {String} The name of the field by which the Records are sorted
9357  direction {String} The sort order, "ASC" or "DESC"
9358      * </code></pre>
9359      */
9360     getSortState : function(){
9361         return this.sortInfo;
9362     },
9363
9364     // private
9365     applySort : function(){
9366         if(this.sortInfo && !this.remoteSort){
9367             var s = this.sortInfo, f = s.field;
9368             var st = this.fields.get(f).sortType;
9369             var fn = function(r1, r2){
9370                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9371                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9372             };
9373             this.data.sort(s.direction, fn);
9374             if(this.snapshot && this.snapshot != this.data){
9375                 this.snapshot.sort(s.direction, fn);
9376             }
9377         }
9378     },
9379
9380     /**
9381      * Sets the default sort column and order to be used by the next load operation.
9382      * @param {String} fieldName The name of the field to sort by.
9383      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9384      */
9385     setDefaultSort : function(field, dir){
9386         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9387     },
9388
9389     /**
9390      * Sort the Records.
9391      * If remote sorting is used, the sort is performed on the server, and the cache is
9392      * reloaded. If local sorting is used, the cache is sorted internally.
9393      * @param {String} fieldName The name of the field to sort by.
9394      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9395      */
9396     sort : function(fieldName, dir){
9397         var f = this.fields.get(fieldName);
9398         if(!dir){
9399             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9400             
9401             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9402                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9403             }else{
9404                 dir = f.sortDir;
9405             }
9406         }
9407         this.sortToggle[f.name] = dir;
9408         this.sortInfo = {field: f.name, direction: dir};
9409         if(!this.remoteSort){
9410             this.applySort();
9411             this.fireEvent("datachanged", this);
9412         }else{
9413             this.load(this.lastOptions);
9414         }
9415     },
9416
9417     /**
9418      * Calls the specified function for each of the Records in the cache.
9419      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9420      * Returning <em>false</em> aborts and exits the iteration.
9421      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9422      */
9423     each : function(fn, scope){
9424         this.data.each(fn, scope);
9425     },
9426
9427     /**
9428      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9429      * (e.g., during paging).
9430      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9431      */
9432     getModifiedRecords : function(){
9433         return this.modified;
9434     },
9435
9436     // private
9437     createFilterFn : function(property, value, anyMatch){
9438         if(!value.exec){ // not a regex
9439             value = String(value);
9440             if(value.length == 0){
9441                 return false;
9442             }
9443             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9444         }
9445         return function(r){
9446             return value.test(r.data[property]);
9447         };
9448     },
9449
9450     /**
9451      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9452      * @param {String} property A field on your records
9453      * @param {Number} start The record index to start at (defaults to 0)
9454      * @param {Number} end The last record index to include (defaults to length - 1)
9455      * @return {Number} The sum
9456      */
9457     sum : function(property, start, end){
9458         var rs = this.data.items, v = 0;
9459         start = start || 0;
9460         end = (end || end === 0) ? end : rs.length-1;
9461
9462         for(var i = start; i <= end; i++){
9463             v += (rs[i].data[property] || 0);
9464         }
9465         return v;
9466     },
9467
9468     /**
9469      * Filter the records by a specified property.
9470      * @param {String} field A field on your records
9471      * @param {String/RegExp} value Either a string that the field
9472      * should start with or a RegExp to test against the field
9473      * @param {Boolean} anyMatch True to match any part not just the beginning
9474      */
9475     filter : function(property, value, anyMatch){
9476         var fn = this.createFilterFn(property, value, anyMatch);
9477         return fn ? this.filterBy(fn) : this.clearFilter();
9478     },
9479
9480     /**
9481      * Filter by a function. The specified function will be called with each
9482      * record in this data source. If the function returns true the record is included,
9483      * otherwise it is filtered.
9484      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9485      * @param {Object} scope (optional) The scope of the function (defaults to this)
9486      */
9487     filterBy : function(fn, scope){
9488         this.snapshot = this.snapshot || this.data;
9489         this.data = this.queryBy(fn, scope||this);
9490         this.fireEvent("datachanged", this);
9491     },
9492
9493     /**
9494      * Query the records by a specified property.
9495      * @param {String} field A field on your records
9496      * @param {String/RegExp} value Either a string that the field
9497      * should start with or a RegExp to test against the field
9498      * @param {Boolean} anyMatch True to match any part not just the beginning
9499      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9500      */
9501     query : function(property, value, anyMatch){
9502         var fn = this.createFilterFn(property, value, anyMatch);
9503         return fn ? this.queryBy(fn) : this.data.clone();
9504     },
9505
9506     /**
9507      * Query by a function. The specified function will be called with each
9508      * record in this data source. If the function returns true the record is included
9509      * in the results.
9510      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9511      * @param {Object} scope (optional) The scope of the function (defaults to this)
9512       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9513      **/
9514     queryBy : function(fn, scope){
9515         var data = this.snapshot || this.data;
9516         return data.filterBy(fn, scope||this);
9517     },
9518
9519     /**
9520      * Collects unique values for a particular dataIndex from this store.
9521      * @param {String} dataIndex The property to collect
9522      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9523      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9524      * @return {Array} An array of the unique values
9525      **/
9526     collect : function(dataIndex, allowNull, bypassFilter){
9527         var d = (bypassFilter === true && this.snapshot) ?
9528                 this.snapshot.items : this.data.items;
9529         var v, sv, r = [], l = {};
9530         for(var i = 0, len = d.length; i < len; i++){
9531             v = d[i].data[dataIndex];
9532             sv = String(v);
9533             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9534                 l[sv] = true;
9535                 r[r.length] = v;
9536             }
9537         }
9538         return r;
9539     },
9540
9541     /**
9542      * Revert to a view of the Record cache with no filtering applied.
9543      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9544      */
9545     clearFilter : function(suppressEvent){
9546         if(this.snapshot && this.snapshot != this.data){
9547             this.data = this.snapshot;
9548             delete this.snapshot;
9549             if(suppressEvent !== true){
9550                 this.fireEvent("datachanged", this);
9551             }
9552         }
9553     },
9554
9555     // private
9556     afterEdit : function(record){
9557         if(this.modified.indexOf(record) == -1){
9558             this.modified.push(record);
9559         }
9560         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9561     },
9562     
9563     // private
9564     afterReject : function(record){
9565         this.modified.remove(record);
9566         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9567     },
9568
9569     // private
9570     afterCommit : function(record){
9571         this.modified.remove(record);
9572         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9573     },
9574
9575     /**
9576      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9577      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9578      */
9579     commitChanges : function(){
9580         var m = this.modified.slice(0);
9581         this.modified = [];
9582         for(var i = 0, len = m.length; i < len; i++){
9583             m[i].commit();
9584         }
9585     },
9586
9587     /**
9588      * Cancel outstanding changes on all changed records.
9589      */
9590     rejectChanges : function(){
9591         var m = this.modified.slice(0);
9592         this.modified = [];
9593         for(var i = 0, len = m.length; i < len; i++){
9594             m[i].reject();
9595         }
9596     },
9597
9598     onMetaChange : function(meta, rtype, o){
9599         this.recordType = rtype;
9600         this.fields = rtype.prototype.fields;
9601         delete this.snapshot;
9602         this.sortInfo = meta.sortInfo || this.sortInfo;
9603         this.modified = [];
9604         this.fireEvent('metachange', this, this.reader.meta);
9605     },
9606     
9607     moveIndex : function(data, type)
9608     {
9609         var index = this.indexOf(data);
9610         
9611         var newIndex = index + type;
9612         
9613         this.remove(data);
9614         
9615         this.insert(newIndex, data);
9616         
9617     }
9618 });/*
9619  * Based on:
9620  * Ext JS Library 1.1.1
9621  * Copyright(c) 2006-2007, Ext JS, LLC.
9622  *
9623  * Originally Released Under LGPL - original licence link has changed is not relivant.
9624  *
9625  * Fork - LGPL
9626  * <script type="text/javascript">
9627  */
9628
9629 /**
9630  * @class Roo.data.SimpleStore
9631  * @extends Roo.data.Store
9632  * Small helper class to make creating Stores from Array data easier.
9633  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9634  * @cfg {Array} fields An array of field definition objects, or field name strings.
9635  * @cfg {Array} data The multi-dimensional array of data
9636  * @constructor
9637  * @param {Object} config
9638  */
9639 Roo.data.SimpleStore = function(config){
9640     Roo.data.SimpleStore.superclass.constructor.call(this, {
9641         isLocal : true,
9642         reader: new Roo.data.ArrayReader({
9643                 id: config.id
9644             },
9645             Roo.data.Record.create(config.fields)
9646         ),
9647         proxy : new Roo.data.MemoryProxy(config.data)
9648     });
9649     this.load();
9650 };
9651 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9652  * Based on:
9653  * Ext JS Library 1.1.1
9654  * Copyright(c) 2006-2007, Ext JS, LLC.
9655  *
9656  * Originally Released Under LGPL - original licence link has changed is not relivant.
9657  *
9658  * Fork - LGPL
9659  * <script type="text/javascript">
9660  */
9661
9662 /**
9663 /**
9664  * @extends Roo.data.Store
9665  * @class Roo.data.JsonStore
9666  * Small helper class to make creating Stores for JSON data easier. <br/>
9667 <pre><code>
9668 var store = new Roo.data.JsonStore({
9669     url: 'get-images.php',
9670     root: 'images',
9671     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9672 });
9673 </code></pre>
9674  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9675  * JsonReader and HttpProxy (unless inline data is provided).</b>
9676  * @cfg {Array} fields An array of field definition objects, or field name strings.
9677  * @constructor
9678  * @param {Object} config
9679  */
9680 Roo.data.JsonStore = function(c){
9681     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9682         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9683         reader: new Roo.data.JsonReader(c, c.fields)
9684     }));
9685 };
9686 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9687  * Based on:
9688  * Ext JS Library 1.1.1
9689  * Copyright(c) 2006-2007, Ext JS, LLC.
9690  *
9691  * Originally Released Under LGPL - original licence link has changed is not relivant.
9692  *
9693  * Fork - LGPL
9694  * <script type="text/javascript">
9695  */
9696
9697  
9698 Roo.data.Field = function(config){
9699     if(typeof config == "string"){
9700         config = {name: config};
9701     }
9702     Roo.apply(this, config);
9703     
9704     if(!this.type){
9705         this.type = "auto";
9706     }
9707     
9708     var st = Roo.data.SortTypes;
9709     // named sortTypes are supported, here we look them up
9710     if(typeof this.sortType == "string"){
9711         this.sortType = st[this.sortType];
9712     }
9713     
9714     // set default sortType for strings and dates
9715     if(!this.sortType){
9716         switch(this.type){
9717             case "string":
9718                 this.sortType = st.asUCString;
9719                 break;
9720             case "date":
9721                 this.sortType = st.asDate;
9722                 break;
9723             default:
9724                 this.sortType = st.none;
9725         }
9726     }
9727
9728     // define once
9729     var stripRe = /[\$,%]/g;
9730
9731     // prebuilt conversion function for this field, instead of
9732     // switching every time we're reading a value
9733     if(!this.convert){
9734         var cv, dateFormat = this.dateFormat;
9735         switch(this.type){
9736             case "":
9737             case "auto":
9738             case undefined:
9739                 cv = function(v){ return v; };
9740                 break;
9741             case "string":
9742                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9743                 break;
9744             case "int":
9745                 cv = function(v){
9746                     return v !== undefined && v !== null && v !== '' ?
9747                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9748                     };
9749                 break;
9750             case "float":
9751                 cv = function(v){
9752                     return v !== undefined && v !== null && v !== '' ?
9753                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9754                     };
9755                 break;
9756             case "bool":
9757             case "boolean":
9758                 cv = function(v){ return v === true || v === "true" || v == 1; };
9759                 break;
9760             case "date":
9761                 cv = function(v){
9762                     if(!v){
9763                         return '';
9764                     }
9765                     if(v instanceof Date){
9766                         return v;
9767                     }
9768                     if(dateFormat){
9769                         if(dateFormat == "timestamp"){
9770                             return new Date(v*1000);
9771                         }
9772                         return Date.parseDate(v, dateFormat);
9773                     }
9774                     var parsed = Date.parse(v);
9775                     return parsed ? new Date(parsed) : null;
9776                 };
9777              break;
9778             
9779         }
9780         this.convert = cv;
9781     }
9782 };
9783
9784 Roo.data.Field.prototype = {
9785     dateFormat: null,
9786     defaultValue: "",
9787     mapping: null,
9788     sortType : null,
9789     sortDir : "ASC"
9790 };/*
9791  * Based on:
9792  * Ext JS Library 1.1.1
9793  * Copyright(c) 2006-2007, Ext JS, LLC.
9794  *
9795  * Originally Released Under LGPL - original licence link has changed is not relivant.
9796  *
9797  * Fork - LGPL
9798  * <script type="text/javascript">
9799  */
9800  
9801 // Base class for reading structured data from a data source.  This class is intended to be
9802 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9803
9804 /**
9805  * @class Roo.data.DataReader
9806  * Base class for reading structured data from a data source.  This class is intended to be
9807  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9808  */
9809
9810 Roo.data.DataReader = function(meta, recordType){
9811     
9812     this.meta = meta;
9813     
9814     this.recordType = recordType instanceof Array ? 
9815         Roo.data.Record.create(recordType) : recordType;
9816 };
9817
9818 Roo.data.DataReader.prototype = {
9819      /**
9820      * Create an empty record
9821      * @param {Object} data (optional) - overlay some values
9822      * @return {Roo.data.Record} record created.
9823      */
9824     newRow :  function(d) {
9825         var da =  {};
9826         this.recordType.prototype.fields.each(function(c) {
9827             switch( c.type) {
9828                 case 'int' : da[c.name] = 0; break;
9829                 case 'date' : da[c.name] = new Date(); break;
9830                 case 'float' : da[c.name] = 0.0; break;
9831                 case 'boolean' : da[c.name] = false; break;
9832                 default : da[c.name] = ""; break;
9833             }
9834             
9835         });
9836         return new this.recordType(Roo.apply(da, d));
9837     }
9838     
9839 };/*
9840  * Based on:
9841  * Ext JS Library 1.1.1
9842  * Copyright(c) 2006-2007, Ext JS, LLC.
9843  *
9844  * Originally Released Under LGPL - original licence link has changed is not relivant.
9845  *
9846  * Fork - LGPL
9847  * <script type="text/javascript">
9848  */
9849
9850 /**
9851  * @class Roo.data.DataProxy
9852  * @extends Roo.data.Observable
9853  * This class is an abstract base class for implementations which provide retrieval of
9854  * unformatted data objects.<br>
9855  * <p>
9856  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9857  * (of the appropriate type which knows how to parse the data object) to provide a block of
9858  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9859  * <p>
9860  * Custom implementations must implement the load method as described in
9861  * {@link Roo.data.HttpProxy#load}.
9862  */
9863 Roo.data.DataProxy = function(){
9864     this.addEvents({
9865         /**
9866          * @event beforeload
9867          * Fires before a network request is made to retrieve a data object.
9868          * @param {Object} This DataProxy object.
9869          * @param {Object} params The params parameter to the load function.
9870          */
9871         beforeload : true,
9872         /**
9873          * @event load
9874          * Fires before the load method's callback is called.
9875          * @param {Object} This DataProxy object.
9876          * @param {Object} o The data object.
9877          * @param {Object} arg The callback argument object passed to the load function.
9878          */
9879         load : true,
9880         /**
9881          * @event loadexception
9882          * Fires if an Exception occurs during data retrieval.
9883          * @param {Object} This DataProxy object.
9884          * @param {Object} o The data object.
9885          * @param {Object} arg The callback argument object passed to the load function.
9886          * @param {Object} e The Exception.
9887          */
9888         loadexception : true
9889     });
9890     Roo.data.DataProxy.superclass.constructor.call(this);
9891 };
9892
9893 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9894
9895     /**
9896      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9897      */
9898 /*
9899  * Based on:
9900  * Ext JS Library 1.1.1
9901  * Copyright(c) 2006-2007, Ext JS, LLC.
9902  *
9903  * Originally Released Under LGPL - original licence link has changed is not relivant.
9904  *
9905  * Fork - LGPL
9906  * <script type="text/javascript">
9907  */
9908 /**
9909  * @class Roo.data.MemoryProxy
9910  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9911  * to the Reader when its load method is called.
9912  * @constructor
9913  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9914  */
9915 Roo.data.MemoryProxy = function(data){
9916     if (data.data) {
9917         data = data.data;
9918     }
9919     Roo.data.MemoryProxy.superclass.constructor.call(this);
9920     this.data = data;
9921 };
9922
9923 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9924     /**
9925      * Load data from the requested source (in this case an in-memory
9926      * data object passed to the constructor), read the data object into
9927      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9928      * process that block using the passed callback.
9929      * @param {Object} params This parameter is not used by the MemoryProxy class.
9930      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9931      * object into a block of Roo.data.Records.
9932      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9933      * The function must be passed <ul>
9934      * <li>The Record block object</li>
9935      * <li>The "arg" argument from the load function</li>
9936      * <li>A boolean success indicator</li>
9937      * </ul>
9938      * @param {Object} scope The scope in which to call the callback
9939      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9940      */
9941     load : function(params, reader, callback, scope, arg){
9942         params = params || {};
9943         var result;
9944         try {
9945             result = reader.readRecords(this.data);
9946         }catch(e){
9947             this.fireEvent("loadexception", this, arg, null, e);
9948             callback.call(scope, null, arg, false);
9949             return;
9950         }
9951         callback.call(scope, result, arg, true);
9952     },
9953     
9954     // private
9955     update : function(params, records){
9956         
9957     }
9958 });/*
9959  * Based on:
9960  * Ext JS Library 1.1.1
9961  * Copyright(c) 2006-2007, Ext JS, LLC.
9962  *
9963  * Originally Released Under LGPL - original licence link has changed is not relivant.
9964  *
9965  * Fork - LGPL
9966  * <script type="text/javascript">
9967  */
9968 /**
9969  * @class Roo.data.HttpProxy
9970  * @extends Roo.data.DataProxy
9971  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9972  * configured to reference a certain URL.<br><br>
9973  * <p>
9974  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9975  * from which the running page was served.<br><br>
9976  * <p>
9977  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9978  * <p>
9979  * Be aware that to enable the browser to parse an XML document, the server must set
9980  * the Content-Type header in the HTTP response to "text/xml".
9981  * @constructor
9982  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9983  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9984  * will be used to make the request.
9985  */
9986 Roo.data.HttpProxy = function(conn){
9987     Roo.data.HttpProxy.superclass.constructor.call(this);
9988     // is conn a conn config or a real conn?
9989     this.conn = conn;
9990     this.useAjax = !conn || !conn.events;
9991   
9992 };
9993
9994 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9995     // thse are take from connection...
9996     
9997     /**
9998      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9999      */
10000     /**
10001      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10002      * extra parameters to each request made by this object. (defaults to undefined)
10003      */
10004     /**
10005      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10006      *  to each request made by this object. (defaults to undefined)
10007      */
10008     /**
10009      * @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)
10010      */
10011     /**
10012      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10013      */
10014      /**
10015      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10016      * @type Boolean
10017      */
10018   
10019
10020     /**
10021      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10022      * @type Boolean
10023      */
10024     /**
10025      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10026      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10027      * a finer-grained basis than the DataProxy events.
10028      */
10029     getConnection : function(){
10030         return this.useAjax ? Roo.Ajax : this.conn;
10031     },
10032
10033     /**
10034      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10035      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10036      * process that block using the passed callback.
10037      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10038      * for the request to the remote server.
10039      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10040      * object into a block of Roo.data.Records.
10041      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10042      * The function must be passed <ul>
10043      * <li>The Record block object</li>
10044      * <li>The "arg" argument from the load function</li>
10045      * <li>A boolean success indicator</li>
10046      * </ul>
10047      * @param {Object} scope The scope in which to call the callback
10048      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10049      */
10050     load : function(params, reader, callback, scope, arg){
10051         if(this.fireEvent("beforeload", this, params) !== false){
10052             var  o = {
10053                 params : params || {},
10054                 request: {
10055                     callback : callback,
10056                     scope : scope,
10057                     arg : arg
10058                 },
10059                 reader: reader,
10060                 callback : this.loadResponse,
10061                 scope: this
10062             };
10063             if(this.useAjax){
10064                 Roo.applyIf(o, this.conn);
10065                 if(this.activeRequest){
10066                     Roo.Ajax.abort(this.activeRequest);
10067                 }
10068                 this.activeRequest = Roo.Ajax.request(o);
10069             }else{
10070                 this.conn.request(o);
10071             }
10072         }else{
10073             callback.call(scope||this, null, arg, false);
10074         }
10075     },
10076
10077     // private
10078     loadResponse : function(o, success, response){
10079         delete this.activeRequest;
10080         if(!success){
10081             this.fireEvent("loadexception", this, o, response);
10082             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10083             return;
10084         }
10085         var result;
10086         try {
10087             result = o.reader.read(response);
10088         }catch(e){
10089             this.fireEvent("loadexception", this, o, response, e);
10090             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10091             return;
10092         }
10093         
10094         this.fireEvent("load", this, o, o.request.arg);
10095         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10096     },
10097
10098     // private
10099     update : function(dataSet){
10100
10101     },
10102
10103     // private
10104     updateResponse : function(dataSet){
10105
10106     }
10107 });/*
10108  * Based on:
10109  * Ext JS Library 1.1.1
10110  * Copyright(c) 2006-2007, Ext JS, LLC.
10111  *
10112  * Originally Released Under LGPL - original licence link has changed is not relivant.
10113  *
10114  * Fork - LGPL
10115  * <script type="text/javascript">
10116  */
10117
10118 /**
10119  * @class Roo.data.ScriptTagProxy
10120  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10121  * other than the originating domain of the running page.<br><br>
10122  * <p>
10123  * <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
10124  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10125  * <p>
10126  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10127  * source code that is used as the source inside a &lt;script> tag.<br><br>
10128  * <p>
10129  * In order for the browser to process the returned data, the server must wrap the data object
10130  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10131  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10132  * depending on whether the callback name was passed:
10133  * <p>
10134  * <pre><code>
10135 boolean scriptTag = false;
10136 String cb = request.getParameter("callback");
10137 if (cb != null) {
10138     scriptTag = true;
10139     response.setContentType("text/javascript");
10140 } else {
10141     response.setContentType("application/x-json");
10142 }
10143 Writer out = response.getWriter();
10144 if (scriptTag) {
10145     out.write(cb + "(");
10146 }
10147 out.print(dataBlock.toJsonString());
10148 if (scriptTag) {
10149     out.write(");");
10150 }
10151 </pre></code>
10152  *
10153  * @constructor
10154  * @param {Object} config A configuration object.
10155  */
10156 Roo.data.ScriptTagProxy = function(config){
10157     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10158     Roo.apply(this, config);
10159     this.head = document.getElementsByTagName("head")[0];
10160 };
10161
10162 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10163
10164 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10165     /**
10166      * @cfg {String} url The URL from which to request the data object.
10167      */
10168     /**
10169      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10170      */
10171     timeout : 30000,
10172     /**
10173      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10174      * the server the name of the callback function set up by the load call to process the returned data object.
10175      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10176      * javascript output which calls this named function passing the data object as its only parameter.
10177      */
10178     callbackParam : "callback",
10179     /**
10180      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10181      * name to the request.
10182      */
10183     nocache : true,
10184
10185     /**
10186      * Load data from the configured URL, read the data object into
10187      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10188      * process that block using the passed callback.
10189      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10190      * for the request to the remote server.
10191      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10192      * object into a block of Roo.data.Records.
10193      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10194      * The function must be passed <ul>
10195      * <li>The Record block object</li>
10196      * <li>The "arg" argument from the load function</li>
10197      * <li>A boolean success indicator</li>
10198      * </ul>
10199      * @param {Object} scope The scope in which to call the callback
10200      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10201      */
10202     load : function(params, reader, callback, scope, arg){
10203         if(this.fireEvent("beforeload", this, params) !== false){
10204
10205             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10206
10207             var url = this.url;
10208             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10209             if(this.nocache){
10210                 url += "&_dc=" + (new Date().getTime());
10211             }
10212             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10213             var trans = {
10214                 id : transId,
10215                 cb : "stcCallback"+transId,
10216                 scriptId : "stcScript"+transId,
10217                 params : params,
10218                 arg : arg,
10219                 url : url,
10220                 callback : callback,
10221                 scope : scope,
10222                 reader : reader
10223             };
10224             var conn = this;
10225
10226             window[trans.cb] = function(o){
10227                 conn.handleResponse(o, trans);
10228             };
10229
10230             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10231
10232             if(this.autoAbort !== false){
10233                 this.abort();
10234             }
10235
10236             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10237
10238             var script = document.createElement("script");
10239             script.setAttribute("src", url);
10240             script.setAttribute("type", "text/javascript");
10241             script.setAttribute("id", trans.scriptId);
10242             this.head.appendChild(script);
10243
10244             this.trans = trans;
10245         }else{
10246             callback.call(scope||this, null, arg, false);
10247         }
10248     },
10249
10250     // private
10251     isLoading : function(){
10252         return this.trans ? true : false;
10253     },
10254
10255     /**
10256      * Abort the current server request.
10257      */
10258     abort : function(){
10259         if(this.isLoading()){
10260             this.destroyTrans(this.trans);
10261         }
10262     },
10263
10264     // private
10265     destroyTrans : function(trans, isLoaded){
10266         this.head.removeChild(document.getElementById(trans.scriptId));
10267         clearTimeout(trans.timeoutId);
10268         if(isLoaded){
10269             window[trans.cb] = undefined;
10270             try{
10271                 delete window[trans.cb];
10272             }catch(e){}
10273         }else{
10274             // if hasn't been loaded, wait for load to remove it to prevent script error
10275             window[trans.cb] = function(){
10276                 window[trans.cb] = undefined;
10277                 try{
10278                     delete window[trans.cb];
10279                 }catch(e){}
10280             };
10281         }
10282     },
10283
10284     // private
10285     handleResponse : function(o, trans){
10286         this.trans = false;
10287         this.destroyTrans(trans, true);
10288         var result;
10289         try {
10290             result = trans.reader.readRecords(o);
10291         }catch(e){
10292             this.fireEvent("loadexception", this, o, trans.arg, e);
10293             trans.callback.call(trans.scope||window, null, trans.arg, false);
10294             return;
10295         }
10296         this.fireEvent("load", this, o, trans.arg);
10297         trans.callback.call(trans.scope||window, result, trans.arg, true);
10298     },
10299
10300     // private
10301     handleFailure : function(trans){
10302         this.trans = false;
10303         this.destroyTrans(trans, false);
10304         this.fireEvent("loadexception", this, null, trans.arg);
10305         trans.callback.call(trans.scope||window, null, trans.arg, false);
10306     }
10307 });/*
10308  * Based on:
10309  * Ext JS Library 1.1.1
10310  * Copyright(c) 2006-2007, Ext JS, LLC.
10311  *
10312  * Originally Released Under LGPL - original licence link has changed is not relivant.
10313  *
10314  * Fork - LGPL
10315  * <script type="text/javascript">
10316  */
10317
10318 /**
10319  * @class Roo.data.JsonReader
10320  * @extends Roo.data.DataReader
10321  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10322  * based on mappings in a provided Roo.data.Record constructor.
10323  * 
10324  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10325  * in the reply previously. 
10326  * 
10327  * <p>
10328  * Example code:
10329  * <pre><code>
10330 var RecordDef = Roo.data.Record.create([
10331     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10332     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10333 ]);
10334 var myReader = new Roo.data.JsonReader({
10335     totalProperty: "results",    // The property which contains the total dataset size (optional)
10336     root: "rows",                // The property which contains an Array of row objects
10337     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10338 }, RecordDef);
10339 </code></pre>
10340  * <p>
10341  * This would consume a JSON file like this:
10342  * <pre><code>
10343 { 'results': 2, 'rows': [
10344     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10345     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10346 }
10347 </code></pre>
10348  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10349  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10350  * paged from the remote server.
10351  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10352  * @cfg {String} root name of the property which contains the Array of row objects.
10353  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10354  * @constructor
10355  * Create a new JsonReader
10356  * @param {Object} meta Metadata configuration options
10357  * @param {Object} recordType Either an Array of field definition objects,
10358  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10359  */
10360 Roo.data.JsonReader = function(meta, recordType){
10361     
10362     meta = meta || {};
10363     // set some defaults:
10364     Roo.applyIf(meta, {
10365         totalProperty: 'total',
10366         successProperty : 'success',
10367         root : 'data',
10368         id : 'id'
10369     });
10370     
10371     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10372 };
10373 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10374     
10375     /**
10376      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10377      * Used by Store query builder to append _requestMeta to params.
10378      * 
10379      */
10380     metaFromRemote : false,
10381     /**
10382      * This method is only used by a DataProxy which has retrieved data from a remote server.
10383      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10384      * @return {Object} data A data block which is used by an Roo.data.Store object as
10385      * a cache of Roo.data.Records.
10386      */
10387     read : function(response){
10388         var json = response.responseText;
10389        
10390         var o = /* eval:var:o */ eval("("+json+")");
10391         if(!o) {
10392             throw {message: "JsonReader.read: Json object not found"};
10393         }
10394         
10395         if(o.metaData){
10396             
10397             delete this.ef;
10398             this.metaFromRemote = true;
10399             this.meta = o.metaData;
10400             this.recordType = Roo.data.Record.create(o.metaData.fields);
10401             this.onMetaChange(this.meta, this.recordType, o);
10402         }
10403         return this.readRecords(o);
10404     },
10405
10406     // private function a store will implement
10407     onMetaChange : function(meta, recordType, o){
10408
10409     },
10410
10411     /**
10412          * @ignore
10413          */
10414     simpleAccess: function(obj, subsc) {
10415         return obj[subsc];
10416     },
10417
10418         /**
10419          * @ignore
10420          */
10421     getJsonAccessor: function(){
10422         var re = /[\[\.]/;
10423         return function(expr) {
10424             try {
10425                 return(re.test(expr))
10426                     ? new Function("obj", "return obj." + expr)
10427                     : function(obj){
10428                         return obj[expr];
10429                     };
10430             } catch(e){}
10431             return Roo.emptyFn;
10432         };
10433     }(),
10434
10435     /**
10436      * Create a data block containing Roo.data.Records from an XML document.
10437      * @param {Object} o An object which contains an Array of row objects in the property specified
10438      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10439      * which contains the total size of the dataset.
10440      * @return {Object} data A data block which is used by an Roo.data.Store object as
10441      * a cache of Roo.data.Records.
10442      */
10443     readRecords : function(o){
10444         /**
10445          * After any data loads, the raw JSON data is available for further custom processing.
10446          * @type Object
10447          */
10448         this.o = o;
10449         var s = this.meta, Record = this.recordType,
10450             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10451
10452 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10453         if (!this.ef) {
10454             if(s.totalProperty) {
10455                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10456                 }
10457                 if(s.successProperty) {
10458                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10459                 }
10460                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10461                 if (s.id) {
10462                         var g = this.getJsonAccessor(s.id);
10463                         this.getId = function(rec) {
10464                                 var r = g(rec);  
10465                                 return (r === undefined || r === "") ? null : r;
10466                         };
10467                 } else {
10468                         this.getId = function(){return null;};
10469                 }
10470             this.ef = [];
10471             for(var jj = 0; jj < fl; jj++){
10472                 f = fi[jj];
10473                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10474                 this.ef[jj] = this.getJsonAccessor(map);
10475             }
10476         }
10477
10478         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10479         if(s.totalProperty){
10480             var vt = parseInt(this.getTotal(o), 10);
10481             if(!isNaN(vt)){
10482                 totalRecords = vt;
10483             }
10484         }
10485         if(s.successProperty){
10486             var vs = this.getSuccess(o);
10487             if(vs === false || vs === 'false'){
10488                 success = false;
10489             }
10490         }
10491         var records = [];
10492         for(var i = 0; i < c; i++){
10493                 var n = root[i];
10494             var values = {};
10495             var id = this.getId(n);
10496             for(var j = 0; j < fl; j++){
10497                 f = fi[j];
10498             var v = this.ef[j](n);
10499             if (!f.convert) {
10500                 Roo.log('missing convert for ' + f.name);
10501                 Roo.log(f);
10502                 continue;
10503             }
10504             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10505             }
10506             var record = new Record(values, id);
10507             record.json = n;
10508             records[i] = record;
10509         }
10510         return {
10511             raw : o,
10512             success : success,
10513             records : records,
10514             totalRecords : totalRecords
10515         };
10516     }
10517 });/*
10518  * Based on:
10519  * Ext JS Library 1.1.1
10520  * Copyright(c) 2006-2007, Ext JS, LLC.
10521  *
10522  * Originally Released Under LGPL - original licence link has changed is not relivant.
10523  *
10524  * Fork - LGPL
10525  * <script type="text/javascript">
10526  */
10527
10528 /**
10529  * @class Roo.data.ArrayReader
10530  * @extends Roo.data.DataReader
10531  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10532  * Each element of that Array represents a row of data fields. The
10533  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10534  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10535  * <p>
10536  * Example code:.
10537  * <pre><code>
10538 var RecordDef = Roo.data.Record.create([
10539     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10540     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10541 ]);
10542 var myReader = new Roo.data.ArrayReader({
10543     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10544 }, RecordDef);
10545 </code></pre>
10546  * <p>
10547  * This would consume an Array like this:
10548  * <pre><code>
10549 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10550   </code></pre>
10551  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10552  * @constructor
10553  * Create a new JsonReader
10554  * @param {Object} meta Metadata configuration options.
10555  * @param {Object} recordType Either an Array of field definition objects
10556  * as specified to {@link Roo.data.Record#create},
10557  * or an {@link Roo.data.Record} object
10558  * created using {@link Roo.data.Record#create}.
10559  */
10560 Roo.data.ArrayReader = function(meta, recordType){
10561     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10562 };
10563
10564 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10565     /**
10566      * Create a data block containing Roo.data.Records from an XML document.
10567      * @param {Object} o An Array of row objects which represents the dataset.
10568      * @return {Object} data A data block which is used by an Roo.data.Store object as
10569      * a cache of Roo.data.Records.
10570      */
10571     readRecords : function(o){
10572         var sid = this.meta ? this.meta.id : null;
10573         var recordType = this.recordType, fields = recordType.prototype.fields;
10574         var records = [];
10575         var root = o;
10576             for(var i = 0; i < root.length; i++){
10577                     var n = root[i];
10578                 var values = {};
10579                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10580                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10581                 var f = fields.items[j];
10582                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10583                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10584                 v = f.convert(v);
10585                 values[f.name] = v;
10586             }
10587                 var record = new recordType(values, id);
10588                 record.json = n;
10589                 records[records.length] = record;
10590             }
10591             return {
10592                 records : records,
10593                 totalRecords : records.length
10594             };
10595     }
10596 });/*
10597  * - LGPL
10598  * * 
10599  */
10600
10601 /**
10602  * @class Roo.bootstrap.ComboBox
10603  * @extends Roo.bootstrap.TriggerField
10604  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10605  * @cfg {Boolean} append (true|false) default false
10606  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10607  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10608  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10609  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10610  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10611  * @constructor
10612  * Create a new ComboBox.
10613  * @param {Object} config Configuration options
10614  */
10615 Roo.bootstrap.ComboBox = function(config){
10616     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10617     this.addEvents({
10618         /**
10619          * @event expand
10620          * Fires when the dropdown list is expanded
10621              * @param {Roo.bootstrap.ComboBox} combo This combo box
10622              */
10623         'expand' : true,
10624         /**
10625          * @event collapse
10626          * Fires when the dropdown list is collapsed
10627              * @param {Roo.bootstrap.ComboBox} combo This combo box
10628              */
10629         'collapse' : true,
10630         /**
10631          * @event beforeselect
10632          * Fires before a list item is selected. Return false to cancel the selection.
10633              * @param {Roo.bootstrap.ComboBox} combo This combo box
10634              * @param {Roo.data.Record} record The data record returned from the underlying store
10635              * @param {Number} index The index of the selected item in the dropdown list
10636              */
10637         'beforeselect' : true,
10638         /**
10639          * @event select
10640          * Fires when a list item is selected
10641              * @param {Roo.bootstrap.ComboBox} combo This combo box
10642              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10643              * @param {Number} index The index of the selected item in the dropdown list
10644              */
10645         'select' : true,
10646         /**
10647          * @event beforequery
10648          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10649          * The event object passed has these properties:
10650              * @param {Roo.bootstrap.ComboBox} combo This combo box
10651              * @param {String} query The query
10652              * @param {Boolean} forceAll true to force "all" query
10653              * @param {Boolean} cancel true to cancel the query
10654              * @param {Object} e The query event object
10655              */
10656         'beforequery': true,
10657          /**
10658          * @event add
10659          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10660              * @param {Roo.bootstrap.ComboBox} combo This combo box
10661              */
10662         'add' : true,
10663         /**
10664          * @event edit
10665          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10666              * @param {Roo.bootstrap.ComboBox} combo This combo box
10667              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10668              */
10669         'edit' : true,
10670         /**
10671          * @event remove
10672          * Fires when the remove value from the combobox array
10673              * @param {Roo.bootstrap.ComboBox} combo This combo box
10674              */
10675         'remove' : true
10676         
10677     });
10678     
10679     this.item = [];
10680     this.tickItems = [];
10681     
10682     this.selectedIndex = -1;
10683     if(this.mode == 'local'){
10684         if(config.queryDelay === undefined){
10685             this.queryDelay = 10;
10686         }
10687         if(config.minChars === undefined){
10688             this.minChars = 0;
10689         }
10690     }
10691 };
10692
10693 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10694      
10695     /**
10696      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10697      * rendering into an Roo.Editor, defaults to false)
10698      */
10699     /**
10700      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10701      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10702      */
10703     /**
10704      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10705      */
10706     /**
10707      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10708      * the dropdown list (defaults to undefined, with no header element)
10709      */
10710
10711      /**
10712      * @cfg {String/Roo.Template} tpl The template to use to render the output
10713      */
10714      
10715      /**
10716      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10717      */
10718     listWidth: undefined,
10719     /**
10720      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10721      * mode = 'remote' or 'text' if mode = 'local')
10722      */
10723     displayField: undefined,
10724     /**
10725      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10726      * mode = 'remote' or 'value' if mode = 'local'). 
10727      * Note: use of a valueField requires the user make a selection
10728      * in order for a value to be mapped.
10729      */
10730     valueField: undefined,
10731     
10732     
10733     /**
10734      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10735      * field's data value (defaults to the underlying DOM element's name)
10736      */
10737     hiddenName: undefined,
10738     /**
10739      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10740      */
10741     listClass: '',
10742     /**
10743      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10744      */
10745     selectedClass: 'active',
10746     
10747     /**
10748      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10749      */
10750     shadow:'sides',
10751     /**
10752      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10753      * anchor positions (defaults to 'tl-bl')
10754      */
10755     listAlign: 'tl-bl?',
10756     /**
10757      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10758      */
10759     maxHeight: 300,
10760     /**
10761      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10762      * query specified by the allQuery config option (defaults to 'query')
10763      */
10764     triggerAction: 'query',
10765     /**
10766      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10767      * (defaults to 4, does not apply if editable = false)
10768      */
10769     minChars : 4,
10770     /**
10771      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10772      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10773      */
10774     typeAhead: false,
10775     /**
10776      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10777      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10778      */
10779     queryDelay: 500,
10780     /**
10781      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10782      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10783      */
10784     pageSize: 0,
10785     /**
10786      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10787      * when editable = true (defaults to false)
10788      */
10789     selectOnFocus:false,
10790     /**
10791      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10792      */
10793     queryParam: 'query',
10794     /**
10795      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10796      * when mode = 'remote' (defaults to 'Loading...')
10797      */
10798     loadingText: 'Loading...',
10799     /**
10800      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10801      */
10802     resizable: false,
10803     /**
10804      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10805      */
10806     handleHeight : 8,
10807     /**
10808      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10809      * traditional select (defaults to true)
10810      */
10811     editable: true,
10812     /**
10813      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10814      */
10815     allQuery: '',
10816     /**
10817      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10818      */
10819     mode: 'remote',
10820     /**
10821      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10822      * listWidth has a higher value)
10823      */
10824     minListWidth : 70,
10825     /**
10826      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10827      * allow the user to set arbitrary text into the field (defaults to false)
10828      */
10829     forceSelection:false,
10830     /**
10831      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10832      * if typeAhead = true (defaults to 250)
10833      */
10834     typeAheadDelay : 250,
10835     /**
10836      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10837      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10838      */
10839     valueNotFoundText : undefined,
10840     /**
10841      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10842      */
10843     blockFocus : false,
10844     
10845     /**
10846      * @cfg {Boolean} disableClear Disable showing of clear button.
10847      */
10848     disableClear : false,
10849     /**
10850      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10851      */
10852     alwaysQuery : false,
10853     
10854     /**
10855      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10856      */
10857     multiple : false,
10858     
10859     /**
10860      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
10861      */
10862     invalidClass : "has-warning",
10863     
10864     /**
10865      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
10866      */
10867     validClass : "has-success",
10868     
10869     //private
10870     addicon : false,
10871     editicon: false,
10872     
10873     page: 0,
10874     hasQuery: false,
10875     append: false,
10876     loadNext: false,
10877     autoFocus : true,
10878     tickable : false,
10879     btnPosition : 'right',
10880     triggerList : true,
10881     showToggleBtn : true,
10882     // element that contains real text value.. (when hidden is used..)
10883     
10884     getAutoCreate : function()
10885     {
10886         var cfg = false;
10887         
10888         /*
10889          *  Normal ComboBox
10890          */
10891         if(!this.tickable){
10892             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10893             return cfg;
10894         }
10895         
10896         /*
10897          *  ComboBox with tickable selections
10898          */
10899              
10900         var align = this.labelAlign || this.parentLabelAlign();
10901         
10902         cfg = {
10903             cls : 'form-group roo-combobox-tickable' //input-group
10904         };
10905         
10906         
10907         var buttons = {
10908             tag : 'div',
10909             cls : 'tickable-buttons',
10910             cn : [
10911                 {
10912                     tag : 'button',
10913                     type : 'button',
10914                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10915                     html : 'Edit'
10916                 },
10917                 {
10918                     tag : 'button',
10919                     type : 'button',
10920                     name : 'ok',
10921                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10922                     html : 'Done'
10923                 },
10924                 {
10925                     tag : 'button',
10926                     type : 'button',
10927                     name : 'cancel',
10928                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10929                     html : 'Cancel'
10930                 }
10931             ]
10932         };
10933         
10934         var _this = this;
10935         Roo.each(buttons.cn, function(c){
10936             if (_this.size) {
10937                 c.cls += ' btn-' + _this.size;
10938             }
10939
10940             if (_this.disabled) {
10941                 c.disabled = true;
10942             }
10943         });
10944         
10945         var box = {
10946             tag: 'div',
10947             cn: [
10948                 {
10949                     tag: 'input',
10950                     type : 'hidden',
10951                     cls: 'form-hidden-field'
10952                 },
10953                 {
10954                     tag: 'ul',
10955                     cls: 'select2-choices',
10956                     cn:[
10957                         {
10958                             tag: 'li',
10959                             cls: 'select2-search-field',
10960                             cn: [
10961
10962                                 buttons
10963                             ]
10964                         }
10965                     ]
10966                 }
10967             ]
10968         }
10969         
10970         var combobox = {
10971             cls: 'select2-container input-group select2-container-multi',
10972             cn: [
10973                 box
10974 //                {
10975 //                    tag: 'ul',
10976 //                    cls: 'typeahead typeahead-long dropdown-menu',
10977 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10978 //                }
10979             ]
10980         };
10981         
10982         if(this.hasFeedback && !this.allowBlank){
10983             
10984             var feedback = {
10985                 tag: 'span',
10986                 cls: 'glyphicon form-control-feedback'
10987             };
10988
10989             combobox.cn.push(feedback);
10990         }
10991         
10992         if (align ==='left' && this.fieldLabel.length) {
10993             
10994                 Roo.log("left and has label");
10995                 cfg.cn = [
10996                     
10997                     {
10998                         tag: 'label',
10999                         'for' :  id,
11000                         cls : 'control-label col-sm-' + this.labelWidth,
11001                         html : this.fieldLabel
11002                         
11003                     },
11004                     {
11005                         cls : "col-sm-" + (12 - this.labelWidth), 
11006                         cn: [
11007                             combobox
11008                         ]
11009                     }
11010                     
11011                 ];
11012         } else if ( this.fieldLabel.length) {
11013                 Roo.log(" label");
11014                  cfg.cn = [
11015                    
11016                     {
11017                         tag: 'label',
11018                         //cls : 'input-group-addon',
11019                         html : this.fieldLabel
11020                         
11021                     },
11022                     
11023                     combobox
11024                     
11025                 ];
11026
11027         } else {
11028             
11029                 Roo.log(" no label && no align");
11030                 cfg = combobox
11031                      
11032                 
11033         }
11034          
11035         var settings=this;
11036         ['xs','sm','md','lg'].map(function(size){
11037             if (settings[size]) {
11038                 cfg.cls += ' col-' + size + '-' + settings[size];
11039             }
11040         });
11041         
11042         return cfg;
11043         
11044     },
11045     
11046     // private
11047     initEvents: function()
11048     {
11049         
11050         if (!this.store) {
11051             throw "can not find store for combo";
11052         }
11053         this.store = Roo.factory(this.store, Roo.data);
11054         
11055         if(this.tickable){
11056             this.initTickableEvents();
11057             return;
11058         }
11059         
11060         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11061         
11062         if(this.hiddenName){
11063             
11064             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11065             
11066             this.hiddenField.dom.value =
11067                 this.hiddenValue !== undefined ? this.hiddenValue :
11068                 this.value !== undefined ? this.value : '';
11069
11070             // prevent input submission
11071             this.el.dom.removeAttribute('name');
11072             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11073              
11074              
11075         }
11076         //if(Roo.isGecko){
11077         //    this.el.dom.setAttribute('autocomplete', 'off');
11078         //}
11079         
11080         var cls = 'x-combo-list';
11081         
11082         //this.list = new Roo.Layer({
11083         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11084         //});
11085         
11086         var _this = this;
11087         
11088         (function(){
11089             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11090             _this.list.setWidth(lw);
11091         }).defer(100);
11092         
11093         this.list.on('mouseover', this.onViewOver, this);
11094         this.list.on('mousemove', this.onViewMove, this);
11095         
11096         this.list.on('scroll', this.onViewScroll, this);
11097         
11098         /*
11099         this.list.swallowEvent('mousewheel');
11100         this.assetHeight = 0;
11101
11102         if(this.title){
11103             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11104             this.assetHeight += this.header.getHeight();
11105         }
11106
11107         this.innerList = this.list.createChild({cls:cls+'-inner'});
11108         this.innerList.on('mouseover', this.onViewOver, this);
11109         this.innerList.on('mousemove', this.onViewMove, this);
11110         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11111         
11112         if(this.allowBlank && !this.pageSize && !this.disableClear){
11113             this.footer = this.list.createChild({cls:cls+'-ft'});
11114             this.pageTb = new Roo.Toolbar(this.footer);
11115            
11116         }
11117         if(this.pageSize){
11118             this.footer = this.list.createChild({cls:cls+'-ft'});
11119             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11120                     {pageSize: this.pageSize});
11121             
11122         }
11123         
11124         if (this.pageTb && this.allowBlank && !this.disableClear) {
11125             var _this = this;
11126             this.pageTb.add(new Roo.Toolbar.Fill(), {
11127                 cls: 'x-btn-icon x-btn-clear',
11128                 text: '&#160;',
11129                 handler: function()
11130                 {
11131                     _this.collapse();
11132                     _this.clearValue();
11133                     _this.onSelect(false, -1);
11134                 }
11135             });
11136         }
11137         if (this.footer) {
11138             this.assetHeight += this.footer.getHeight();
11139         }
11140         */
11141             
11142         if(!this.tpl){
11143             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11144         }
11145
11146         this.view = new Roo.View(this.list, this.tpl, {
11147             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11148         });
11149         //this.view.wrapEl.setDisplayed(false);
11150         this.view.on('click', this.onViewClick, this);
11151         
11152         
11153         
11154         this.store.on('beforeload', this.onBeforeLoad, this);
11155         this.store.on('load', this.onLoad, this);
11156         this.store.on('loadexception', this.onLoadException, this);
11157         /*
11158         if(this.resizable){
11159             this.resizer = new Roo.Resizable(this.list,  {
11160                pinned:true, handles:'se'
11161             });
11162             this.resizer.on('resize', function(r, w, h){
11163                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11164                 this.listWidth = w;
11165                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11166                 this.restrictHeight();
11167             }, this);
11168             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11169         }
11170         */
11171         if(!this.editable){
11172             this.editable = true;
11173             this.setEditable(false);
11174         }
11175         
11176         /*
11177         
11178         if (typeof(this.events.add.listeners) != 'undefined') {
11179             
11180             this.addicon = this.wrap.createChild(
11181                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11182        
11183             this.addicon.on('click', function(e) {
11184                 this.fireEvent('add', this);
11185             }, this);
11186         }
11187         if (typeof(this.events.edit.listeners) != 'undefined') {
11188             
11189             this.editicon = this.wrap.createChild(
11190                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11191             if (this.addicon) {
11192                 this.editicon.setStyle('margin-left', '40px');
11193             }
11194             this.editicon.on('click', function(e) {
11195                 
11196                 // we fire even  if inothing is selected..
11197                 this.fireEvent('edit', this, this.lastData );
11198                 
11199             }, this);
11200         }
11201         */
11202         
11203         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11204             "up" : function(e){
11205                 this.inKeyMode = true;
11206                 this.selectPrev();
11207             },
11208
11209             "down" : function(e){
11210                 if(!this.isExpanded()){
11211                     this.onTriggerClick();
11212                 }else{
11213                     this.inKeyMode = true;
11214                     this.selectNext();
11215                 }
11216             },
11217
11218             "enter" : function(e){
11219 //                this.onViewClick();
11220                 //return true;
11221                 this.collapse();
11222                 
11223                 if(this.fireEvent("specialkey", this, e)){
11224                     this.onViewClick(false);
11225                 }
11226                 
11227                 return true;
11228             },
11229
11230             "esc" : function(e){
11231                 this.collapse();
11232             },
11233
11234             "tab" : function(e){
11235                 this.collapse();
11236                 
11237                 if(this.fireEvent("specialkey", this, e)){
11238                     this.onViewClick(false);
11239                 }
11240                 
11241                 return true;
11242             },
11243
11244             scope : this,
11245
11246             doRelay : function(foo, bar, hname){
11247                 if(hname == 'down' || this.scope.isExpanded()){
11248                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11249                 }
11250                 return true;
11251             },
11252
11253             forceKeyDown: true
11254         });
11255         
11256         
11257         this.queryDelay = Math.max(this.queryDelay || 10,
11258                 this.mode == 'local' ? 10 : 250);
11259         
11260         
11261         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11262         
11263         if(this.typeAhead){
11264             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11265         }
11266         if(this.editable !== false){
11267             this.inputEl().on("keyup", this.onKeyUp, this);
11268         }
11269         if(this.forceSelection){
11270             this.inputEl().on('blur', this.doForce, this);
11271         }
11272         
11273         if(this.multiple){
11274             this.choices = this.el.select('ul.select2-choices', true).first();
11275             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11276         }
11277     },
11278     
11279     initTickableEvents: function()
11280     {   
11281         this.createList();
11282         
11283         if(this.hiddenName){
11284             
11285             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11286             
11287             this.hiddenField.dom.value =
11288                 this.hiddenValue !== undefined ? this.hiddenValue :
11289                 this.value !== undefined ? this.value : '';
11290
11291             // prevent input submission
11292             this.el.dom.removeAttribute('name');
11293             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11294              
11295              
11296         }
11297         
11298 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11299         
11300         this.choices = this.el.select('ul.select2-choices', true).first();
11301         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11302         if(this.triggerList){
11303             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11304         }
11305          
11306         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11307         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11308         
11309         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11310         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11311         
11312         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11313         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11314         
11315         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11316         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11317         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11318         
11319         this.okBtn.hide();
11320         this.cancelBtn.hide();
11321         
11322         var _this = this;
11323         
11324         (function(){
11325             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11326             _this.list.setWidth(lw);
11327         }).defer(100);
11328         
11329         this.list.on('mouseover', this.onViewOver, this);
11330         this.list.on('mousemove', this.onViewMove, this);
11331         
11332         this.list.on('scroll', this.onViewScroll, this);
11333         
11334         if(!this.tpl){
11335             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>';
11336         }
11337
11338         this.view = new Roo.View(this.list, this.tpl, {
11339             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11340         });
11341         
11342         //this.view.wrapEl.setDisplayed(false);
11343         this.view.on('click', this.onViewClick, this);
11344         
11345         
11346         
11347         this.store.on('beforeload', this.onBeforeLoad, this);
11348         this.store.on('load', this.onLoad, this);
11349         this.store.on('loadexception', this.onLoadException, this);
11350         
11351 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11352 //            "up" : function(e){
11353 //                this.inKeyMode = true;
11354 //                this.selectPrev();
11355 //            },
11356 //
11357 //            "down" : function(e){
11358 //                if(!this.isExpanded()){
11359 //                    this.onTriggerClick();
11360 //                }else{
11361 //                    this.inKeyMode = true;
11362 //                    this.selectNext();
11363 //                }
11364 //            },
11365 //
11366 //            "enter" : function(e){
11367 ////                this.onViewClick();
11368 //                //return true;
11369 //                this.collapse();
11370 //                
11371 //                if(this.fireEvent("specialkey", this, e)){
11372 //                    this.onViewClick(false);
11373 //                }
11374 //                
11375 //                return true;
11376 //            },
11377 //
11378 //            "esc" : function(e){
11379 //                this.collapse();
11380 //            },
11381 //
11382 //            "tab" : function(e){
11383 //                this.collapse();
11384 //                
11385 //                if(this.fireEvent("specialkey", this, e)){
11386 //                    this.onViewClick(false);
11387 //                }
11388 //                
11389 //                return true;
11390 //            },
11391 //
11392 //            scope : this,
11393 //
11394 //            doRelay : function(foo, bar, hname){
11395 //                if(hname == 'down' || this.scope.isExpanded()){
11396 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11397 //                }
11398 //                return true;
11399 //            },
11400 //
11401 //            forceKeyDown: true
11402 //        });
11403         
11404         
11405         this.queryDelay = Math.max(this.queryDelay || 10,
11406                 this.mode == 'local' ? 10 : 250);
11407         
11408         
11409         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11410         
11411         if(this.typeAhead){
11412             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11413         }
11414         
11415     },
11416
11417     onDestroy : function(){
11418         if(this.view){
11419             this.view.setStore(null);
11420             this.view.el.removeAllListeners();
11421             this.view.el.remove();
11422             this.view.purgeListeners();
11423         }
11424         if(this.list){
11425             this.list.dom.innerHTML  = '';
11426         }
11427         
11428         if(this.store){
11429             this.store.un('beforeload', this.onBeforeLoad, this);
11430             this.store.un('load', this.onLoad, this);
11431             this.store.un('loadexception', this.onLoadException, this);
11432         }
11433         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11434     },
11435
11436     // private
11437     fireKey : function(e){
11438         if(e.isNavKeyPress() && !this.list.isVisible()){
11439             this.fireEvent("specialkey", this, e);
11440         }
11441     },
11442
11443     // private
11444     onResize: function(w, h){
11445 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11446 //        
11447 //        if(typeof w != 'number'){
11448 //            // we do not handle it!?!?
11449 //            return;
11450 //        }
11451 //        var tw = this.trigger.getWidth();
11452 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11453 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11454 //        var x = w - tw;
11455 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11456 //            
11457 //        //this.trigger.setStyle('left', x+'px');
11458 //        
11459 //        if(this.list && this.listWidth === undefined){
11460 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11461 //            this.list.setWidth(lw);
11462 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11463 //        }
11464         
11465     
11466         
11467     },
11468
11469     /**
11470      * Allow or prevent the user from directly editing the field text.  If false is passed,
11471      * the user will only be able to select from the items defined in the dropdown list.  This method
11472      * is the runtime equivalent of setting the 'editable' config option at config time.
11473      * @param {Boolean} value True to allow the user to directly edit the field text
11474      */
11475     setEditable : function(value){
11476         if(value == this.editable){
11477             return;
11478         }
11479         this.editable = value;
11480         if(!value){
11481             this.inputEl().dom.setAttribute('readOnly', true);
11482             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11483             this.inputEl().addClass('x-combo-noedit');
11484         }else{
11485             this.inputEl().dom.setAttribute('readOnly', false);
11486             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11487             this.inputEl().removeClass('x-combo-noedit');
11488         }
11489     },
11490
11491     // private
11492     
11493     onBeforeLoad : function(combo,opts){
11494         if(!this.hasFocus){
11495             return;
11496         }
11497          if (!opts.add) {
11498             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11499          }
11500         this.restrictHeight();
11501         this.selectedIndex = -1;
11502     },
11503
11504     // private
11505     onLoad : function(){
11506         
11507         this.hasQuery = false;
11508         
11509         if(!this.hasFocus){
11510             return;
11511         }
11512         
11513         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11514             this.loading.hide();
11515         }
11516         
11517         if(this.store.getCount() > 0){
11518             this.expand();
11519 //            this.restrictHeight();
11520             if(this.lastQuery == this.allQuery){
11521                 if(this.editable && !this.tickable){
11522                     this.inputEl().dom.select();
11523                 }
11524                 
11525                 if(
11526                     !this.selectByValue(this.value, true) &&
11527                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11528                     this.store.lastOptions.add != true)
11529                 ){
11530                     this.select(0, true);
11531                 }
11532             }else{
11533                 if(this.autoFocus){
11534                     this.selectNext();
11535                 }
11536                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11537                     this.taTask.delay(this.typeAheadDelay);
11538                 }
11539             }
11540         }else{
11541             this.onEmptyResults();
11542         }
11543         
11544         //this.el.focus();
11545     },
11546     // private
11547     onLoadException : function()
11548     {
11549         this.hasQuery = false;
11550         
11551         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11552             this.loading.hide();
11553         }
11554         
11555         this.collapse();
11556         Roo.log(this.store.reader.jsonData);
11557         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11558             // fixme
11559             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11560         }
11561         
11562         
11563     },
11564     // private
11565     onTypeAhead : function(){
11566         if(this.store.getCount() > 0){
11567             var r = this.store.getAt(0);
11568             var newValue = r.data[this.displayField];
11569             var len = newValue.length;
11570             var selStart = this.getRawValue().length;
11571             
11572             if(selStart != len){
11573                 this.setRawValue(newValue);
11574                 this.selectText(selStart, newValue.length);
11575             }
11576         }
11577     },
11578
11579     // private
11580     onSelect : function(record, index){
11581         
11582         if(this.fireEvent('beforeselect', this, record, index) !== false){
11583         
11584             this.setFromData(index > -1 ? record.data : false);
11585             
11586             this.collapse();
11587             this.fireEvent('select', this, record, index);
11588         }
11589     },
11590
11591     /**
11592      * Returns the currently selected field value or empty string if no value is set.
11593      * @return {String} value The selected value
11594      */
11595     getValue : function(){
11596         
11597         if(this.multiple){
11598             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11599         }
11600         
11601         if(this.valueField){
11602             return typeof this.value != 'undefined' ? this.value : '';
11603         }else{
11604             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11605         }
11606     },
11607
11608     /**
11609      * Clears any text/value currently set in the field
11610      */
11611     clearValue : function(){
11612         if(this.hiddenField){
11613             this.hiddenField.dom.value = '';
11614         }
11615         this.value = '';
11616         this.setRawValue('');
11617         this.lastSelectionText = '';
11618         this.lastData = false;
11619         
11620     },
11621
11622     /**
11623      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11624      * will be displayed in the field.  If the value does not match the data value of an existing item,
11625      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11626      * Otherwise the field will be blank (although the value will still be set).
11627      * @param {String} value The value to match
11628      */
11629     setValue : function(v){
11630         if(this.multiple){
11631             this.syncValue();
11632             return;
11633         }
11634         
11635         var text = v;
11636         if(this.valueField){
11637             var r = this.findRecord(this.valueField, v);
11638             if(r){
11639                 text = r.data[this.displayField];
11640             }else if(this.valueNotFoundText !== undefined){
11641                 text = this.valueNotFoundText;
11642             }
11643         }
11644         this.lastSelectionText = text;
11645         if(this.hiddenField){
11646             this.hiddenField.dom.value = v;
11647         }
11648         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11649         this.value = v;
11650     },
11651     /**
11652      * @property {Object} the last set data for the element
11653      */
11654     
11655     lastData : false,
11656     /**
11657      * Sets the value of the field based on a object which is related to the record format for the store.
11658      * @param {Object} value the value to set as. or false on reset?
11659      */
11660     setFromData : function(o){
11661         
11662         if(this.multiple){
11663             this.addItem(o);
11664             return;
11665         }
11666             
11667         var dv = ''; // display value
11668         var vv = ''; // value value..
11669         this.lastData = o;
11670         if (this.displayField) {
11671             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11672         } else {
11673             // this is an error condition!!!
11674             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11675         }
11676         
11677         if(this.valueField){
11678             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11679         }
11680         
11681         if(this.hiddenField){
11682             this.hiddenField.dom.value = vv;
11683             
11684             this.lastSelectionText = dv;
11685             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11686             this.value = vv;
11687             return;
11688         }
11689         // no hidden field.. - we store the value in 'value', but still display
11690         // display field!!!!
11691         this.lastSelectionText = dv;
11692         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11693         this.value = vv;
11694         
11695         
11696     },
11697     // private
11698     reset : function(){
11699         // overridden so that last data is reset..
11700         this.setValue(this.originalValue);
11701         this.clearInvalid();
11702         this.lastData = false;
11703         if (this.view) {
11704             this.view.clearSelections();
11705         }
11706     },
11707     // private
11708     findRecord : function(prop, value){
11709         var record;
11710         if(this.store.getCount() > 0){
11711             this.store.each(function(r){
11712                 if(r.data[prop] == value){
11713                     record = r;
11714                     return false;
11715                 }
11716                 return true;
11717             });
11718         }
11719         return record;
11720     },
11721     
11722     getName: function()
11723     {
11724         // returns hidden if it's set..
11725         if (!this.rendered) {return ''};
11726         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11727         
11728     },
11729     // private
11730     onViewMove : function(e, t){
11731         this.inKeyMode = false;
11732     },
11733
11734     // private
11735     onViewOver : function(e, t){
11736         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11737             return;
11738         }
11739         var item = this.view.findItemFromChild(t);
11740         
11741         if(item){
11742             var index = this.view.indexOf(item);
11743             this.select(index, false);
11744         }
11745     },
11746
11747     // private
11748     onViewClick : function(view, doFocus, el, e)
11749     {
11750         var index = this.view.getSelectedIndexes()[0];
11751         
11752         var r = this.store.getAt(index);
11753         
11754         if(this.tickable){
11755             
11756             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11757                 return;
11758             }
11759             
11760             var rm = false;
11761             var _this = this;
11762             
11763             Roo.each(this.tickItems, function(v,k){
11764                 
11765                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11766                     _this.tickItems.splice(k, 1);
11767                     rm = true;
11768                     return;
11769                 }
11770             });
11771             
11772             if(rm){
11773                 return;
11774             }
11775             
11776             this.tickItems.push(r.data);
11777             return;
11778         }
11779         
11780         if(r){
11781             this.onSelect(r, index);
11782         }
11783         if(doFocus !== false && !this.blockFocus){
11784             this.inputEl().focus();
11785         }
11786     },
11787
11788     // private
11789     restrictHeight : function(){
11790         //this.innerList.dom.style.height = '';
11791         //var inner = this.innerList.dom;
11792         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11793         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11794         //this.list.beginUpdate();
11795         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11796         this.list.alignTo(this.inputEl(), this.listAlign);
11797         this.list.alignTo(this.inputEl(), this.listAlign);
11798         //this.list.endUpdate();
11799     },
11800
11801     // private
11802     onEmptyResults : function(){
11803         this.collapse();
11804     },
11805
11806     /**
11807      * Returns true if the dropdown list is expanded, else false.
11808      */
11809     isExpanded : function(){
11810         return this.list.isVisible();
11811     },
11812
11813     /**
11814      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11815      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11816      * @param {String} value The data value of the item to select
11817      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11818      * selected item if it is not currently in view (defaults to true)
11819      * @return {Boolean} True if the value matched an item in the list, else false
11820      */
11821     selectByValue : function(v, scrollIntoView){
11822         if(v !== undefined && v !== null){
11823             var r = this.findRecord(this.valueField || this.displayField, v);
11824             if(r){
11825                 this.select(this.store.indexOf(r), scrollIntoView);
11826                 return true;
11827             }
11828         }
11829         return false;
11830     },
11831
11832     /**
11833      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11834      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11835      * @param {Number} index The zero-based index of the list item to select
11836      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11837      * selected item if it is not currently in view (defaults to true)
11838      */
11839     select : function(index, scrollIntoView){
11840         this.selectedIndex = index;
11841         this.view.select(index);
11842         if(scrollIntoView !== false){
11843             var el = this.view.getNode(index);
11844             if(el && !this.multiple && !this.tickable){
11845                 this.list.scrollChildIntoView(el, false);
11846             }
11847         }
11848     },
11849
11850     // private
11851     selectNext : function(){
11852         var ct = this.store.getCount();
11853         if(ct > 0){
11854             if(this.selectedIndex == -1){
11855                 this.select(0);
11856             }else if(this.selectedIndex < ct-1){
11857                 this.select(this.selectedIndex+1);
11858             }
11859         }
11860     },
11861
11862     // private
11863     selectPrev : function(){
11864         var ct = this.store.getCount();
11865         if(ct > 0){
11866             if(this.selectedIndex == -1){
11867                 this.select(0);
11868             }else if(this.selectedIndex != 0){
11869                 this.select(this.selectedIndex-1);
11870             }
11871         }
11872     },
11873
11874     // private
11875     onKeyUp : function(e){
11876         if(this.editable !== false && !e.isSpecialKey()){
11877             this.lastKey = e.getKey();
11878             this.dqTask.delay(this.queryDelay);
11879         }
11880     },
11881
11882     // private
11883     validateBlur : function(){
11884         return !this.list || !this.list.isVisible();   
11885     },
11886
11887     // private
11888     initQuery : function(){
11889         this.doQuery(this.getRawValue());
11890     },
11891
11892     // private
11893     doForce : function(){
11894         if(this.inputEl().dom.value.length > 0){
11895             this.inputEl().dom.value =
11896                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11897              
11898         }
11899     },
11900
11901     /**
11902      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11903      * query allowing the query action to be canceled if needed.
11904      * @param {String} query The SQL query to execute
11905      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11906      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11907      * saved in the current store (defaults to false)
11908      */
11909     doQuery : function(q, forceAll){
11910         
11911         if(q === undefined || q === null){
11912             q = '';
11913         }
11914         var qe = {
11915             query: q,
11916             forceAll: forceAll,
11917             combo: this,
11918             cancel:false
11919         };
11920         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11921             return false;
11922         }
11923         q = qe.query;
11924         
11925         forceAll = qe.forceAll;
11926         if(forceAll === true || (q.length >= this.minChars)){
11927             
11928             this.hasQuery = true;
11929             
11930             if(this.lastQuery != q || this.alwaysQuery){
11931                 this.lastQuery = q;
11932                 if(this.mode == 'local'){
11933                     this.selectedIndex = -1;
11934                     if(forceAll){
11935                         this.store.clearFilter();
11936                     }else{
11937                         this.store.filter(this.displayField, q);
11938                     }
11939                     this.onLoad();
11940                 }else{
11941                     this.store.baseParams[this.queryParam] = q;
11942                     
11943                     var options = {params : this.getParams(q)};
11944                     
11945                     if(this.loadNext){
11946                         options.add = true;
11947                         options.params.start = this.page * this.pageSize;
11948                     }
11949                     
11950                     this.store.load(options);
11951                     /*
11952                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11953                      *  we should expand the list on onLoad
11954                      *  so command out it
11955                      */
11956 //                    this.expand();
11957                 }
11958             }else{
11959                 this.selectedIndex = -1;
11960                 this.onLoad();   
11961             }
11962         }
11963         
11964         this.loadNext = false;
11965     },
11966
11967     // private
11968     getParams : function(q){
11969         var p = {};
11970         //p[this.queryParam] = q;
11971         
11972         if(this.pageSize){
11973             p.start = 0;
11974             p.limit = this.pageSize;
11975         }
11976         return p;
11977     },
11978
11979     /**
11980      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11981      */
11982     collapse : function(){
11983         if(!this.isExpanded()){
11984             return;
11985         }
11986         
11987         this.list.hide();
11988         
11989         if(this.tickable){
11990             this.hasFocus = false;
11991             this.okBtn.hide();
11992             this.cancelBtn.hide();
11993             this.trigger.show();
11994         }
11995         
11996         Roo.get(document).un('mousedown', this.collapseIf, this);
11997         Roo.get(document).un('mousewheel', this.collapseIf, this);
11998         if (!this.editable) {
11999             Roo.get(document).un('keydown', this.listKeyPress, this);
12000         }
12001         this.fireEvent('collapse', this);
12002     },
12003
12004     // private
12005     collapseIf : function(e){
12006         var in_combo  = e.within(this.el);
12007         var in_list =  e.within(this.list);
12008         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12009         
12010         if (in_combo || in_list || is_list) {
12011             //e.stopPropagation();
12012             return;
12013         }
12014         
12015         if(this.tickable){
12016             this.onTickableFooterButtonClick(e, false, false);
12017         }
12018
12019         this.collapse();
12020         
12021     },
12022
12023     /**
12024      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12025      */
12026     expand : function(){
12027        
12028         if(this.isExpanded() || !this.hasFocus){
12029             return;
12030         }
12031         
12032         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12033         this.list.setWidth(lw);
12034         
12035         
12036          Roo.log('expand');
12037         
12038         this.list.show();
12039         
12040         this.restrictHeight();
12041         
12042         if(this.tickable){
12043             
12044             this.tickItems = Roo.apply([], this.item);
12045             
12046             this.okBtn.show();
12047             this.cancelBtn.show();
12048             this.trigger.hide();
12049             
12050         }
12051         
12052         Roo.get(document).on('mousedown', this.collapseIf, this);
12053         Roo.get(document).on('mousewheel', this.collapseIf, this);
12054         if (!this.editable) {
12055             Roo.get(document).on('keydown', this.listKeyPress, this);
12056         }
12057         
12058         this.fireEvent('expand', this);
12059     },
12060
12061     // private
12062     // Implements the default empty TriggerField.onTriggerClick function
12063     onTriggerClick : function(e)
12064     {
12065         Roo.log('trigger click');
12066         
12067         if(this.disabled || !this.triggerList){
12068             return;
12069         }
12070         
12071         this.page = 0;
12072         this.loadNext = false;
12073         
12074         if(this.isExpanded()){
12075             this.collapse();
12076             if (!this.blockFocus) {
12077                 this.inputEl().focus();
12078             }
12079             
12080         }else {
12081             this.hasFocus = true;
12082             if(this.triggerAction == 'all') {
12083                 this.doQuery(this.allQuery, true);
12084             } else {
12085                 this.doQuery(this.getRawValue());
12086             }
12087             if (!this.blockFocus) {
12088                 this.inputEl().focus();
12089             }
12090         }
12091     },
12092     
12093     onTickableTriggerClick : function(e)
12094     {
12095         if(this.disabled){
12096             return;
12097         }
12098         
12099         this.page = 0;
12100         this.loadNext = false;
12101         this.hasFocus = true;
12102         
12103         if(this.triggerAction == 'all') {
12104             this.doQuery(this.allQuery, true);
12105         } else {
12106             this.doQuery(this.getRawValue());
12107         }
12108     },
12109     
12110     onSearchFieldClick : function(e)
12111     {
12112         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12113             this.onTickableFooterButtonClick(e, false, false);
12114             return;
12115         }
12116         
12117         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12118             return;
12119         }
12120         
12121         this.page = 0;
12122         this.loadNext = false;
12123         this.hasFocus = true;
12124         
12125         if(this.triggerAction == 'all') {
12126             this.doQuery(this.allQuery, true);
12127         } else {
12128             this.doQuery(this.getRawValue());
12129         }
12130     },
12131     
12132     listKeyPress : function(e)
12133     {
12134         //Roo.log('listkeypress');
12135         // scroll to first matching element based on key pres..
12136         if (e.isSpecialKey()) {
12137             return false;
12138         }
12139         var k = String.fromCharCode(e.getKey()).toUpperCase();
12140         //Roo.log(k);
12141         var match  = false;
12142         var csel = this.view.getSelectedNodes();
12143         var cselitem = false;
12144         if (csel.length) {
12145             var ix = this.view.indexOf(csel[0]);
12146             cselitem  = this.store.getAt(ix);
12147             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12148                 cselitem = false;
12149             }
12150             
12151         }
12152         
12153         this.store.each(function(v) { 
12154             if (cselitem) {
12155                 // start at existing selection.
12156                 if (cselitem.id == v.id) {
12157                     cselitem = false;
12158                 }
12159                 return true;
12160             }
12161                 
12162             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12163                 match = this.store.indexOf(v);
12164                 return false;
12165             }
12166             return true;
12167         }, this);
12168         
12169         if (match === false) {
12170             return true; // no more action?
12171         }
12172         // scroll to?
12173         this.view.select(match);
12174         var sn = Roo.get(this.view.getSelectedNodes()[0])
12175         sn.scrollIntoView(sn.dom.parentNode, false);
12176     },
12177     
12178     onViewScroll : function(e, t){
12179         
12180         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){
12181             return;
12182         }
12183         
12184         this.hasQuery = true;
12185         
12186         this.loading = this.list.select('.loading', true).first();
12187         
12188         if(this.loading === null){
12189             this.list.createChild({
12190                 tag: 'div',
12191                 cls: 'loading select2-more-results select2-active',
12192                 html: 'Loading more results...'
12193             })
12194             
12195             this.loading = this.list.select('.loading', true).first();
12196             
12197             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12198             
12199             this.loading.hide();
12200         }
12201         
12202         this.loading.show();
12203         
12204         var _combo = this;
12205         
12206         this.page++;
12207         this.loadNext = true;
12208         
12209         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12210         
12211         return;
12212     },
12213     
12214     addItem : function(o)
12215     {   
12216         var dv = ''; // display value
12217         
12218         if (this.displayField) {
12219             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12220         } else {
12221             // this is an error condition!!!
12222             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12223         }
12224         
12225         if(!dv.length){
12226             return;
12227         }
12228         
12229         var choice = this.choices.createChild({
12230             tag: 'li',
12231             cls: 'select2-search-choice',
12232             cn: [
12233                 {
12234                     tag: 'div',
12235                     html: dv
12236                 },
12237                 {
12238                     tag: 'a',
12239                     href: '#',
12240                     cls: 'select2-search-choice-close',
12241                     tabindex: '-1'
12242                 }
12243             ]
12244             
12245         }, this.searchField);
12246         
12247         var close = choice.select('a.select2-search-choice-close', true).first()
12248         
12249         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12250         
12251         this.item.push(o);
12252         
12253         this.lastData = o;
12254         
12255         this.syncValue();
12256         
12257         this.inputEl().dom.value = '';
12258         
12259         this.validate();
12260     },
12261     
12262     onRemoveItem : function(e, _self, o)
12263     {
12264         e.preventDefault();
12265         
12266         this.lastItem = Roo.apply([], this.item);
12267         
12268         var index = this.item.indexOf(o.data) * 1;
12269         
12270         if( index < 0){
12271             Roo.log('not this item?!');
12272             return;
12273         }
12274         
12275         this.item.splice(index, 1);
12276         o.item.remove();
12277         
12278         this.syncValue();
12279         
12280         this.fireEvent('remove', this, e);
12281         
12282         this.validate();
12283         
12284     },
12285     
12286     syncValue : function()
12287     {
12288         if(!this.item.length){
12289             this.clearValue();
12290             return;
12291         }
12292             
12293         var value = [];
12294         var _this = this;
12295         Roo.each(this.item, function(i){
12296             if(_this.valueField){
12297                 value.push(i[_this.valueField]);
12298                 return;
12299             }
12300
12301             value.push(i);
12302         });
12303
12304         this.value = value.join(',');
12305
12306         if(this.hiddenField){
12307             this.hiddenField.dom.value = this.value;
12308         }
12309     },
12310     
12311     clearItem : function()
12312     {
12313         if(!this.multiple){
12314             return;
12315         }
12316         
12317         this.item = [];
12318         
12319         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12320            c.remove();
12321         });
12322         
12323         this.syncValue();
12324         
12325         this.validate();
12326     },
12327     
12328     inputEl: function ()
12329     {
12330         if(this.tickable){
12331             return this.searchField;
12332         }
12333         return this.el.select('input.form-control',true).first();
12334     },
12335     
12336     
12337     onTickableFooterButtonClick : function(e, btn, el)
12338     {
12339         e.preventDefault();
12340         
12341         this.lastItem = Roo.apply([], this.item);
12342         
12343         if(btn && btn.name == 'cancel'){
12344             this.tickItems = Roo.apply([], this.item);
12345             this.collapse();
12346             return;
12347         }
12348         
12349         this.clearItem();
12350         
12351         var _this = this;
12352         
12353         Roo.each(this.tickItems, function(o){
12354             _this.addItem(o);
12355         });
12356         
12357         this.collapse();
12358         
12359     },
12360     
12361     validate : function()
12362     {
12363         var v = this.getRawValue();
12364         
12365         if(this.multiple){
12366             v = this.getValue();
12367         }
12368         
12369         if(this.disabled || this.allowBlank || v.length){
12370             this.markValid();
12371             return true;
12372         }
12373         
12374         this.markInvalid();
12375         return false;
12376     }
12377     
12378     
12379
12380     /** 
12381     * @cfg {Boolean} grow 
12382     * @hide 
12383     */
12384     /** 
12385     * @cfg {Number} growMin 
12386     * @hide 
12387     */
12388     /** 
12389     * @cfg {Number} growMax 
12390     * @hide 
12391     */
12392     /**
12393      * @hide
12394      * @method autoSize
12395      */
12396 });
12397 /*
12398  * Based on:
12399  * Ext JS Library 1.1.1
12400  * Copyright(c) 2006-2007, Ext JS, LLC.
12401  *
12402  * Originally Released Under LGPL - original licence link has changed is not relivant.
12403  *
12404  * Fork - LGPL
12405  * <script type="text/javascript">
12406  */
12407
12408 /**
12409  * @class Roo.View
12410  * @extends Roo.util.Observable
12411  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12412  * This class also supports single and multi selection modes. <br>
12413  * Create a data model bound view:
12414  <pre><code>
12415  var store = new Roo.data.Store(...);
12416
12417  var view = new Roo.View({
12418     el : "my-element",
12419     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12420  
12421     singleSelect: true,
12422     selectedClass: "ydataview-selected",
12423     store: store
12424  });
12425
12426  // listen for node click?
12427  view.on("click", function(vw, index, node, e){
12428  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12429  });
12430
12431  // load XML data
12432  dataModel.load("foobar.xml");
12433  </code></pre>
12434  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12435  * <br><br>
12436  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12437  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12438  * 
12439  * Note: old style constructor is still suported (container, template, config)
12440  * 
12441  * @constructor
12442  * Create a new View
12443  * @param {Object} config The config object
12444  * 
12445  */
12446 Roo.View = function(config, depreciated_tpl, depreciated_config){
12447     
12448     this.parent = false;
12449     
12450     if (typeof(depreciated_tpl) == 'undefined') {
12451         // new way.. - universal constructor.
12452         Roo.apply(this, config);
12453         this.el  = Roo.get(this.el);
12454     } else {
12455         // old format..
12456         this.el  = Roo.get(config);
12457         this.tpl = depreciated_tpl;
12458         Roo.apply(this, depreciated_config);
12459     }
12460     this.wrapEl  = this.el.wrap().wrap();
12461     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12462     
12463     
12464     if(typeof(this.tpl) == "string"){
12465         this.tpl = new Roo.Template(this.tpl);
12466     } else {
12467         // support xtype ctors..
12468         this.tpl = new Roo.factory(this.tpl, Roo);
12469     }
12470     
12471     
12472     this.tpl.compile();
12473     
12474     /** @private */
12475     this.addEvents({
12476         /**
12477          * @event beforeclick
12478          * Fires before a click is processed. Returns false to cancel the default action.
12479          * @param {Roo.View} this
12480          * @param {Number} index The index of the target node
12481          * @param {HTMLElement} node The target node
12482          * @param {Roo.EventObject} e The raw event object
12483          */
12484             "beforeclick" : true,
12485         /**
12486          * @event click
12487          * Fires when a template node is clicked.
12488          * @param {Roo.View} this
12489          * @param {Number} index The index of the target node
12490          * @param {HTMLElement} node The target node
12491          * @param {Roo.EventObject} e The raw event object
12492          */
12493             "click" : true,
12494         /**
12495          * @event dblclick
12496          * Fires when a template node is double clicked.
12497          * @param {Roo.View} this
12498          * @param {Number} index The index of the target node
12499          * @param {HTMLElement} node The target node
12500          * @param {Roo.EventObject} e The raw event object
12501          */
12502             "dblclick" : true,
12503         /**
12504          * @event contextmenu
12505          * Fires when a template node is right clicked.
12506          * @param {Roo.View} this
12507          * @param {Number} index The index of the target node
12508          * @param {HTMLElement} node The target node
12509          * @param {Roo.EventObject} e The raw event object
12510          */
12511             "contextmenu" : true,
12512         /**
12513          * @event selectionchange
12514          * Fires when the selected nodes change.
12515          * @param {Roo.View} this
12516          * @param {Array} selections Array of the selected nodes
12517          */
12518             "selectionchange" : true,
12519     
12520         /**
12521          * @event beforeselect
12522          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12523          * @param {Roo.View} this
12524          * @param {HTMLElement} node The node to be selected
12525          * @param {Array} selections Array of currently selected nodes
12526          */
12527             "beforeselect" : true,
12528         /**
12529          * @event preparedata
12530          * Fires on every row to render, to allow you to change the data.
12531          * @param {Roo.View} this
12532          * @param {Object} data to be rendered (change this)
12533          */
12534           "preparedata" : true
12535           
12536           
12537         });
12538
12539
12540
12541     this.el.on({
12542         "click": this.onClick,
12543         "dblclick": this.onDblClick,
12544         "contextmenu": this.onContextMenu,
12545         scope:this
12546     });
12547
12548     this.selections = [];
12549     this.nodes = [];
12550     this.cmp = new Roo.CompositeElementLite([]);
12551     if(this.store){
12552         this.store = Roo.factory(this.store, Roo.data);
12553         this.setStore(this.store, true);
12554     }
12555     
12556     if ( this.footer && this.footer.xtype) {
12557            
12558          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12559         
12560         this.footer.dataSource = this.store
12561         this.footer.container = fctr;
12562         this.footer = Roo.factory(this.footer, Roo);
12563         fctr.insertFirst(this.el);
12564         
12565         // this is a bit insane - as the paging toolbar seems to detach the el..
12566 //        dom.parentNode.parentNode.parentNode
12567          // they get detached?
12568     }
12569     
12570     
12571     Roo.View.superclass.constructor.call(this);
12572     
12573     
12574 };
12575
12576 Roo.extend(Roo.View, Roo.util.Observable, {
12577     
12578      /**
12579      * @cfg {Roo.data.Store} store Data store to load data from.
12580      */
12581     store : false,
12582     
12583     /**
12584      * @cfg {String|Roo.Element} el The container element.
12585      */
12586     el : '',
12587     
12588     /**
12589      * @cfg {String|Roo.Template} tpl The template used by this View 
12590      */
12591     tpl : false,
12592     /**
12593      * @cfg {String} dataName the named area of the template to use as the data area
12594      *                          Works with domtemplates roo-name="name"
12595      */
12596     dataName: false,
12597     /**
12598      * @cfg {String} selectedClass The css class to add to selected nodes
12599      */
12600     selectedClass : "x-view-selected",
12601      /**
12602      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12603      */
12604     emptyText : "",
12605     
12606     /**
12607      * @cfg {String} text to display on mask (default Loading)
12608      */
12609     mask : false,
12610     /**
12611      * @cfg {Boolean} multiSelect Allow multiple selection
12612      */
12613     multiSelect : false,
12614     /**
12615      * @cfg {Boolean} singleSelect Allow single selection
12616      */
12617     singleSelect:  false,
12618     
12619     /**
12620      * @cfg {Boolean} toggleSelect - selecting 
12621      */
12622     toggleSelect : false,
12623     
12624     /**
12625      * @cfg {Boolean} tickable - selecting 
12626      */
12627     tickable : false,
12628     
12629     /**
12630      * Returns the element this view is bound to.
12631      * @return {Roo.Element}
12632      */
12633     getEl : function(){
12634         return this.wrapEl;
12635     },
12636     
12637     
12638
12639     /**
12640      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12641      */
12642     refresh : function(){
12643         //Roo.log('refresh');
12644         var t = this.tpl;
12645         
12646         // if we are using something like 'domtemplate', then
12647         // the what gets used is:
12648         // t.applySubtemplate(NAME, data, wrapping data..)
12649         // the outer template then get' applied with
12650         //     the store 'extra data'
12651         // and the body get's added to the
12652         //      roo-name="data" node?
12653         //      <span class='roo-tpl-{name}'></span> ?????
12654         
12655         
12656         
12657         this.clearSelections();
12658         this.el.update("");
12659         var html = [];
12660         var records = this.store.getRange();
12661         if(records.length < 1) {
12662             
12663             // is this valid??  = should it render a template??
12664             
12665             this.el.update(this.emptyText);
12666             return;
12667         }
12668         var el = this.el;
12669         if (this.dataName) {
12670             this.el.update(t.apply(this.store.meta)); //????
12671             el = this.el.child('.roo-tpl-' + this.dataName);
12672         }
12673         
12674         for(var i = 0, len = records.length; i < len; i++){
12675             var data = this.prepareData(records[i].data, i, records[i]);
12676             this.fireEvent("preparedata", this, data, i, records[i]);
12677             
12678             var d = Roo.apply({}, data);
12679             
12680             if(this.tickable){
12681                 Roo.apply(d, {'roo-id' : Roo.id()});
12682                 
12683                 var _this = this;
12684             
12685                 Roo.each(this.parent.item, function(item){
12686                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12687                         return;
12688                     }
12689                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12690                 });
12691             }
12692             
12693             html[html.length] = Roo.util.Format.trim(
12694                 this.dataName ?
12695                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12696                     t.apply(d)
12697             );
12698         }
12699         
12700         
12701         
12702         el.update(html.join(""));
12703         this.nodes = el.dom.childNodes;
12704         this.updateIndexes(0);
12705     },
12706     
12707
12708     /**
12709      * Function to override to reformat the data that is sent to
12710      * the template for each node.
12711      * DEPRICATED - use the preparedata event handler.
12712      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12713      * a JSON object for an UpdateManager bound view).
12714      */
12715     prepareData : function(data, index, record)
12716     {
12717         this.fireEvent("preparedata", this, data, index, record);
12718         return data;
12719     },
12720
12721     onUpdate : function(ds, record){
12722         // Roo.log('on update');   
12723         this.clearSelections();
12724         var index = this.store.indexOf(record);
12725         var n = this.nodes[index];
12726         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12727         n.parentNode.removeChild(n);
12728         this.updateIndexes(index, index);
12729     },
12730
12731     
12732     
12733 // --------- FIXME     
12734     onAdd : function(ds, records, index)
12735     {
12736         //Roo.log(['on Add', ds, records, index] );        
12737         this.clearSelections();
12738         if(this.nodes.length == 0){
12739             this.refresh();
12740             return;
12741         }
12742         var n = this.nodes[index];
12743         for(var i = 0, len = records.length; i < len; i++){
12744             var d = this.prepareData(records[i].data, i, records[i]);
12745             if(n){
12746                 this.tpl.insertBefore(n, d);
12747             }else{
12748                 
12749                 this.tpl.append(this.el, d);
12750             }
12751         }
12752         this.updateIndexes(index);
12753     },
12754
12755     onRemove : function(ds, record, index){
12756        // Roo.log('onRemove');
12757         this.clearSelections();
12758         var el = this.dataName  ?
12759             this.el.child('.roo-tpl-' + this.dataName) :
12760             this.el; 
12761         
12762         el.dom.removeChild(this.nodes[index]);
12763         this.updateIndexes(index);
12764     },
12765
12766     /**
12767      * Refresh an individual node.
12768      * @param {Number} index
12769      */
12770     refreshNode : function(index){
12771         this.onUpdate(this.store, this.store.getAt(index));
12772     },
12773
12774     updateIndexes : function(startIndex, endIndex){
12775         var ns = this.nodes;
12776         startIndex = startIndex || 0;
12777         endIndex = endIndex || ns.length - 1;
12778         for(var i = startIndex; i <= endIndex; i++){
12779             ns[i].nodeIndex = i;
12780         }
12781     },
12782
12783     /**
12784      * Changes the data store this view uses and refresh the view.
12785      * @param {Store} store
12786      */
12787     setStore : function(store, initial){
12788         if(!initial && this.store){
12789             this.store.un("datachanged", this.refresh);
12790             this.store.un("add", this.onAdd);
12791             this.store.un("remove", this.onRemove);
12792             this.store.un("update", this.onUpdate);
12793             this.store.un("clear", this.refresh);
12794             this.store.un("beforeload", this.onBeforeLoad);
12795             this.store.un("load", this.onLoad);
12796             this.store.un("loadexception", this.onLoad);
12797         }
12798         if(store){
12799           
12800             store.on("datachanged", this.refresh, this);
12801             store.on("add", this.onAdd, this);
12802             store.on("remove", this.onRemove, this);
12803             store.on("update", this.onUpdate, this);
12804             store.on("clear", this.refresh, this);
12805             store.on("beforeload", this.onBeforeLoad, this);
12806             store.on("load", this.onLoad, this);
12807             store.on("loadexception", this.onLoad, this);
12808         }
12809         
12810         if(store){
12811             this.refresh();
12812         }
12813     },
12814     /**
12815      * onbeforeLoad - masks the loading area.
12816      *
12817      */
12818     onBeforeLoad : function(store,opts)
12819     {
12820          //Roo.log('onBeforeLoad');   
12821         if (!opts.add) {
12822             this.el.update("");
12823         }
12824         this.el.mask(this.mask ? this.mask : "Loading" ); 
12825     },
12826     onLoad : function ()
12827     {
12828         this.el.unmask();
12829     },
12830     
12831
12832     /**
12833      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12834      * @param {HTMLElement} node
12835      * @return {HTMLElement} The template node
12836      */
12837     findItemFromChild : function(node){
12838         var el = this.dataName  ?
12839             this.el.child('.roo-tpl-' + this.dataName,true) :
12840             this.el.dom; 
12841         
12842         if(!node || node.parentNode == el){
12843                     return node;
12844             }
12845             var p = node.parentNode;
12846             while(p && p != el){
12847             if(p.parentNode == el){
12848                 return p;
12849             }
12850             p = p.parentNode;
12851         }
12852             return null;
12853     },
12854
12855     /** @ignore */
12856     onClick : function(e){
12857         var item = this.findItemFromChild(e.getTarget());
12858         if(item){
12859             var index = this.indexOf(item);
12860             if(this.onItemClick(item, index, e) !== false){
12861                 this.fireEvent("click", this, index, item, e);
12862             }
12863         }else{
12864             this.clearSelections();
12865         }
12866     },
12867
12868     /** @ignore */
12869     onContextMenu : function(e){
12870         var item = this.findItemFromChild(e.getTarget());
12871         if(item){
12872             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12873         }
12874     },
12875
12876     /** @ignore */
12877     onDblClick : function(e){
12878         var item = this.findItemFromChild(e.getTarget());
12879         if(item){
12880             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12881         }
12882     },
12883
12884     onItemClick : function(item, index, e)
12885     {
12886         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12887             return false;
12888         }
12889         if (this.toggleSelect) {
12890             var m = this.isSelected(item) ? 'unselect' : 'select';
12891             //Roo.log(m);
12892             var _t = this;
12893             _t[m](item, true, false);
12894             return true;
12895         }
12896         if(this.multiSelect || this.singleSelect){
12897             if(this.multiSelect && e.shiftKey && this.lastSelection){
12898                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12899             }else{
12900                 this.select(item, this.multiSelect && e.ctrlKey);
12901                 this.lastSelection = item;
12902             }
12903             
12904             if(!this.tickable){
12905                 e.preventDefault();
12906             }
12907             
12908         }
12909         return true;
12910     },
12911
12912     /**
12913      * Get the number of selected nodes.
12914      * @return {Number}
12915      */
12916     getSelectionCount : function(){
12917         return this.selections.length;
12918     },
12919
12920     /**
12921      * Get the currently selected nodes.
12922      * @return {Array} An array of HTMLElements
12923      */
12924     getSelectedNodes : function(){
12925         return this.selections;
12926     },
12927
12928     /**
12929      * Get the indexes of the selected nodes.
12930      * @return {Array}
12931      */
12932     getSelectedIndexes : function(){
12933         var indexes = [], s = this.selections;
12934         for(var i = 0, len = s.length; i < len; i++){
12935             indexes.push(s[i].nodeIndex);
12936         }
12937         return indexes;
12938     },
12939
12940     /**
12941      * Clear all selections
12942      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12943      */
12944     clearSelections : function(suppressEvent){
12945         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12946             this.cmp.elements = this.selections;
12947             this.cmp.removeClass(this.selectedClass);
12948             this.selections = [];
12949             if(!suppressEvent){
12950                 this.fireEvent("selectionchange", this, this.selections);
12951             }
12952         }
12953     },
12954
12955     /**
12956      * Returns true if the passed node is selected
12957      * @param {HTMLElement/Number} node The node or node index
12958      * @return {Boolean}
12959      */
12960     isSelected : function(node){
12961         var s = this.selections;
12962         if(s.length < 1){
12963             return false;
12964         }
12965         node = this.getNode(node);
12966         return s.indexOf(node) !== -1;
12967     },
12968
12969     /**
12970      * Selects nodes.
12971      * @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
12972      * @param {Boolean} keepExisting (optional) true to keep existing selections
12973      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12974      */
12975     select : function(nodeInfo, keepExisting, suppressEvent){
12976         if(nodeInfo instanceof Array){
12977             if(!keepExisting){
12978                 this.clearSelections(true);
12979             }
12980             for(var i = 0, len = nodeInfo.length; i < len; i++){
12981                 this.select(nodeInfo[i], true, true);
12982             }
12983             return;
12984         } 
12985         var node = this.getNode(nodeInfo);
12986         if(!node || this.isSelected(node)){
12987             return; // already selected.
12988         }
12989         if(!keepExisting){
12990             this.clearSelections(true);
12991         }
12992         
12993         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12994             Roo.fly(node).addClass(this.selectedClass);
12995             this.selections.push(node);
12996             if(!suppressEvent){
12997                 this.fireEvent("selectionchange", this, this.selections);
12998             }
12999         }
13000         
13001         
13002     },
13003       /**
13004      * Unselects nodes.
13005      * @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
13006      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13007      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13008      */
13009     unselect : function(nodeInfo, keepExisting, suppressEvent)
13010     {
13011         if(nodeInfo instanceof Array){
13012             Roo.each(this.selections, function(s) {
13013                 this.unselect(s, nodeInfo);
13014             }, this);
13015             return;
13016         }
13017         var node = this.getNode(nodeInfo);
13018         if(!node || !this.isSelected(node)){
13019             //Roo.log("not selected");
13020             return; // not selected.
13021         }
13022         // fireevent???
13023         var ns = [];
13024         Roo.each(this.selections, function(s) {
13025             if (s == node ) {
13026                 Roo.fly(node).removeClass(this.selectedClass);
13027
13028                 return;
13029             }
13030             ns.push(s);
13031         },this);
13032         
13033         this.selections= ns;
13034         this.fireEvent("selectionchange", this, this.selections);
13035     },
13036
13037     /**
13038      * Gets a template 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 {HTMLElement} The node or null if it wasn't found
13041      */
13042     getNode : function(nodeInfo){
13043         if(typeof nodeInfo == "string"){
13044             return document.getElementById(nodeInfo);
13045         }else if(typeof nodeInfo == "number"){
13046             return this.nodes[nodeInfo];
13047         }
13048         return nodeInfo;
13049     },
13050
13051     /**
13052      * Gets a range template nodes.
13053      * @param {Number} startIndex
13054      * @param {Number} endIndex
13055      * @return {Array} An array of nodes
13056      */
13057     getNodes : function(start, end){
13058         var ns = this.nodes;
13059         start = start || 0;
13060         end = typeof end == "undefined" ? ns.length - 1 : end;
13061         var nodes = [];
13062         if(start <= end){
13063             for(var i = start; i <= end; i++){
13064                 nodes.push(ns[i]);
13065             }
13066         } else{
13067             for(var i = start; i >= end; i--){
13068                 nodes.push(ns[i]);
13069             }
13070         }
13071         return nodes;
13072     },
13073
13074     /**
13075      * Finds the index of the passed node
13076      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13077      * @return {Number} The index of the node or -1
13078      */
13079     indexOf : function(node){
13080         node = this.getNode(node);
13081         if(typeof node.nodeIndex == "number"){
13082             return node.nodeIndex;
13083         }
13084         var ns = this.nodes;
13085         for(var i = 0, len = ns.length; i < len; i++){
13086             if(ns[i] == node){
13087                 return i;
13088             }
13089         }
13090         return -1;
13091     }
13092 });
13093 /*
13094  * - LGPL
13095  *
13096  * based on jquery fullcalendar
13097  * 
13098  */
13099
13100 Roo.bootstrap = Roo.bootstrap || {};
13101 /**
13102  * @class Roo.bootstrap.Calendar
13103  * @extends Roo.bootstrap.Component
13104  * Bootstrap Calendar class
13105  * @cfg {Boolean} loadMask (true|false) default false
13106  * @cfg {Object} header generate the user specific header of the calendar, default false
13107
13108  * @constructor
13109  * Create a new Container
13110  * @param {Object} config The config object
13111  */
13112
13113
13114
13115 Roo.bootstrap.Calendar = function(config){
13116     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13117      this.addEvents({
13118         /**
13119              * @event select
13120              * Fires when a date is selected
13121              * @param {DatePicker} this
13122              * @param {Date} date The selected date
13123              */
13124         'select': true,
13125         /**
13126              * @event monthchange
13127              * Fires when the displayed month changes 
13128              * @param {DatePicker} this
13129              * @param {Date} date The selected month
13130              */
13131         'monthchange': true,
13132         /**
13133              * @event evententer
13134              * Fires when mouse over an event
13135              * @param {Calendar} this
13136              * @param {event} Event
13137              */
13138         'evententer': true,
13139         /**
13140              * @event eventleave
13141              * Fires when the mouse leaves an
13142              * @param {Calendar} this
13143              * @param {event}
13144              */
13145         'eventleave': true,
13146         /**
13147              * @event eventclick
13148              * Fires when the mouse click an
13149              * @param {Calendar} this
13150              * @param {event}
13151              */
13152         'eventclick': true
13153         
13154     });
13155
13156 };
13157
13158 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13159     
13160      /**
13161      * @cfg {Number} startDay
13162      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13163      */
13164     startDay : 0,
13165     
13166     loadMask : false,
13167     
13168     header : false,
13169       
13170     getAutoCreate : function(){
13171         
13172         
13173         var fc_button = function(name, corner, style, content ) {
13174             return Roo.apply({},{
13175                 tag : 'span',
13176                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13177                          (corner.length ?
13178                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13179                             ''
13180                         ),
13181                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13182                 unselectable: 'on'
13183             });
13184         };
13185         
13186         var header = {};
13187         
13188         if(!this.header){
13189             header = {
13190                 tag : 'table',
13191                 cls : 'fc-header',
13192                 style : 'width:100%',
13193                 cn : [
13194                     {
13195                         tag: 'tr',
13196                         cn : [
13197                             {
13198                                 tag : 'td',
13199                                 cls : 'fc-header-left',
13200                                 cn : [
13201                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13202                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13203                                     { tag: 'span', cls: 'fc-header-space' },
13204                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13205
13206
13207                                 ]
13208                             },
13209
13210                             {
13211                                 tag : 'td',
13212                                 cls : 'fc-header-center',
13213                                 cn : [
13214                                     {
13215                                         tag: 'span',
13216                                         cls: 'fc-header-title',
13217                                         cn : {
13218                                             tag: 'H2',
13219                                             html : 'month / year'
13220                                         }
13221                                     }
13222
13223                                 ]
13224                             },
13225                             {
13226                                 tag : 'td',
13227                                 cls : 'fc-header-right',
13228                                 cn : [
13229                               /*      fc_button('month', 'left', '', 'month' ),
13230                                     fc_button('week', '', '', 'week' ),
13231                                     fc_button('day', 'right', '', 'day' )
13232                                 */    
13233
13234                                 ]
13235                             }
13236
13237                         ]
13238                     }
13239                 ]
13240             };
13241         }
13242         
13243         header = this.header;
13244         
13245        
13246         var cal_heads = function() {
13247             var ret = [];
13248             // fixme - handle this.
13249             
13250             for (var i =0; i < Date.dayNames.length; i++) {
13251                 var d = Date.dayNames[i];
13252                 ret.push({
13253                     tag: 'th',
13254                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13255                     html : d.substring(0,3)
13256                 });
13257                 
13258             }
13259             ret[0].cls += ' fc-first';
13260             ret[6].cls += ' fc-last';
13261             return ret;
13262         };
13263         var cal_cell = function(n) {
13264             return  {
13265                 tag: 'td',
13266                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13267                 cn : [
13268                     {
13269                         cn : [
13270                             {
13271                                 cls: 'fc-day-number',
13272                                 html: 'D'
13273                             },
13274                             {
13275                                 cls: 'fc-day-content',
13276                              
13277                                 cn : [
13278                                      {
13279                                         style: 'position: relative;' // height: 17px;
13280                                     }
13281                                 ]
13282                             }
13283                             
13284                             
13285                         ]
13286                     }
13287                 ]
13288                 
13289             }
13290         };
13291         var cal_rows = function() {
13292             
13293             var ret = [];
13294             for (var r = 0; r < 6; r++) {
13295                 var row= {
13296                     tag : 'tr',
13297                     cls : 'fc-week',
13298                     cn : []
13299                 };
13300                 
13301                 for (var i =0; i < Date.dayNames.length; i++) {
13302                     var d = Date.dayNames[i];
13303                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13304
13305                 }
13306                 row.cn[0].cls+=' fc-first';
13307                 row.cn[0].cn[0].style = 'min-height:90px';
13308                 row.cn[6].cls+=' fc-last';
13309                 ret.push(row);
13310                 
13311             }
13312             ret[0].cls += ' fc-first';
13313             ret[4].cls += ' fc-prev-last';
13314             ret[5].cls += ' fc-last';
13315             return ret;
13316             
13317         };
13318         
13319         var cal_table = {
13320             tag: 'table',
13321             cls: 'fc-border-separate',
13322             style : 'width:100%',
13323             cellspacing  : 0,
13324             cn : [
13325                 { 
13326                     tag: 'thead',
13327                     cn : [
13328                         { 
13329                             tag: 'tr',
13330                             cls : 'fc-first fc-last',
13331                             cn : cal_heads()
13332                         }
13333                     ]
13334                 },
13335                 { 
13336                     tag: 'tbody',
13337                     cn : cal_rows()
13338                 }
13339                   
13340             ]
13341         };
13342          
13343          var cfg = {
13344             cls : 'fc fc-ltr',
13345             cn : [
13346                 header,
13347                 {
13348                     cls : 'fc-content',
13349                     style : "position: relative;",
13350                     cn : [
13351                         {
13352                             cls : 'fc-view fc-view-month fc-grid',
13353                             style : 'position: relative',
13354                             unselectable : 'on',
13355                             cn : [
13356                                 {
13357                                     cls : 'fc-event-container',
13358                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13359                                 },
13360                                 cal_table
13361                             ]
13362                         }
13363                     ]
13364     
13365                 }
13366            ] 
13367             
13368         };
13369         
13370          
13371         
13372         return cfg;
13373     },
13374     
13375     
13376     initEvents : function()
13377     {
13378         if(!this.store){
13379             throw "can not find store for calendar";
13380         }
13381         
13382         var mark = {
13383             tag: "div",
13384             cls:"x-dlg-mask",
13385             style: "text-align:center",
13386             cn: [
13387                 {
13388                     tag: "div",
13389                     style: "background-color:white;width:50%;margin:250 auto",
13390                     cn: [
13391                         {
13392                             tag: "img",
13393                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13394                         },
13395                         {
13396                             tag: "span",
13397                             html: "Loading"
13398                         }
13399                         
13400                     ]
13401                 }
13402             ]
13403         }
13404         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13405         
13406         var size = this.el.select('.fc-content', true).first().getSize();
13407         this.maskEl.setSize(size.width, size.height);
13408         this.maskEl.enableDisplayMode("block");
13409         if(!this.loadMask){
13410             this.maskEl.hide();
13411         }
13412         
13413         this.store = Roo.factory(this.store, Roo.data);
13414         this.store.on('load', this.onLoad, this);
13415         this.store.on('beforeload', this.onBeforeLoad, this);
13416         
13417         this.resize();
13418         
13419         this.cells = this.el.select('.fc-day',true);
13420         //Roo.log(this.cells);
13421         this.textNodes = this.el.query('.fc-day-number');
13422         this.cells.addClassOnOver('fc-state-hover');
13423         
13424         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13425         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13426         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13427         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13428         
13429         this.on('monthchange', this.onMonthChange, this);
13430         
13431         this.update(new Date().clearTime());
13432     },
13433     
13434     resize : function() {
13435         var sz  = this.el.getSize();
13436         
13437         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13438         this.el.select('.fc-day-content div',true).setHeight(34);
13439     },
13440     
13441     
13442     // private
13443     showPrevMonth : function(e){
13444         this.update(this.activeDate.add("mo", -1));
13445     },
13446     showToday : function(e){
13447         this.update(new Date().clearTime());
13448     },
13449     // private
13450     showNextMonth : function(e){
13451         this.update(this.activeDate.add("mo", 1));
13452     },
13453
13454     // private
13455     showPrevYear : function(){
13456         this.update(this.activeDate.add("y", -1));
13457     },
13458
13459     // private
13460     showNextYear : function(){
13461         this.update(this.activeDate.add("y", 1));
13462     },
13463
13464     
13465    // private
13466     update : function(date)
13467     {
13468         var vd = this.activeDate;
13469         this.activeDate = date;
13470 //        if(vd && this.el){
13471 //            var t = date.getTime();
13472 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13473 //                Roo.log('using add remove');
13474 //                
13475 //                this.fireEvent('monthchange', this, date);
13476 //                
13477 //                this.cells.removeClass("fc-state-highlight");
13478 //                this.cells.each(function(c){
13479 //                   if(c.dateValue == t){
13480 //                       c.addClass("fc-state-highlight");
13481 //                       setTimeout(function(){
13482 //                            try{c.dom.firstChild.focus();}catch(e){}
13483 //                       }, 50);
13484 //                       return false;
13485 //                   }
13486 //                   return true;
13487 //                });
13488 //                return;
13489 //            }
13490 //        }
13491         
13492         var days = date.getDaysInMonth();
13493         
13494         var firstOfMonth = date.getFirstDateOfMonth();
13495         var startingPos = firstOfMonth.getDay()-this.startDay;
13496         
13497         if(startingPos < this.startDay){
13498             startingPos += 7;
13499         }
13500         
13501         var pm = date.add(Date.MONTH, -1);
13502         var prevStart = pm.getDaysInMonth()-startingPos;
13503 //        
13504         this.cells = this.el.select('.fc-day',true);
13505         this.textNodes = this.el.query('.fc-day-number');
13506         this.cells.addClassOnOver('fc-state-hover');
13507         
13508         var cells = this.cells.elements;
13509         var textEls = this.textNodes;
13510         
13511         Roo.each(cells, function(cell){
13512             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13513         });
13514         
13515         days += startingPos;
13516
13517         // convert everything to numbers so it's fast
13518         var day = 86400000;
13519         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13520         //Roo.log(d);
13521         //Roo.log(pm);
13522         //Roo.log(prevStart);
13523         
13524         var today = new Date().clearTime().getTime();
13525         var sel = date.clearTime().getTime();
13526         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13527         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13528         var ddMatch = this.disabledDatesRE;
13529         var ddText = this.disabledDatesText;
13530         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13531         var ddaysText = this.disabledDaysText;
13532         var format = this.format;
13533         
13534         var setCellClass = function(cal, cell){
13535             cell.row = 0;
13536             cell.events = [];
13537             cell.more = [];
13538             //Roo.log('set Cell Class');
13539             cell.title = "";
13540             var t = d.getTime();
13541             
13542             //Roo.log(d);
13543             
13544             cell.dateValue = t;
13545             if(t == today){
13546                 cell.className += " fc-today";
13547                 cell.className += " fc-state-highlight";
13548                 cell.title = cal.todayText;
13549             }
13550             if(t == sel){
13551                 // disable highlight in other month..
13552                 //cell.className += " fc-state-highlight";
13553                 
13554             }
13555             // disabling
13556             if(t < min) {
13557                 cell.className = " fc-state-disabled";
13558                 cell.title = cal.minText;
13559                 return;
13560             }
13561             if(t > max) {
13562                 cell.className = " fc-state-disabled";
13563                 cell.title = cal.maxText;
13564                 return;
13565             }
13566             if(ddays){
13567                 if(ddays.indexOf(d.getDay()) != -1){
13568                     cell.title = ddaysText;
13569                     cell.className = " fc-state-disabled";
13570                 }
13571             }
13572             if(ddMatch && format){
13573                 var fvalue = d.dateFormat(format);
13574                 if(ddMatch.test(fvalue)){
13575                     cell.title = ddText.replace("%0", fvalue);
13576                     cell.className = " fc-state-disabled";
13577                 }
13578             }
13579             
13580             if (!cell.initialClassName) {
13581                 cell.initialClassName = cell.dom.className;
13582             }
13583             
13584             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13585         };
13586
13587         var i = 0;
13588         
13589         for(; i < startingPos; i++) {
13590             textEls[i].innerHTML = (++prevStart);
13591             d.setDate(d.getDate()+1);
13592             
13593             cells[i].className = "fc-past fc-other-month";
13594             setCellClass(this, cells[i]);
13595         }
13596         
13597         var intDay = 0;
13598         
13599         for(; i < days; i++){
13600             intDay = i - startingPos + 1;
13601             textEls[i].innerHTML = (intDay);
13602             d.setDate(d.getDate()+1);
13603             
13604             cells[i].className = ''; // "x-date-active";
13605             setCellClass(this, cells[i]);
13606         }
13607         var extraDays = 0;
13608         
13609         for(; i < 42; i++) {
13610             textEls[i].innerHTML = (++extraDays);
13611             d.setDate(d.getDate()+1);
13612             
13613             cells[i].className = "fc-future fc-other-month";
13614             setCellClass(this, cells[i]);
13615         }
13616         
13617         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13618         
13619         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13620         
13621         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13622         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13623         
13624         if(totalRows != 6){
13625             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13626             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13627         }
13628         
13629         this.fireEvent('monthchange', this, date);
13630         
13631         
13632         /*
13633         if(!this.internalRender){
13634             var main = this.el.dom.firstChild;
13635             var w = main.offsetWidth;
13636             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13637             Roo.fly(main).setWidth(w);
13638             this.internalRender = true;
13639             // opera does not respect the auto grow header center column
13640             // then, after it gets a width opera refuses to recalculate
13641             // without a second pass
13642             if(Roo.isOpera && !this.secondPass){
13643                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13644                 this.secondPass = true;
13645                 this.update.defer(10, this, [date]);
13646             }
13647         }
13648         */
13649         
13650     },
13651     
13652     findCell : function(dt) {
13653         dt = dt.clearTime().getTime();
13654         var ret = false;
13655         this.cells.each(function(c){
13656             //Roo.log("check " +c.dateValue + '?=' + dt);
13657             if(c.dateValue == dt){
13658                 ret = c;
13659                 return false;
13660             }
13661             return true;
13662         });
13663         
13664         return ret;
13665     },
13666     
13667     findCells : function(ev) {
13668         var s = ev.start.clone().clearTime().getTime();
13669        // Roo.log(s);
13670         var e= ev.end.clone().clearTime().getTime();
13671        // Roo.log(e);
13672         var ret = [];
13673         this.cells.each(function(c){
13674              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13675             
13676             if(c.dateValue > e){
13677                 return ;
13678             }
13679             if(c.dateValue < s){
13680                 return ;
13681             }
13682             ret.push(c);
13683         });
13684         
13685         return ret;    
13686     },
13687     
13688 //    findBestRow: function(cells)
13689 //    {
13690 //        var ret = 0;
13691 //        
13692 //        for (var i =0 ; i < cells.length;i++) {
13693 //            ret  = Math.max(cells[i].rows || 0,ret);
13694 //        }
13695 //        return ret;
13696 //        
13697 //    },
13698     
13699     
13700     addItem : function(ev)
13701     {
13702         // look for vertical location slot in
13703         var cells = this.findCells(ev);
13704         
13705 //        ev.row = this.findBestRow(cells);
13706         
13707         // work out the location.
13708         
13709         var crow = false;
13710         var rows = [];
13711         for(var i =0; i < cells.length; i++) {
13712             
13713             cells[i].row = cells[0].row;
13714             
13715             if(i == 0){
13716                 cells[i].row = cells[i].row + 1;
13717             }
13718             
13719             if (!crow) {
13720                 crow = {
13721                     start : cells[i],
13722                     end :  cells[i]
13723                 };
13724                 continue;
13725             }
13726             if (crow.start.getY() == cells[i].getY()) {
13727                 // on same row.
13728                 crow.end = cells[i];
13729                 continue;
13730             }
13731             // different row.
13732             rows.push(crow);
13733             crow = {
13734                 start: cells[i],
13735                 end : cells[i]
13736             };
13737             
13738         }
13739         
13740         rows.push(crow);
13741         ev.els = [];
13742         ev.rows = rows;
13743         ev.cells = cells;
13744         
13745         cells[0].events.push(ev);
13746         
13747         this.calevents.push(ev);
13748     },
13749     
13750     clearEvents: function() {
13751         
13752         if(!this.calevents){
13753             return;
13754         }
13755         
13756         Roo.each(this.cells.elements, function(c){
13757             c.row = 0;
13758             c.events = [];
13759             c.more = [];
13760         });
13761         
13762         Roo.each(this.calevents, function(e) {
13763             Roo.each(e.els, function(el) {
13764                 el.un('mouseenter' ,this.onEventEnter, this);
13765                 el.un('mouseleave' ,this.onEventLeave, this);
13766                 el.remove();
13767             },this);
13768         },this);
13769         
13770         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13771             e.remove();
13772         });
13773         
13774     },
13775     
13776     renderEvents: function()
13777     {   
13778         var _this = this;
13779         
13780         this.cells.each(function(c) {
13781             
13782             if(c.row < 5){
13783                 return;
13784             }
13785             
13786             var ev = c.events;
13787             
13788             var r = 4;
13789             if(c.row != c.events.length){
13790                 r = 4 - (4 - (c.row - c.events.length));
13791             }
13792             
13793             c.events = ev.slice(0, r);
13794             c.more = ev.slice(r);
13795             
13796             if(c.more.length && c.more.length == 1){
13797                 c.events.push(c.more.pop());
13798             }
13799             
13800             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13801             
13802         });
13803             
13804         this.cells.each(function(c) {
13805             
13806             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13807             
13808             
13809             for (var e = 0; e < c.events.length; e++){
13810                 var ev = c.events[e];
13811                 var rows = ev.rows;
13812                 
13813                 for(var i = 0; i < rows.length; i++) {
13814                 
13815                     // how many rows should it span..
13816
13817                     var  cfg = {
13818                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13819                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13820
13821                         unselectable : "on",
13822                         cn : [
13823                             {
13824                                 cls: 'fc-event-inner',
13825                                 cn : [
13826     //                                {
13827     //                                  tag:'span',
13828     //                                  cls: 'fc-event-time',
13829     //                                  html : cells.length > 1 ? '' : ev.time
13830     //                                },
13831                                     {
13832                                       tag:'span',
13833                                       cls: 'fc-event-title',
13834                                       html : String.format('{0}', ev.title)
13835                                     }
13836
13837
13838                                 ]
13839                             },
13840                             {
13841                                 cls: 'ui-resizable-handle ui-resizable-e',
13842                                 html : '&nbsp;&nbsp;&nbsp'
13843                             }
13844
13845                         ]
13846                     };
13847
13848                     if (i == 0) {
13849                         cfg.cls += ' fc-event-start';
13850                     }
13851                     if ((i+1) == rows.length) {
13852                         cfg.cls += ' fc-event-end';
13853                     }
13854
13855                     var ctr = _this.el.select('.fc-event-container',true).first();
13856                     var cg = ctr.createChild(cfg);
13857
13858                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13859                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13860
13861                     var r = (c.more.length) ? 1 : 0;
13862                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13863                     cg.setWidth(ebox.right - sbox.x -2);
13864
13865                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13866                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13867                     cg.on('click', _this.onEventClick, _this, ev);
13868
13869                     ev.els.push(cg);
13870                     
13871                 }
13872                 
13873             }
13874             
13875             
13876             if(c.more.length){
13877                 var  cfg = {
13878                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13879                     style : 'position: absolute',
13880                     unselectable : "on",
13881                     cn : [
13882                         {
13883                             cls: 'fc-event-inner',
13884                             cn : [
13885                                 {
13886                                   tag:'span',
13887                                   cls: 'fc-event-title',
13888                                   html : 'More'
13889                                 }
13890
13891
13892                             ]
13893                         },
13894                         {
13895                             cls: 'ui-resizable-handle ui-resizable-e',
13896                             html : '&nbsp;&nbsp;&nbsp'
13897                         }
13898
13899                     ]
13900                 };
13901
13902                 var ctr = _this.el.select('.fc-event-container',true).first();
13903                 var cg = ctr.createChild(cfg);
13904
13905                 var sbox = c.select('.fc-day-content',true).first().getBox();
13906                 var ebox = c.select('.fc-day-content',true).first().getBox();
13907                 //Roo.log(cg);
13908                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13909                 cg.setWidth(ebox.right - sbox.x -2);
13910
13911                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13912                 
13913             }
13914             
13915         });
13916         
13917         
13918         
13919     },
13920     
13921     onEventEnter: function (e, el,event,d) {
13922         this.fireEvent('evententer', this, el, event);
13923     },
13924     
13925     onEventLeave: function (e, el,event,d) {
13926         this.fireEvent('eventleave', this, el, event);
13927     },
13928     
13929     onEventClick: function (e, el,event,d) {
13930         this.fireEvent('eventclick', this, el, event);
13931     },
13932     
13933     onMonthChange: function () {
13934         this.store.load();
13935     },
13936     
13937     onMoreEventClick: function(e, el, more)
13938     {
13939         var _this = this;
13940         
13941         this.calpopover.placement = 'right';
13942         this.calpopover.setTitle('More');
13943         
13944         this.calpopover.setContent('');
13945         
13946         var ctr = this.calpopover.el.select('.popover-content', true).first();
13947         
13948         Roo.each(more, function(m){
13949             var cfg = {
13950                 cls : 'fc-event-hori fc-event-draggable',
13951                 html : m.title
13952             }
13953             var cg = ctr.createChild(cfg);
13954             
13955             cg.on('click', _this.onEventClick, _this, m);
13956         });
13957         
13958         this.calpopover.show(el);
13959         
13960         
13961     },
13962     
13963     onLoad: function () 
13964     {   
13965         this.calevents = [];
13966         var cal = this;
13967         
13968         if(this.store.getCount() > 0){
13969             this.store.data.each(function(d){
13970                cal.addItem({
13971                     id : d.data.id,
13972                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13973                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13974                     time : d.data.start_time,
13975                     title : d.data.title,
13976                     description : d.data.description,
13977                     venue : d.data.venue
13978                 });
13979             });
13980         }
13981         
13982         this.renderEvents();
13983         
13984         if(this.calevents.length && this.loadMask){
13985             this.maskEl.hide();
13986         }
13987     },
13988     
13989     onBeforeLoad: function()
13990     {
13991         this.clearEvents();
13992         if(this.loadMask){
13993             this.maskEl.show();
13994         }
13995     }
13996 });
13997
13998  
13999  /*
14000  * - LGPL
14001  *
14002  * element
14003  * 
14004  */
14005
14006 /**
14007  * @class Roo.bootstrap.Popover
14008  * @extends Roo.bootstrap.Component
14009  * Bootstrap Popover class
14010  * @cfg {String} html contents of the popover   (or false to use children..)
14011  * @cfg {String} title of popover (or false to hide)
14012  * @cfg {String} placement how it is placed
14013  * @cfg {String} trigger click || hover (or false to trigger manually)
14014  * @cfg {String} over what (parent or false to trigger manually.)
14015  * @cfg {Number} delay - delay before showing
14016  
14017  * @constructor
14018  * Create a new Popover
14019  * @param {Object} config The config object
14020  */
14021
14022 Roo.bootstrap.Popover = function(config){
14023     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14024 };
14025
14026 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14027     
14028     title: 'Fill in a title',
14029     html: false,
14030     
14031     placement : 'right',
14032     trigger : 'hover', // hover
14033     
14034     delay : 0,
14035     
14036     over: 'parent',
14037     
14038     can_build_overlaid : false,
14039     
14040     getChildContainer : function()
14041     {
14042         return this.el.select('.popover-content',true).first();
14043     },
14044     
14045     getAutoCreate : function(){
14046          Roo.log('make popover?');
14047         var cfg = {
14048            cls : 'popover roo-dynamic',
14049            style: 'display:block',
14050            cn : [
14051                 {
14052                     cls : 'arrow'
14053                 },
14054                 {
14055                     cls : 'popover-inner',
14056                     cn : [
14057                         {
14058                             tag: 'h3',
14059                             cls: 'popover-title',
14060                             html : this.title
14061                         },
14062                         {
14063                             cls : 'popover-content',
14064                             html : this.html
14065                         }
14066                     ]
14067                     
14068                 }
14069            ]
14070         };
14071         
14072         return cfg;
14073     },
14074     setTitle: function(str)
14075     {
14076         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14077     },
14078     setContent: function(str)
14079     {
14080         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14081     },
14082     // as it get's added to the bottom of the page.
14083     onRender : function(ct, position)
14084     {
14085         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14086         if(!this.el){
14087             var cfg = Roo.apply({},  this.getAutoCreate());
14088             cfg.id = Roo.id();
14089             
14090             if (this.cls) {
14091                 cfg.cls += ' ' + this.cls;
14092             }
14093             if (this.style) {
14094                 cfg.style = this.style;
14095             }
14096             Roo.log("adding to ")
14097             this.el = Roo.get(document.body).createChild(cfg, position);
14098             Roo.log(this.el);
14099         }
14100         this.initEvents();
14101     },
14102     
14103     initEvents : function()
14104     {
14105         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14106         this.el.enableDisplayMode('block');
14107         this.el.hide();
14108         if (this.over === false) {
14109             return; 
14110         }
14111         if (this.triggers === false) {
14112             return;
14113         }
14114         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14115         var triggers = this.trigger ? this.trigger.split(' ') : [];
14116         Roo.each(triggers, function(trigger) {
14117         
14118             if (trigger == 'click') {
14119                 on_el.on('click', this.toggle, this);
14120             } else if (trigger != 'manual') {
14121                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14122                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14123       
14124                 on_el.on(eventIn  ,this.enter, this);
14125                 on_el.on(eventOut, this.leave, this);
14126             }
14127         }, this);
14128         
14129     },
14130     
14131     
14132     // private
14133     timeout : null,
14134     hoverState : null,
14135     
14136     toggle : function () {
14137         this.hoverState == 'in' ? this.leave() : this.enter();
14138     },
14139     
14140     enter : function () {
14141        
14142     
14143         clearTimeout(this.timeout);
14144     
14145         this.hoverState = 'in';
14146     
14147         if (!this.delay || !this.delay.show) {
14148             this.show();
14149             return;
14150         }
14151         var _t = this;
14152         this.timeout = setTimeout(function () {
14153             if (_t.hoverState == 'in') {
14154                 _t.show();
14155             }
14156         }, this.delay.show)
14157     },
14158     leave : function() {
14159         clearTimeout(this.timeout);
14160     
14161         this.hoverState = 'out';
14162     
14163         if (!this.delay || !this.delay.hide) {
14164             this.hide();
14165             return;
14166         }
14167         var _t = this;
14168         this.timeout = setTimeout(function () {
14169             if (_t.hoverState == 'out') {
14170                 _t.hide();
14171             }
14172         }, this.delay.hide)
14173     },
14174     
14175     show : function (on_el)
14176     {
14177         if (!on_el) {
14178             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14179         }
14180         // set content.
14181         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14182         if (this.html !== false) {
14183             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14184         }
14185         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14186         if (!this.title.length) {
14187             this.el.select('.popover-title',true).hide();
14188         }
14189         
14190         var placement = typeof this.placement == 'function' ?
14191             this.placement.call(this, this.el, on_el) :
14192             this.placement;
14193             
14194         var autoToken = /\s?auto?\s?/i;
14195         var autoPlace = autoToken.test(placement);
14196         if (autoPlace) {
14197             placement = placement.replace(autoToken, '') || 'top';
14198         }
14199         
14200         //this.el.detach()
14201         //this.el.setXY([0,0]);
14202         this.el.show();
14203         this.el.dom.style.display='block';
14204         this.el.addClass(placement);
14205         
14206         //this.el.appendTo(on_el);
14207         
14208         var p = this.getPosition();
14209         var box = this.el.getBox();
14210         
14211         if (autoPlace) {
14212             // fixme..
14213         }
14214         var align = Roo.bootstrap.Popover.alignment[placement];
14215         this.el.alignTo(on_el, align[0],align[1]);
14216         //var arrow = this.el.select('.arrow',true).first();
14217         //arrow.set(align[2], 
14218         
14219         this.el.addClass('in');
14220         this.hoverState = null;
14221         
14222         if (this.el.hasClass('fade')) {
14223             // fade it?
14224         }
14225         
14226     },
14227     hide : function()
14228     {
14229         this.el.setXY([0,0]);
14230         this.el.removeClass('in');
14231         this.el.hide();
14232         
14233     }
14234     
14235 });
14236
14237 Roo.bootstrap.Popover.alignment = {
14238     'left' : ['r-l', [-10,0], 'right'],
14239     'right' : ['l-r', [10,0], 'left'],
14240     'bottom' : ['t-b', [0,10], 'top'],
14241     'top' : [ 'b-t', [0,-10], 'bottom']
14242 };
14243
14244  /*
14245  * - LGPL
14246  *
14247  * Progress
14248  * 
14249  */
14250
14251 /**
14252  * @class Roo.bootstrap.Progress
14253  * @extends Roo.bootstrap.Component
14254  * Bootstrap Progress class
14255  * @cfg {Boolean} striped striped of the progress bar
14256  * @cfg {Boolean} active animated of the progress bar
14257  * 
14258  * 
14259  * @constructor
14260  * Create a new Progress
14261  * @param {Object} config The config object
14262  */
14263
14264 Roo.bootstrap.Progress = function(config){
14265     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14266 };
14267
14268 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14269     
14270     striped : false,
14271     active: false,
14272     
14273     getAutoCreate : function(){
14274         var cfg = {
14275             tag: 'div',
14276             cls: 'progress'
14277         };
14278         
14279         
14280         if(this.striped){
14281             cfg.cls += ' progress-striped';
14282         }
14283       
14284         if(this.active){
14285             cfg.cls += ' active';
14286         }
14287         
14288         
14289         return cfg;
14290     }
14291    
14292 });
14293
14294  
14295
14296  /*
14297  * - LGPL
14298  *
14299  * ProgressBar
14300  * 
14301  */
14302
14303 /**
14304  * @class Roo.bootstrap.ProgressBar
14305  * @extends Roo.bootstrap.Component
14306  * Bootstrap ProgressBar class
14307  * @cfg {Number} aria_valuenow aria-value now
14308  * @cfg {Number} aria_valuemin aria-value min
14309  * @cfg {Number} aria_valuemax aria-value max
14310  * @cfg {String} label label for the progress bar
14311  * @cfg {String} panel (success | info | warning | danger )
14312  * @cfg {String} role role of the progress bar
14313  * @cfg {String} sr_only text
14314  * 
14315  * 
14316  * @constructor
14317  * Create a new ProgressBar
14318  * @param {Object} config The config object
14319  */
14320
14321 Roo.bootstrap.ProgressBar = function(config){
14322     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14323 };
14324
14325 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14326     
14327     aria_valuenow : 0,
14328     aria_valuemin : 0,
14329     aria_valuemax : 100,
14330     label : false,
14331     panel : false,
14332     role : false,
14333     sr_only: false,
14334     
14335     getAutoCreate : function()
14336     {
14337         
14338         var cfg = {
14339             tag: 'div',
14340             cls: 'progress-bar',
14341             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14342         };
14343         
14344         if(this.sr_only){
14345             cfg.cn = {
14346                 tag: 'span',
14347                 cls: 'sr-only',
14348                 html: this.sr_only
14349             }
14350         }
14351         
14352         if(this.role){
14353             cfg.role = this.role;
14354         }
14355         
14356         if(this.aria_valuenow){
14357             cfg['aria-valuenow'] = this.aria_valuenow;
14358         }
14359         
14360         if(this.aria_valuemin){
14361             cfg['aria-valuemin'] = this.aria_valuemin;
14362         }
14363         
14364         if(this.aria_valuemax){
14365             cfg['aria-valuemax'] = this.aria_valuemax;
14366         }
14367         
14368         if(this.label && !this.sr_only){
14369             cfg.html = this.label;
14370         }
14371         
14372         if(this.panel){
14373             cfg.cls += ' progress-bar-' + this.panel;
14374         }
14375         
14376         return cfg;
14377     },
14378     
14379     update : function(aria_valuenow)
14380     {
14381         this.aria_valuenow = aria_valuenow;
14382         
14383         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14384     }
14385    
14386 });
14387
14388  
14389
14390  /*
14391  * - LGPL
14392  *
14393  * column
14394  * 
14395  */
14396
14397 /**
14398  * @class Roo.bootstrap.TabGroup
14399  * @extends Roo.bootstrap.Column
14400  * Bootstrap Column class
14401  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14402  * @cfg {Boolean} carousel true to make the group behave like a carousel
14403  * 
14404  * @constructor
14405  * Create a new TabGroup
14406  * @param {Object} config The config object
14407  */
14408
14409 Roo.bootstrap.TabGroup = function(config){
14410     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14411     if (!this.navId) {
14412         this.navId = Roo.id();
14413     }
14414     this.tabs = [];
14415     Roo.bootstrap.TabGroup.register(this);
14416     
14417 };
14418
14419 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14420     
14421     carousel : false,
14422     transition : false,
14423      
14424     getAutoCreate : function()
14425     {
14426         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14427         
14428         cfg.cls += ' tab-content';
14429         
14430         if (this.carousel) {
14431             cfg.cls += ' carousel slide';
14432             cfg.cn = [{
14433                cls : 'carousel-inner'
14434             }]
14435         }
14436         
14437         
14438         return cfg;
14439     },
14440     getChildContainer : function()
14441     {
14442         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14443     },
14444     
14445     /**
14446     * register a Navigation item
14447     * @param {Roo.bootstrap.NavItem} the navitem to add
14448     */
14449     register : function(item)
14450     {
14451         this.tabs.push( item);
14452         item.navId = this.navId; // not really needed..
14453     
14454     },
14455     
14456     getActivePanel : function()
14457     {
14458         var r = false;
14459         Roo.each(this.tabs, function(t) {
14460             if (t.active) {
14461                 r = t;
14462                 return false;
14463             }
14464             return null;
14465         });
14466         return r;
14467         
14468     },
14469     getPanelByName : function(n)
14470     {
14471         var r = false;
14472         Roo.each(this.tabs, function(t) {
14473             if (t.tabId == n) {
14474                 r = t;
14475                 return false;
14476             }
14477             return null;
14478         });
14479         return r;
14480     },
14481     indexOfPanel : function(p)
14482     {
14483         var r = false;
14484         Roo.each(this.tabs, function(t,i) {
14485             if (t.tabId == p.tabId) {
14486                 r = i;
14487                 return false;
14488             }
14489             return null;
14490         });
14491         return r;
14492     },
14493     /**
14494      * show a specific panel
14495      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14496      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14497      */
14498     showPanel : function (pan)
14499     {
14500         
14501         if (typeof(pan) == 'number') {
14502             pan = this.tabs[pan];
14503         }
14504         if (typeof(pan) == 'string') {
14505             pan = this.getPanelByName(pan);
14506         }
14507         if (pan.tabId == this.getActivePanel().tabId) {
14508             return true;
14509         }
14510         var cur = this.getActivePanel();
14511         
14512         if (false === cur.fireEvent('beforedeactivate')) {
14513             return false;
14514         }
14515         
14516         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14517             
14518             this.transition = true;
14519             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14520             var lr = dir == 'next' ? 'left' : 'right';
14521             pan.el.addClass(dir); // or prev
14522             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14523             cur.el.addClass(lr); // or right
14524             pan.el.addClass(lr);
14525             
14526             var _this = this;
14527             cur.el.on('transitionend', function() {
14528                 Roo.log("trans end?");
14529                 
14530                 pan.el.removeClass([lr,dir]);
14531                 pan.setActive(true);
14532                 
14533                 cur.el.removeClass([lr]);
14534                 cur.setActive(false);
14535                 
14536                 _this.transition = false;
14537                 
14538             }, this, { single:  true } );
14539             return true;
14540         }
14541         
14542         cur.setActive(false);
14543         pan.setActive(true);
14544         return true;
14545         
14546     },
14547     showPanelNext : function()
14548     {
14549         var i = this.indexOfPanel(this.getActivePanel());
14550         if (i > this.tabs.length) {
14551             return;
14552         }
14553         this.showPanel(this.tabs[i+1]);
14554     },
14555     showPanelPrev : function()
14556     {
14557         var i = this.indexOfPanel(this.getActivePanel());
14558         if (i  < 1) {
14559             return;
14560         }
14561         this.showPanel(this.tabs[i-1]);
14562     }
14563     
14564     
14565   
14566 });
14567
14568  
14569
14570  
14571  
14572 Roo.apply(Roo.bootstrap.TabGroup, {
14573     
14574     groups: {},
14575      /**
14576     * register a Navigation Group
14577     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14578     */
14579     register : function(navgrp)
14580     {
14581         this.groups[navgrp.navId] = navgrp;
14582         
14583     },
14584     /**
14585     * fetch a Navigation Group based on the navigation ID
14586     * if one does not exist , it will get created.
14587     * @param {string} the navgroup to add
14588     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14589     */
14590     get: function(navId) {
14591         if (typeof(this.groups[navId]) == 'undefined') {
14592             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14593         }
14594         return this.groups[navId] ;
14595     }
14596     
14597     
14598     
14599 });
14600
14601  /*
14602  * - LGPL
14603  *
14604  * TabPanel
14605  * 
14606  */
14607
14608 /**
14609  * @class Roo.bootstrap.TabPanel
14610  * @extends Roo.bootstrap.Component
14611  * Bootstrap TabPanel class
14612  * @cfg {Boolean} active panel active
14613  * @cfg {String} html panel content
14614  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14615  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14616  * 
14617  * 
14618  * @constructor
14619  * Create a new TabPanel
14620  * @param {Object} config The config object
14621  */
14622
14623 Roo.bootstrap.TabPanel = function(config){
14624     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14625     this.addEvents({
14626         /**
14627              * @event changed
14628              * Fires when the active status changes
14629              * @param {Roo.bootstrap.TabPanel} this
14630              * @param {Boolean} state the new state
14631             
14632          */
14633         'changed': true,
14634         /**
14635              * @event beforedeactivate
14636              * Fires before a tab is de-activated - can be used to do validation on a form.
14637              * @param {Roo.bootstrap.TabPanel} this
14638              * @return {Boolean} false if there is an error
14639             
14640          */
14641         'beforedeactivate': true
14642      });
14643     
14644     this.tabId = this.tabId || Roo.id();
14645   
14646 };
14647
14648 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14649     
14650     active: false,
14651     html: false,
14652     tabId: false,
14653     navId : false,
14654     
14655     getAutoCreate : function(){
14656         var cfg = {
14657             tag: 'div',
14658             // item is needed for carousel - not sure if it has any effect otherwise
14659             cls: 'tab-pane item',
14660             html: this.html || ''
14661         };
14662         
14663         if(this.active){
14664             cfg.cls += ' active';
14665         }
14666         
14667         if(this.tabId){
14668             cfg.tabId = this.tabId;
14669         }
14670         
14671         
14672         return cfg;
14673     },
14674     
14675     initEvents:  function()
14676     {
14677         Roo.log('-------- init events on tab panel ---------');
14678         
14679         var p = this.parent();
14680         this.navId = this.navId || p.navId;
14681         
14682         if (typeof(this.navId) != 'undefined') {
14683             // not really needed.. but just in case.. parent should be a NavGroup.
14684             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14685             Roo.log(['register', tg, this]);
14686             tg.register(this);
14687         }
14688     },
14689     
14690     
14691     onRender : function(ct, position)
14692     {
14693        // Roo.log("Call onRender: " + this.xtype);
14694         
14695         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14696         
14697         
14698         
14699         
14700         
14701     },
14702     
14703     setActive: function(state)
14704     {
14705         Roo.log("panel - set active " + this.tabId + "=" + state);
14706         
14707         this.active = state;
14708         if (!state) {
14709             this.el.removeClass('active');
14710             
14711         } else  if (!this.el.hasClass('active')) {
14712             this.el.addClass('active');
14713         }
14714         this.fireEvent('changed', this, state);
14715     }
14716     
14717     
14718 });
14719  
14720
14721  
14722
14723  /*
14724  * - LGPL
14725  *
14726  * DateField
14727  * 
14728  */
14729
14730 /**
14731  * @class Roo.bootstrap.DateField
14732  * @extends Roo.bootstrap.Input
14733  * Bootstrap DateField class
14734  * @cfg {Number} weekStart default 0
14735  * @cfg {String} viewMode default empty, (months|years)
14736  * @cfg {String} minViewMode default empty, (months|years)
14737  * @cfg {Number} startDate default -Infinity
14738  * @cfg {Number} endDate default Infinity
14739  * @cfg {Boolean} todayHighlight default false
14740  * @cfg {Boolean} todayBtn default false
14741  * @cfg {Boolean} calendarWeeks default false
14742  * @cfg {Object} daysOfWeekDisabled default empty
14743  * @cfg {Boolean} singleMode default false (true | false)
14744  * 
14745  * @cfg {Boolean} keyboardNavigation default true
14746  * @cfg {String} language default en
14747  * 
14748  * @constructor
14749  * Create a new DateField
14750  * @param {Object} config The config object
14751  */
14752
14753 Roo.bootstrap.DateField = function(config){
14754     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14755      this.addEvents({
14756             /**
14757              * @event show
14758              * Fires when this field show.
14759              * @param {Roo.bootstrap.DateField} this
14760              * @param {Mixed} date The date value
14761              */
14762             show : true,
14763             /**
14764              * @event show
14765              * Fires when this field hide.
14766              * @param {Roo.bootstrap.DateField} this
14767              * @param {Mixed} date The date value
14768              */
14769             hide : true,
14770             /**
14771              * @event select
14772              * Fires when select a date.
14773              * @param {Roo.bootstrap.DateField} this
14774              * @param {Mixed} date The date value
14775              */
14776             select : true
14777         });
14778 };
14779
14780 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14781     
14782     /**
14783      * @cfg {String} format
14784      * The default date format string which can be overriden for localization support.  The format must be
14785      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14786      */
14787     format : "m/d/y",
14788     /**
14789      * @cfg {String} altFormats
14790      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14791      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14792      */
14793     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14794     
14795     weekStart : 0,
14796     
14797     viewMode : '',
14798     
14799     minViewMode : '',
14800     
14801     todayHighlight : false,
14802     
14803     todayBtn: false,
14804     
14805     language: 'en',
14806     
14807     keyboardNavigation: true,
14808     
14809     calendarWeeks: false,
14810     
14811     startDate: -Infinity,
14812     
14813     endDate: Infinity,
14814     
14815     daysOfWeekDisabled: [],
14816     
14817     _events: [],
14818     
14819     singleMode : false,
14820     
14821     UTCDate: function()
14822     {
14823         return new Date(Date.UTC.apply(Date, arguments));
14824     },
14825     
14826     UTCToday: function()
14827     {
14828         var today = new Date();
14829         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14830     },
14831     
14832     getDate: function() {
14833             var d = this.getUTCDate();
14834             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14835     },
14836     
14837     getUTCDate: function() {
14838             return this.date;
14839     },
14840     
14841     setDate: function(d) {
14842             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14843     },
14844     
14845     setUTCDate: function(d) {
14846             this.date = d;
14847             this.setValue(this.formatDate(this.date));
14848     },
14849         
14850     onRender: function(ct, position)
14851     {
14852         
14853         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14854         
14855         this.language = this.language || 'en';
14856         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14857         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14858         
14859         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14860         this.format = this.format || 'm/d/y';
14861         this.isInline = false;
14862         this.isInput = true;
14863         this.component = this.el.select('.add-on', true).first() || false;
14864         this.component = (this.component && this.component.length === 0) ? false : this.component;
14865         this.hasInput = this.component && this.inputEL().length;
14866         
14867         if (typeof(this.minViewMode === 'string')) {
14868             switch (this.minViewMode) {
14869                 case 'months':
14870                     this.minViewMode = 1;
14871                     break;
14872                 case 'years':
14873                     this.minViewMode = 2;
14874                     break;
14875                 default:
14876                     this.minViewMode = 0;
14877                     break;
14878             }
14879         }
14880         
14881         if (typeof(this.viewMode === 'string')) {
14882             switch (this.viewMode) {
14883                 case 'months':
14884                     this.viewMode = 1;
14885                     break;
14886                 case 'years':
14887                     this.viewMode = 2;
14888                     break;
14889                 default:
14890                     this.viewMode = 0;
14891                     break;
14892             }
14893         }
14894                 
14895         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14896         
14897 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14898         
14899         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14900         
14901         this.picker().on('mousedown', this.onMousedown, this);
14902         this.picker().on('click', this.onClick, this);
14903         
14904         this.picker().addClass('datepicker-dropdown');
14905         
14906         this.startViewMode = this.viewMode;
14907         
14908         if(this.singleMode){
14909             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
14910                 v.setVisibilityMode(Roo.Element.DISPLAY)
14911                 v.hide();
14912             });
14913             
14914             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
14915                 v.setStyle('width', '189px');
14916             });
14917         }
14918         
14919         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14920             if(!this.calendarWeeks){
14921                 v.remove();
14922                 return;
14923             }
14924             
14925             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14926             v.attr('colspan', function(i, val){
14927                 return parseInt(val) + 1;
14928             });
14929         })
14930                         
14931         
14932         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14933         
14934         this.setStartDate(this.startDate);
14935         this.setEndDate(this.endDate);
14936         
14937         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14938         
14939         this.fillDow();
14940         this.fillMonths();
14941         this.update();
14942         this.showMode();
14943         
14944         if(this.isInline) {
14945             this.show();
14946         }
14947     },
14948     
14949     picker : function()
14950     {
14951         return this.pickerEl;
14952 //        return this.el.select('.datepicker', true).first();
14953     },
14954     
14955     fillDow: function()
14956     {
14957         var dowCnt = this.weekStart;
14958         
14959         var dow = {
14960             tag: 'tr',
14961             cn: [
14962                 
14963             ]
14964         };
14965         
14966         if(this.calendarWeeks){
14967             dow.cn.push({
14968                 tag: 'th',
14969                 cls: 'cw',
14970                 html: '&nbsp;'
14971             })
14972         }
14973         
14974         while (dowCnt < this.weekStart + 7) {
14975             dow.cn.push({
14976                 tag: 'th',
14977                 cls: 'dow',
14978                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14979             });
14980         }
14981         
14982         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14983     },
14984     
14985     fillMonths: function()
14986     {    
14987         var i = 0;
14988         var months = this.picker().select('>.datepicker-months td', true).first();
14989         
14990         months.dom.innerHTML = '';
14991         
14992         while (i < 12) {
14993             var month = {
14994                 tag: 'span',
14995                 cls: 'month',
14996                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14997             }
14998             
14999             months.createChild(month);
15000         }
15001         
15002     },
15003     
15004     update: function()
15005     {
15006         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;
15007         
15008         if (this.date < this.startDate) {
15009             this.viewDate = new Date(this.startDate);
15010         } else if (this.date > this.endDate) {
15011             this.viewDate = new Date(this.endDate);
15012         } else {
15013             this.viewDate = new Date(this.date);
15014         }
15015         
15016         this.fill();
15017     },
15018     
15019     fill: function() 
15020     {
15021         var d = new Date(this.viewDate),
15022                 year = d.getUTCFullYear(),
15023                 month = d.getUTCMonth(),
15024                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15025                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15026                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15027                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15028                 currentDate = this.date && this.date.valueOf(),
15029                 today = this.UTCToday();
15030         
15031         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15032         
15033 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15034         
15035 //        this.picker.select('>tfoot th.today').
15036 //                                              .text(dates[this.language].today)
15037 //                                              .toggle(this.todayBtn !== false);
15038     
15039         this.updateNavArrows();
15040         this.fillMonths();
15041                                                 
15042         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15043         
15044         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15045          
15046         prevMonth.setUTCDate(day);
15047         
15048         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15049         
15050         var nextMonth = new Date(prevMonth);
15051         
15052         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15053         
15054         nextMonth = nextMonth.valueOf();
15055         
15056         var fillMonths = false;
15057         
15058         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15059         
15060         while(prevMonth.valueOf() < nextMonth) {
15061             var clsName = '';
15062             
15063             if (prevMonth.getUTCDay() === this.weekStart) {
15064                 if(fillMonths){
15065                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15066                 }
15067                     
15068                 fillMonths = {
15069                     tag: 'tr',
15070                     cn: []
15071                 };
15072                 
15073                 if(this.calendarWeeks){
15074                     // ISO 8601: First week contains first thursday.
15075                     // ISO also states week starts on Monday, but we can be more abstract here.
15076                     var
15077                     // Start of current week: based on weekstart/current date
15078                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15079                     // Thursday of this week
15080                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15081                     // First Thursday of year, year from thursday
15082                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15083                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15084                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15085                     
15086                     fillMonths.cn.push({
15087                         tag: 'td',
15088                         cls: 'cw',
15089                         html: calWeek
15090                     });
15091                 }
15092             }
15093             
15094             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15095                 clsName += ' old';
15096             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15097                 clsName += ' new';
15098             }
15099             if (this.todayHighlight &&
15100                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15101                 prevMonth.getUTCMonth() == today.getMonth() &&
15102                 prevMonth.getUTCDate() == today.getDate()) {
15103                 clsName += ' today';
15104             }
15105             
15106             if (currentDate && prevMonth.valueOf() === currentDate) {
15107                 clsName += ' active';
15108             }
15109             
15110             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15111                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15112                     clsName += ' disabled';
15113             }
15114             
15115             fillMonths.cn.push({
15116                 tag: 'td',
15117                 cls: 'day ' + clsName,
15118                 html: prevMonth.getDate()
15119             })
15120             
15121             prevMonth.setDate(prevMonth.getDate()+1);
15122         }
15123           
15124         var currentYear = this.date && this.date.getUTCFullYear();
15125         var currentMonth = this.date && this.date.getUTCMonth();
15126         
15127         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15128         
15129         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15130             v.removeClass('active');
15131             
15132             if(currentYear === year && k === currentMonth){
15133                 v.addClass('active');
15134             }
15135             
15136             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15137                 v.addClass('disabled');
15138             }
15139             
15140         });
15141         
15142         
15143         year = parseInt(year/10, 10) * 10;
15144         
15145         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15146         
15147         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15148         
15149         year -= 1;
15150         for (var i = -1; i < 11; i++) {
15151             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15152                 tag: 'span',
15153                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15154                 html: year
15155             })
15156             
15157             year += 1;
15158         }
15159     },
15160     
15161     showMode: function(dir) 
15162     {
15163         if (dir) {
15164             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15165         }
15166         
15167         Roo.each(this.picker().select('>div',true).elements, function(v){
15168             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15169             v.hide();
15170         });
15171         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15172     },
15173     
15174     place: function()
15175     {
15176         if(this.isInline) return;
15177         
15178         this.picker().removeClass(['bottom', 'top']);
15179         
15180         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15181             /*
15182              * place to the top of element!
15183              *
15184              */
15185             
15186             this.picker().addClass('top');
15187             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15188             
15189             return;
15190         }
15191         
15192         this.picker().addClass('bottom');
15193         
15194         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15195     },
15196     
15197     parseDate : function(value)
15198     {
15199         if(!value || value instanceof Date){
15200             return value;
15201         }
15202         var v = Date.parseDate(value, this.format);
15203         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15204             v = Date.parseDate(value, 'Y-m-d');
15205         }
15206         if(!v && this.altFormats){
15207             if(!this.altFormatsArray){
15208                 this.altFormatsArray = this.altFormats.split("|");
15209             }
15210             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15211                 v = Date.parseDate(value, this.altFormatsArray[i]);
15212             }
15213         }
15214         return v;
15215     },
15216     
15217     formatDate : function(date, fmt)
15218     {   
15219         return (!date || !(date instanceof Date)) ?
15220         date : date.dateFormat(fmt || this.format);
15221     },
15222     
15223     onFocus : function()
15224     {
15225         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15226         this.show();
15227     },
15228     
15229     onBlur : function()
15230     {
15231         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15232         
15233         var d = this.inputEl().getValue();
15234         
15235         this.setValue(d);
15236                 
15237         this.hide();
15238     },
15239     
15240     show : function()
15241     {
15242         this.picker().show();
15243         this.update();
15244         this.place();
15245         
15246         this.fireEvent('show', this, this.date);
15247     },
15248     
15249     hide : function()
15250     {
15251         if(this.isInline) return;
15252         this.picker().hide();
15253         this.viewMode = this.startViewMode;
15254         this.showMode();
15255         
15256         this.fireEvent('hide', this, this.date);
15257         
15258     },
15259     
15260     onMousedown: function(e)
15261     {
15262         e.stopPropagation();
15263         e.preventDefault();
15264     },
15265     
15266     keyup: function(e)
15267     {
15268         Roo.bootstrap.DateField.superclass.keyup.call(this);
15269         this.update();
15270     },
15271
15272     setValue: function(v)
15273     {
15274         
15275         // v can be a string or a date..
15276         
15277         
15278         var d = new Date(this.parseDate(v) ).clearTime();
15279         
15280         if(isNaN(d.getTime())){
15281             this.date = this.viewDate = '';
15282             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15283             return;
15284         }
15285         
15286         v = this.formatDate(d);
15287         
15288         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15289         
15290         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15291      
15292         this.update();
15293
15294         this.fireEvent('select', this, this.date);
15295         
15296     },
15297     
15298     getValue: function()
15299     {
15300         return this.formatDate(this.date);
15301     },
15302     
15303     fireKey: function(e)
15304     {
15305         if (!this.picker().isVisible()){
15306             if (e.keyCode == 27) // allow escape to hide and re-show picker
15307                 this.show();
15308             return;
15309         }
15310         
15311         var dateChanged = false,
15312         dir, day, month,
15313         newDate, newViewDate;
15314         
15315         switch(e.keyCode){
15316             case 27: // escape
15317                 this.hide();
15318                 e.preventDefault();
15319                 break;
15320             case 37: // left
15321             case 39: // right
15322                 if (!this.keyboardNavigation) break;
15323                 dir = e.keyCode == 37 ? -1 : 1;
15324                 
15325                 if (e.ctrlKey){
15326                     newDate = this.moveYear(this.date, dir);
15327                     newViewDate = this.moveYear(this.viewDate, dir);
15328                 } else if (e.shiftKey){
15329                     newDate = this.moveMonth(this.date, dir);
15330                     newViewDate = this.moveMonth(this.viewDate, dir);
15331                 } else {
15332                     newDate = new Date(this.date);
15333                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15334                     newViewDate = new Date(this.viewDate);
15335                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15336                 }
15337                 if (this.dateWithinRange(newDate)){
15338                     this.date = newDate;
15339                     this.viewDate = newViewDate;
15340                     this.setValue(this.formatDate(this.date));
15341 //                    this.update();
15342                     e.preventDefault();
15343                     dateChanged = true;
15344                 }
15345                 break;
15346             case 38: // up
15347             case 40: // down
15348                 if (!this.keyboardNavigation) break;
15349                 dir = e.keyCode == 38 ? -1 : 1;
15350                 if (e.ctrlKey){
15351                     newDate = this.moveYear(this.date, dir);
15352                     newViewDate = this.moveYear(this.viewDate, dir);
15353                 } else if (e.shiftKey){
15354                     newDate = this.moveMonth(this.date, dir);
15355                     newViewDate = this.moveMonth(this.viewDate, dir);
15356                 } else {
15357                     newDate = new Date(this.date);
15358                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15359                     newViewDate = new Date(this.viewDate);
15360                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15361                 }
15362                 if (this.dateWithinRange(newDate)){
15363                     this.date = newDate;
15364                     this.viewDate = newViewDate;
15365                     this.setValue(this.formatDate(this.date));
15366 //                    this.update();
15367                     e.preventDefault();
15368                     dateChanged = true;
15369                 }
15370                 break;
15371             case 13: // enter
15372                 this.setValue(this.formatDate(this.date));
15373                 this.hide();
15374                 e.preventDefault();
15375                 break;
15376             case 9: // tab
15377                 this.setValue(this.formatDate(this.date));
15378                 this.hide();
15379                 break;
15380             case 16: // shift
15381             case 17: // ctrl
15382             case 18: // alt
15383                 break;
15384             default :
15385                 this.hide();
15386                 
15387         }
15388     },
15389     
15390     
15391     onClick: function(e) 
15392     {
15393         e.stopPropagation();
15394         e.preventDefault();
15395         
15396         var target = e.getTarget();
15397         
15398         if(target.nodeName.toLowerCase() === 'i'){
15399             target = Roo.get(target).dom.parentNode;
15400         }
15401         
15402         var nodeName = target.nodeName;
15403         var className = target.className;
15404         var html = target.innerHTML;
15405         //Roo.log(nodeName);
15406         
15407         switch(nodeName.toLowerCase()) {
15408             case 'th':
15409                 switch(className) {
15410                     case 'switch':
15411                         this.showMode(1);
15412                         break;
15413                     case 'prev':
15414                     case 'next':
15415                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15416                         switch(this.viewMode){
15417                                 case 0:
15418                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15419                                         break;
15420                                 case 1:
15421                                 case 2:
15422                                         this.viewDate = this.moveYear(this.viewDate, dir);
15423                                         break;
15424                         }
15425                         this.fill();
15426                         break;
15427                     case 'today':
15428                         var date = new Date();
15429                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15430 //                        this.fill()
15431                         this.setValue(this.formatDate(this.date));
15432                         
15433                         this.hide();
15434                         break;
15435                 }
15436                 break;
15437             case 'span':
15438                 if (className.indexOf('disabled') < 0) {
15439                     this.viewDate.setUTCDate(1);
15440                     if (className.indexOf('month') > -1) {
15441                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15442                     } else {
15443                         var year = parseInt(html, 10) || 0;
15444                         this.viewDate.setUTCFullYear(year);
15445                         
15446                     }
15447                     
15448                     if(this.singleMode){
15449                         this.setValue(this.formatDate(this.viewDate));
15450                         this.hide();
15451                         return;
15452                     }
15453                     
15454                     this.showMode(-1);
15455                     this.fill();
15456                 }
15457                 break;
15458                 
15459             case 'td':
15460                 //Roo.log(className);
15461                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15462                     var day = parseInt(html, 10) || 1;
15463                     var year = this.viewDate.getUTCFullYear(),
15464                         month = this.viewDate.getUTCMonth();
15465
15466                     if (className.indexOf('old') > -1) {
15467                         if(month === 0 ){
15468                             month = 11;
15469                             year -= 1;
15470                         }else{
15471                             month -= 1;
15472                         }
15473                     } else if (className.indexOf('new') > -1) {
15474                         if (month == 11) {
15475                             month = 0;
15476                             year += 1;
15477                         } else {
15478                             month += 1;
15479                         }
15480                     }
15481                     //Roo.log([year,month,day]);
15482                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15483                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15484 //                    this.fill();
15485                     //Roo.log(this.formatDate(this.date));
15486                     this.setValue(this.formatDate(this.date));
15487                     this.hide();
15488                 }
15489                 break;
15490         }
15491     },
15492     
15493     setStartDate: function(startDate)
15494     {
15495         this.startDate = startDate || -Infinity;
15496         if (this.startDate !== -Infinity) {
15497             this.startDate = this.parseDate(this.startDate);
15498         }
15499         this.update();
15500         this.updateNavArrows();
15501     },
15502
15503     setEndDate: function(endDate)
15504     {
15505         this.endDate = endDate || Infinity;
15506         if (this.endDate !== Infinity) {
15507             this.endDate = this.parseDate(this.endDate);
15508         }
15509         this.update();
15510         this.updateNavArrows();
15511     },
15512     
15513     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15514     {
15515         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15516         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15517             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15518         }
15519         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15520             return parseInt(d, 10);
15521         });
15522         this.update();
15523         this.updateNavArrows();
15524     },
15525     
15526     updateNavArrows: function() 
15527     {
15528         if(this.singleMode){
15529             return;
15530         }
15531         
15532         var d = new Date(this.viewDate),
15533         year = d.getUTCFullYear(),
15534         month = d.getUTCMonth();
15535         
15536         Roo.each(this.picker().select('.prev', true).elements, function(v){
15537             v.show();
15538             switch (this.viewMode) {
15539                 case 0:
15540
15541                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15542                         v.hide();
15543                     }
15544                     break;
15545                 case 1:
15546                 case 2:
15547                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15548                         v.hide();
15549                     }
15550                     break;
15551             }
15552         });
15553         
15554         Roo.each(this.picker().select('.next', true).elements, function(v){
15555             v.show();
15556             switch (this.viewMode) {
15557                 case 0:
15558
15559                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15560                         v.hide();
15561                     }
15562                     break;
15563                 case 1:
15564                 case 2:
15565                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15566                         v.hide();
15567                     }
15568                     break;
15569             }
15570         })
15571     },
15572     
15573     moveMonth: function(date, dir)
15574     {
15575         if (!dir) return date;
15576         var new_date = new Date(date.valueOf()),
15577         day = new_date.getUTCDate(),
15578         month = new_date.getUTCMonth(),
15579         mag = Math.abs(dir),
15580         new_month, test;
15581         dir = dir > 0 ? 1 : -1;
15582         if (mag == 1){
15583             test = dir == -1
15584             // If going back one month, make sure month is not current month
15585             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15586             ? function(){
15587                 return new_date.getUTCMonth() == month;
15588             }
15589             // If going forward one month, make sure month is as expected
15590             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15591             : function(){
15592                 return new_date.getUTCMonth() != new_month;
15593             };
15594             new_month = month + dir;
15595             new_date.setUTCMonth(new_month);
15596             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15597             if (new_month < 0 || new_month > 11)
15598                 new_month = (new_month + 12) % 12;
15599         } else {
15600             // For magnitudes >1, move one month at a time...
15601             for (var i=0; i<mag; i++)
15602                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15603                 new_date = this.moveMonth(new_date, dir);
15604             // ...then reset the day, keeping it in the new month
15605             new_month = new_date.getUTCMonth();
15606             new_date.setUTCDate(day);
15607             test = function(){
15608                 return new_month != new_date.getUTCMonth();
15609             };
15610         }
15611         // Common date-resetting loop -- if date is beyond end of month, make it
15612         // end of month
15613         while (test()){
15614             new_date.setUTCDate(--day);
15615             new_date.setUTCMonth(new_month);
15616         }
15617         return new_date;
15618     },
15619
15620     moveYear: function(date, dir)
15621     {
15622         return this.moveMonth(date, dir*12);
15623     },
15624
15625     dateWithinRange: function(date)
15626     {
15627         return date >= this.startDate && date <= this.endDate;
15628     },
15629
15630     
15631     remove: function() 
15632     {
15633         this.picker().remove();
15634     }
15635    
15636 });
15637
15638 Roo.apply(Roo.bootstrap.DateField,  {
15639     
15640     head : {
15641         tag: 'thead',
15642         cn: [
15643         {
15644             tag: 'tr',
15645             cn: [
15646             {
15647                 tag: 'th',
15648                 cls: 'prev',
15649                 html: '<i class="fa fa-arrow-left"/>'
15650             },
15651             {
15652                 tag: 'th',
15653                 cls: 'switch',
15654                 colspan: '5'
15655             },
15656             {
15657                 tag: 'th',
15658                 cls: 'next',
15659                 html: '<i class="fa fa-arrow-right"/>'
15660             }
15661
15662             ]
15663         }
15664         ]
15665     },
15666     
15667     content : {
15668         tag: 'tbody',
15669         cn: [
15670         {
15671             tag: 'tr',
15672             cn: [
15673             {
15674                 tag: 'td',
15675                 colspan: '7'
15676             }
15677             ]
15678         }
15679         ]
15680     },
15681     
15682     footer : {
15683         tag: 'tfoot',
15684         cn: [
15685         {
15686             tag: 'tr',
15687             cn: [
15688             {
15689                 tag: 'th',
15690                 colspan: '7',
15691                 cls: 'today'
15692             }
15693                     
15694             ]
15695         }
15696         ]
15697     },
15698     
15699     dates:{
15700         en: {
15701             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15702             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15703             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15704             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15705             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15706             today: "Today"
15707         }
15708     },
15709     
15710     modes: [
15711     {
15712         clsName: 'days',
15713         navFnc: 'Month',
15714         navStep: 1
15715     },
15716     {
15717         clsName: 'months',
15718         navFnc: 'FullYear',
15719         navStep: 1
15720     },
15721     {
15722         clsName: 'years',
15723         navFnc: 'FullYear',
15724         navStep: 10
15725     }]
15726 });
15727
15728 Roo.apply(Roo.bootstrap.DateField,  {
15729   
15730     template : {
15731         tag: 'div',
15732         cls: 'datepicker dropdown-menu roo-dynamic',
15733         cn: [
15734         {
15735             tag: 'div',
15736             cls: 'datepicker-days',
15737             cn: [
15738             {
15739                 tag: 'table',
15740                 cls: 'table-condensed',
15741                 cn:[
15742                 Roo.bootstrap.DateField.head,
15743                 {
15744                     tag: 'tbody'
15745                 },
15746                 Roo.bootstrap.DateField.footer
15747                 ]
15748             }
15749             ]
15750         },
15751         {
15752             tag: 'div',
15753             cls: 'datepicker-months',
15754             cn: [
15755             {
15756                 tag: 'table',
15757                 cls: 'table-condensed',
15758                 cn:[
15759                 Roo.bootstrap.DateField.head,
15760                 Roo.bootstrap.DateField.content,
15761                 Roo.bootstrap.DateField.footer
15762                 ]
15763             }
15764             ]
15765         },
15766         {
15767             tag: 'div',
15768             cls: 'datepicker-years',
15769             cn: [
15770             {
15771                 tag: 'table',
15772                 cls: 'table-condensed',
15773                 cn:[
15774                 Roo.bootstrap.DateField.head,
15775                 Roo.bootstrap.DateField.content,
15776                 Roo.bootstrap.DateField.footer
15777                 ]
15778             }
15779             ]
15780         }
15781         ]
15782     }
15783 });
15784
15785  
15786
15787  /*
15788  * - LGPL
15789  *
15790  * TimeField
15791  * 
15792  */
15793
15794 /**
15795  * @class Roo.bootstrap.TimeField
15796  * @extends Roo.bootstrap.Input
15797  * Bootstrap DateField class
15798  * 
15799  * 
15800  * @constructor
15801  * Create a new TimeField
15802  * @param {Object} config The config object
15803  */
15804
15805 Roo.bootstrap.TimeField = function(config){
15806     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15807     this.addEvents({
15808             /**
15809              * @event show
15810              * Fires when this field show.
15811              * @param {Roo.bootstrap.DateField} thisthis
15812              * @param {Mixed} date The date value
15813              */
15814             show : true,
15815             /**
15816              * @event show
15817              * Fires when this field hide.
15818              * @param {Roo.bootstrap.DateField} this
15819              * @param {Mixed} date The date value
15820              */
15821             hide : true,
15822             /**
15823              * @event select
15824              * Fires when select a date.
15825              * @param {Roo.bootstrap.DateField} this
15826              * @param {Mixed} date The date value
15827              */
15828             select : true
15829         });
15830 };
15831
15832 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15833     
15834     /**
15835      * @cfg {String} format
15836      * The default time format string which can be overriden for localization support.  The format must be
15837      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15838      */
15839     format : "H:i",
15840        
15841     onRender: function(ct, position)
15842     {
15843         
15844         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15845                 
15846         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15847         
15848         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15849         
15850         this.pop = this.picker().select('>.datepicker-time',true).first();
15851         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15852         
15853         this.picker().on('mousedown', this.onMousedown, this);
15854         this.picker().on('click', this.onClick, this);
15855         
15856         this.picker().addClass('datepicker-dropdown');
15857     
15858         this.fillTime();
15859         this.update();
15860             
15861         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15862         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15863         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15864         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15865         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15866         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15867
15868     },
15869     
15870     fireKey: function(e){
15871         if (!this.picker().isVisible()){
15872             if (e.keyCode == 27) { // allow escape to hide and re-show picker
15873                 this.show();
15874             }
15875             return;
15876         }
15877
15878         e.preventDefault();
15879         
15880         switch(e.keyCode){
15881             case 27: // escape
15882                 this.hide();
15883                 break;
15884             case 37: // left
15885             case 39: // right
15886                 this.onTogglePeriod();
15887                 break;
15888             case 38: // up
15889                 this.onIncrementMinutes();
15890                 break;
15891             case 40: // down
15892                 this.onDecrementMinutes();
15893                 break;
15894             case 13: // enter
15895             case 9: // tab
15896                 this.setTime();
15897                 break;
15898         }
15899     },
15900     
15901     onClick: function(e) {
15902         e.stopPropagation();
15903         e.preventDefault();
15904     },
15905     
15906     picker : function()
15907     {
15908         return this.el.select('.datepicker', true).first();
15909     },
15910     
15911     fillTime: function()
15912     {    
15913         var time = this.pop.select('tbody', true).first();
15914         
15915         time.dom.innerHTML = '';
15916         
15917         time.createChild({
15918             tag: 'tr',
15919             cn: [
15920                 {
15921                     tag: 'td',
15922                     cn: [
15923                         {
15924                             tag: 'a',
15925                             href: '#',
15926                             cls: 'btn',
15927                             cn: [
15928                                 {
15929                                     tag: 'span',
15930                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15931                                 }
15932                             ]
15933                         } 
15934                     ]
15935                 },
15936                 {
15937                     tag: 'td',
15938                     cls: 'separator'
15939                 },
15940                 {
15941                     tag: 'td',
15942                     cn: [
15943                         {
15944                             tag: 'a',
15945                             href: '#',
15946                             cls: 'btn',
15947                             cn: [
15948                                 {
15949                                     tag: 'span',
15950                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15951                                 }
15952                             ]
15953                         }
15954                     ]
15955                 },
15956                 {
15957                     tag: 'td',
15958                     cls: 'separator'
15959                 }
15960             ]
15961         });
15962         
15963         time.createChild({
15964             tag: 'tr',
15965             cn: [
15966                 {
15967                     tag: 'td',
15968                     cn: [
15969                         {
15970                             tag: 'span',
15971                             cls: 'timepicker-hour',
15972                             html: '00'
15973                         }  
15974                     ]
15975                 },
15976                 {
15977                     tag: 'td',
15978                     cls: 'separator',
15979                     html: ':'
15980                 },
15981                 {
15982                     tag: 'td',
15983                     cn: [
15984                         {
15985                             tag: 'span',
15986                             cls: 'timepicker-minute',
15987                             html: '00'
15988                         }  
15989                     ]
15990                 },
15991                 {
15992                     tag: 'td',
15993                     cls: 'separator'
15994                 },
15995                 {
15996                     tag: 'td',
15997                     cn: [
15998                         {
15999                             tag: 'button',
16000                             type: 'button',
16001                             cls: 'btn btn-primary period',
16002                             html: 'AM'
16003                             
16004                         }
16005                     ]
16006                 }
16007             ]
16008         });
16009         
16010         time.createChild({
16011             tag: 'tr',
16012             cn: [
16013                 {
16014                     tag: 'td',
16015                     cn: [
16016                         {
16017                             tag: 'a',
16018                             href: '#',
16019                             cls: 'btn',
16020                             cn: [
16021                                 {
16022                                     tag: 'span',
16023                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16024                                 }
16025                             ]
16026                         }
16027                     ]
16028                 },
16029                 {
16030                     tag: 'td',
16031                     cls: 'separator'
16032                 },
16033                 {
16034                     tag: 'td',
16035                     cn: [
16036                         {
16037                             tag: 'a',
16038                             href: '#',
16039                             cls: 'btn',
16040                             cn: [
16041                                 {
16042                                     tag: 'span',
16043                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16044                                 }
16045                             ]
16046                         }
16047                     ]
16048                 },
16049                 {
16050                     tag: 'td',
16051                     cls: 'separator'
16052                 }
16053             ]
16054         });
16055         
16056     },
16057     
16058     update: function()
16059     {
16060         
16061         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16062         
16063         this.fill();
16064     },
16065     
16066     fill: function() 
16067     {
16068         var hours = this.time.getHours();
16069         var minutes = this.time.getMinutes();
16070         var period = 'AM';
16071         
16072         if(hours > 11){
16073             period = 'PM';
16074         }
16075         
16076         if(hours == 0){
16077             hours = 12;
16078         }
16079         
16080         
16081         if(hours > 12){
16082             hours = hours - 12;
16083         }
16084         
16085         if(hours < 10){
16086             hours = '0' + hours;
16087         }
16088         
16089         if(minutes < 10){
16090             minutes = '0' + minutes;
16091         }
16092         
16093         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16094         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16095         this.pop.select('button', true).first().dom.innerHTML = period;
16096         
16097     },
16098     
16099     place: function()
16100     {   
16101         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16102         
16103         var cls = ['bottom'];
16104         
16105         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16106             cls.pop();
16107             cls.push('top');
16108         }
16109         
16110         cls.push('right');
16111         
16112         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16113             cls.pop();
16114             cls.push('left');
16115         }
16116         
16117         this.picker().addClass(cls.join('-'));
16118         
16119         var _this = this;
16120         
16121         Roo.each(cls, function(c){
16122             if(c == 'bottom'){
16123                 _this.picker().setTop(_this.inputEl().getHeight());
16124                 return;
16125             }
16126             if(c == 'top'){
16127                 _this.picker().setTop(0 - _this.picker().getHeight());
16128                 return;
16129             }
16130             
16131             if(c == 'left'){
16132                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16133                 return;
16134             }
16135             if(c == 'right'){
16136                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16137                 return;
16138             }
16139         });
16140         
16141     },
16142   
16143     onFocus : function()
16144     {
16145         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16146         this.show();
16147     },
16148     
16149     onBlur : function()
16150     {
16151         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16152         this.hide();
16153     },
16154     
16155     show : function()
16156     {
16157         this.picker().show();
16158         this.pop.show();
16159         this.update();
16160         this.place();
16161         
16162         this.fireEvent('show', this, this.date);
16163     },
16164     
16165     hide : function()
16166     {
16167         this.picker().hide();
16168         this.pop.hide();
16169         
16170         this.fireEvent('hide', this, this.date);
16171     },
16172     
16173     setTime : function()
16174     {
16175         this.hide();
16176         this.setValue(this.time.format(this.format));
16177         
16178         this.fireEvent('select', this, this.date);
16179         
16180         
16181     },
16182     
16183     onMousedown: function(e){
16184         e.stopPropagation();
16185         e.preventDefault();
16186     },
16187     
16188     onIncrementHours: function()
16189     {
16190         Roo.log('onIncrementHours');
16191         this.time = this.time.add(Date.HOUR, 1);
16192         this.update();
16193         
16194     },
16195     
16196     onDecrementHours: function()
16197     {
16198         Roo.log('onDecrementHours');
16199         this.time = this.time.add(Date.HOUR, -1);
16200         this.update();
16201     },
16202     
16203     onIncrementMinutes: function()
16204     {
16205         Roo.log('onIncrementMinutes');
16206         this.time = this.time.add(Date.MINUTE, 1);
16207         this.update();
16208     },
16209     
16210     onDecrementMinutes: function()
16211     {
16212         Roo.log('onDecrementMinutes');
16213         this.time = this.time.add(Date.MINUTE, -1);
16214         this.update();
16215     },
16216     
16217     onTogglePeriod: function()
16218     {
16219         Roo.log('onTogglePeriod');
16220         this.time = this.time.add(Date.HOUR, 12);
16221         this.update();
16222     }
16223     
16224    
16225 });
16226
16227 Roo.apply(Roo.bootstrap.TimeField,  {
16228     
16229     content : {
16230         tag: 'tbody',
16231         cn: [
16232             {
16233                 tag: 'tr',
16234                 cn: [
16235                 {
16236                     tag: 'td',
16237                     colspan: '7'
16238                 }
16239                 ]
16240             }
16241         ]
16242     },
16243     
16244     footer : {
16245         tag: 'tfoot',
16246         cn: [
16247             {
16248                 tag: 'tr',
16249                 cn: [
16250                 {
16251                     tag: 'th',
16252                     colspan: '7',
16253                     cls: '',
16254                     cn: [
16255                         {
16256                             tag: 'button',
16257                             cls: 'btn btn-info ok',
16258                             html: 'OK'
16259                         }
16260                     ]
16261                 }
16262
16263                 ]
16264             }
16265         ]
16266     }
16267 });
16268
16269 Roo.apply(Roo.bootstrap.TimeField,  {
16270   
16271     template : {
16272         tag: 'div',
16273         cls: 'datepicker dropdown-menu',
16274         cn: [
16275             {
16276                 tag: 'div',
16277                 cls: 'datepicker-time',
16278                 cn: [
16279                 {
16280                     tag: 'table',
16281                     cls: 'table-condensed',
16282                     cn:[
16283                     Roo.bootstrap.TimeField.content,
16284                     Roo.bootstrap.TimeField.footer
16285                     ]
16286                 }
16287                 ]
16288             }
16289         ]
16290     }
16291 });
16292
16293  
16294
16295  /*
16296  * - LGPL
16297  *
16298  * MonthField
16299  * 
16300  */
16301
16302 /**
16303  * @class Roo.bootstrap.MonthField
16304  * @extends Roo.bootstrap.Input
16305  * Bootstrap MonthField class
16306  * 
16307  * @cfg {String} language default en
16308  * 
16309  * @constructor
16310  * Create a new MonthField
16311  * @param {Object} config The config object
16312  */
16313
16314 Roo.bootstrap.MonthField = function(config){
16315     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16316     
16317     this.addEvents({
16318         /**
16319          * @event show
16320          * Fires when this field show.
16321          * @param {Roo.bootstrap.MonthField} this
16322          * @param {Mixed} date The date value
16323          */
16324         show : true,
16325         /**
16326          * @event show
16327          * Fires when this field hide.
16328          * @param {Roo.bootstrap.MonthField} this
16329          * @param {Mixed} date The date value
16330          */
16331         hide : true,
16332         /**
16333          * @event select
16334          * Fires when select a date.
16335          * @param {Roo.bootstrap.MonthField} this
16336          * @param {String} oldvalue The old value
16337          * @param {String} newvalue The new value
16338          */
16339         select : true
16340     });
16341 };
16342
16343 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16344     
16345     onRender: function(ct, position)
16346     {
16347         
16348         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16349         
16350         this.language = this.language || 'en';
16351         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16352         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16353         
16354         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16355         this.isInline = false;
16356         this.isInput = true;
16357         this.component = this.el.select('.add-on', true).first() || false;
16358         this.component = (this.component && this.component.length === 0) ? false : this.component;
16359         this.hasInput = this.component && this.inputEL().length;
16360         
16361         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16362         
16363         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16364         
16365         this.picker().on('mousedown', this.onMousedown, this);
16366         this.picker().on('click', this.onClick, this);
16367         
16368         this.picker().addClass('datepicker-dropdown');
16369         
16370         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16371             v.setStyle('width', '189px');
16372         });
16373         
16374         this.fillMonths();
16375         
16376         this.update();
16377         
16378         if(this.isInline) {
16379             this.show();
16380         }
16381         
16382     },
16383     
16384     setValue: function(v, suppressEvent)
16385     {   
16386         var o = this.getValue();
16387         
16388         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16389         
16390         this.update();
16391
16392         if(suppressEvent !== true){
16393             this.fireEvent('select', this, o, v);
16394         }
16395         
16396     },
16397     
16398     getValue: function()
16399     {
16400         return this.value;
16401     },
16402     
16403     onClick: function(e) 
16404     {
16405         e.stopPropagation();
16406         e.preventDefault();
16407         
16408         var target = e.getTarget();
16409         
16410         if(target.nodeName.toLowerCase() === 'i'){
16411             target = Roo.get(target).dom.parentNode;
16412         }
16413         
16414         var nodeName = target.nodeName;
16415         var className = target.className;
16416         var html = target.innerHTML;
16417         
16418         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16419             return;
16420         }
16421         
16422         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16423         
16424         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16425         
16426         this.hide();
16427                         
16428     },
16429     
16430     picker : function()
16431     {
16432         return this.pickerEl;
16433     },
16434     
16435     fillMonths: function()
16436     {    
16437         var i = 0;
16438         var months = this.picker().select('>.datepicker-months td', true).first();
16439         
16440         months.dom.innerHTML = '';
16441         
16442         while (i < 12) {
16443             var month = {
16444                 tag: 'span',
16445                 cls: 'month',
16446                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16447             }
16448             
16449             months.createChild(month);
16450         }
16451         
16452     },
16453     
16454     update: function()
16455     {
16456         var _this = this;
16457         
16458         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16459             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16460         }
16461         
16462         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16463             e.removeClass('active');
16464             
16465             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16466                 e.addClass('active');
16467             }
16468         })
16469     },
16470     
16471     place: function()
16472     {
16473         if(this.isInline) return;
16474         
16475         this.picker().removeClass(['bottom', 'top']);
16476         
16477         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16478             /*
16479              * place to the top of element!
16480              *
16481              */
16482             
16483             this.picker().addClass('top');
16484             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16485             
16486             return;
16487         }
16488         
16489         this.picker().addClass('bottom');
16490         
16491         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16492     },
16493     
16494     onFocus : function()
16495     {
16496         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
16497         this.show();
16498     },
16499     
16500     onBlur : function()
16501     {
16502         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
16503         
16504         var d = this.inputEl().getValue();
16505         
16506         this.setValue(d);
16507                 
16508         this.hide();
16509     },
16510     
16511     show : function()
16512     {
16513         this.picker().show();
16514         this.picker().select('>.datepicker-months', true).first().show();
16515         this.update();
16516         this.place();
16517         
16518         this.fireEvent('show', this, this.date);
16519     },
16520     
16521     hide : function()
16522     {
16523         if(this.isInline) return;
16524         this.picker().hide();
16525         this.fireEvent('hide', this, this.date);
16526         
16527     },
16528     
16529     onMousedown: function(e)
16530     {
16531         e.stopPropagation();
16532         e.preventDefault();
16533     },
16534     
16535     keyup: function(e)
16536     {
16537         Roo.bootstrap.MonthField.superclass.keyup.call(this);
16538         this.update();
16539     },
16540
16541     fireKey: function(e)
16542     {
16543         if (!this.picker().isVisible()){
16544             if (e.keyCode == 27) // allow escape to hide and re-show picker
16545                 this.show();
16546             return;
16547         }
16548         
16549         var dir;
16550         
16551         switch(e.keyCode){
16552             case 27: // escape
16553                 this.hide();
16554                 e.preventDefault();
16555                 break;
16556             case 37: // left
16557             case 39: // right
16558                 dir = e.keyCode == 37 ? -1 : 1;
16559                 
16560                 this.vIndex = this.vIndex + dir;
16561                 
16562                 if(this.vIndex < 0){
16563                     this.vIndex = 0;
16564                 }
16565                 
16566                 if(this.vIndex > 11){
16567                     this.vIndex = 11;
16568                 }
16569                 
16570                 if(isNaN(this.vIndex)){
16571                     this.vIndex = 0;
16572                 }
16573                 
16574                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16575                 
16576                 break;
16577             case 38: // up
16578             case 40: // down
16579                 
16580                 dir = e.keyCode == 38 ? -1 : 1;
16581                 
16582                 this.vIndex = this.vIndex + dir * 4;
16583                 
16584                 if(this.vIndex < 0){
16585                     this.vIndex = 0;
16586                 }
16587                 
16588                 if(this.vIndex > 11){
16589                     this.vIndex = 11;
16590                 }
16591                 
16592                 if(isNaN(this.vIndex)){
16593                     this.vIndex = 0;
16594                 }
16595                 
16596                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16597                 break;
16598                 
16599             case 13: // enter
16600                 
16601                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16602                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16603                 }
16604                 
16605                 this.hide();
16606                 e.preventDefault();
16607                 break;
16608             case 9: // tab
16609                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16610                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16611                 }
16612                 this.hide();
16613                 break;
16614             case 16: // shift
16615             case 17: // ctrl
16616             case 18: // alt
16617                 break;
16618             default :
16619                 this.hide();
16620                 
16621         }
16622     },
16623     
16624     remove: function() 
16625     {
16626         this.picker().remove();
16627     }
16628    
16629 });
16630
16631 Roo.apply(Roo.bootstrap.MonthField,  {
16632     
16633     content : {
16634         tag: 'tbody',
16635         cn: [
16636         {
16637             tag: 'tr',
16638             cn: [
16639             {
16640                 tag: 'td',
16641                 colspan: '7'
16642             }
16643             ]
16644         }
16645         ]
16646     },
16647     
16648     dates:{
16649         en: {
16650             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16651             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
16652         }
16653     }
16654 });
16655
16656 Roo.apply(Roo.bootstrap.MonthField,  {
16657   
16658     template : {
16659         tag: 'div',
16660         cls: 'datepicker dropdown-menu roo-dynamic',
16661         cn: [
16662             {
16663                 tag: 'div',
16664                 cls: 'datepicker-months',
16665                 cn: [
16666                 {
16667                     tag: 'table',
16668                     cls: 'table-condensed',
16669                     cn:[
16670                         Roo.bootstrap.DateField.content
16671                     ]
16672                 }
16673                 ]
16674             }
16675         ]
16676     }
16677 });
16678
16679  
16680
16681  
16682  /*
16683  * - LGPL
16684  *
16685  * CheckBox
16686  * 
16687  */
16688
16689 /**
16690  * @class Roo.bootstrap.CheckBox
16691  * @extends Roo.bootstrap.Input
16692  * Bootstrap CheckBox class
16693  * 
16694  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
16695  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
16696  * @cfg {String} boxLabel The text that appears beside the checkbox
16697  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
16698  * @cfg {Boolean} checked initnal the element
16699  * @cfg {Boolean} inline inline the element (default false)
16700  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
16701  * 
16702  * @constructor
16703  * Create a new CheckBox
16704  * @param {Object} config The config object
16705  */
16706
16707 Roo.bootstrap.CheckBox = function(config){
16708     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
16709    
16710     this.addEvents({
16711         /**
16712         * @event check
16713         * Fires when the element is checked or unchecked.
16714         * @param {Roo.bootstrap.CheckBox} this This input
16715         * @param {Boolean} checked The new checked value
16716         */
16717        check : true
16718     });
16719     
16720 };
16721
16722 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
16723   
16724     inputType: 'checkbox',
16725     inputValue: 1,
16726     valueOff: 0,
16727     boxLabel: false,
16728     checked: false,
16729     weight : false,
16730     inline: false,
16731     
16732     getAutoCreate : function()
16733     {
16734         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16735         
16736         var id = Roo.id();
16737         
16738         var cfg = {};
16739         
16740         cfg.cls = 'form-group ' + this.inputType; //input-group
16741         
16742         if(this.inline){
16743             cfg.cls += ' ' + this.inputType + '-inline';
16744         }
16745         
16746         var input =  {
16747             tag: 'input',
16748             id : id,
16749             type : this.inputType,
16750             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
16751             cls : 'roo-' + this.inputType, //'form-box',
16752             placeholder : this.placeholder || ''
16753             
16754         };
16755         
16756         if (this.weight) { // Validity check?
16757             cfg.cls += " " + this.inputType + "-" + this.weight;
16758         }
16759         
16760         if (this.disabled) {
16761             input.disabled=true;
16762         }
16763         
16764         if(this.checked){
16765             input.checked = this.checked;
16766         }
16767         
16768         if (this.name) {
16769             input.name = this.name;
16770         }
16771         
16772         if (this.size) {
16773             input.cls += ' input-' + this.size;
16774         }
16775         
16776         var settings=this;
16777         
16778         ['xs','sm','md','lg'].map(function(size){
16779             if (settings[size]) {
16780                 cfg.cls += ' col-' + size + '-' + settings[size];
16781             }
16782         });
16783         
16784         var inputblock = input;
16785          
16786         if (this.before || this.after) {
16787             
16788             inputblock = {
16789                 cls : 'input-group',
16790                 cn :  [] 
16791             };
16792             
16793             if (this.before) {
16794                 inputblock.cn.push({
16795                     tag :'span',
16796                     cls : 'input-group-addon',
16797                     html : this.before
16798                 });
16799             }
16800             
16801             inputblock.cn.push(input);
16802             
16803             if (this.after) {
16804                 inputblock.cn.push({
16805                     tag :'span',
16806                     cls : 'input-group-addon',
16807                     html : this.after
16808                 });
16809             }
16810             
16811         }
16812         
16813         if (align ==='left' && this.fieldLabel.length) {
16814                 Roo.log("left and has label");
16815                 cfg.cn = [
16816                     
16817                     {
16818                         tag: 'label',
16819                         'for' :  id,
16820                         cls : 'control-label col-md-' + this.labelWidth,
16821                         html : this.fieldLabel
16822                         
16823                     },
16824                     {
16825                         cls : "col-md-" + (12 - this.labelWidth), 
16826                         cn: [
16827                             inputblock
16828                         ]
16829                     }
16830                     
16831                 ];
16832         } else if ( this.fieldLabel.length) {
16833                 Roo.log(" label");
16834                 cfg.cn = [
16835                    
16836                     {
16837                         tag: this.boxLabel ? 'span' : 'label',
16838                         'for': id,
16839                         cls: 'control-label box-input-label',
16840                         //cls : 'input-group-addon',
16841                         html : this.fieldLabel
16842                         
16843                     },
16844                     
16845                     inputblock
16846                     
16847                 ];
16848
16849         } else {
16850             
16851                 Roo.log(" no label && no align");
16852                 cfg.cn = [  inputblock ] ;
16853                 
16854                 
16855         }
16856         if(this.boxLabel){
16857              var boxLabelCfg = {
16858                 tag: 'label',
16859                 //'for': id, // box label is handled by onclick - so no for...
16860                 cls: 'box-label',
16861                 html: this.boxLabel
16862             }
16863             
16864             if(this.tooltip){
16865                 boxLabelCfg.tooltip = this.tooltip;
16866             }
16867              
16868             cfg.cn.push(boxLabelCfg);
16869         }
16870         
16871         
16872        
16873         return cfg;
16874         
16875     },
16876     
16877     /**
16878      * return the real input element.
16879      */
16880     inputEl: function ()
16881     {
16882         return this.el.select('input.roo-' + this.inputType,true).first();
16883     },
16884     
16885     labelEl: function()
16886     {
16887         return this.el.select('label.control-label',true).first();
16888     },
16889     /* depricated... */
16890     
16891     label: function()
16892     {
16893         return this.labelEl();
16894     },
16895     
16896     initEvents : function()
16897     {
16898 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16899         
16900         this.inputEl().on('click', this.onClick,  this);
16901         
16902         if (this.boxLabel) { 
16903             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
16904         }
16905         
16906         this.startValue = this.getValue();
16907         
16908         if(this.groupId){
16909             Roo.bootstrap.CheckBox.register(this);
16910         }
16911     },
16912     
16913     onClick : function()
16914     {   
16915         this.setChecked(!this.checked);
16916     },
16917     
16918     setChecked : function(state,suppressEvent)
16919     {
16920         this.startValue = this.getValue();
16921         
16922         if(this.inputType == 'radio'){
16923             
16924             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
16925                 e.dom.checked = false;
16926             });
16927             
16928             this.inputEl().dom.checked = true;
16929             
16930             this.inputEl().dom.value = this.inputValue;
16931             
16932             if(suppressEvent !== true){
16933                 this.fireEvent('check', this, true);
16934             }
16935             
16936             this.validate();
16937             
16938             return;
16939         }
16940         
16941         this.checked = state;
16942         
16943         this.inputEl().dom.checked = state;
16944         
16945         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16946         
16947         if(suppressEvent !== true){
16948             this.fireEvent('check', this, state);
16949         }
16950         
16951         this.validate();
16952     },
16953     
16954     getValue : function()
16955     {
16956         if(this.inputType == 'radio'){
16957             return this.getGroupValue();
16958         }
16959         
16960         return this.inputEl().getValue();
16961         
16962     },
16963     
16964     getGroupValue : function()
16965     {
16966         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
16967             return '';
16968         }
16969         
16970         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
16971     },
16972     
16973     setValue : function(v,suppressEvent)
16974     {
16975         if(this.inputType == 'radio'){
16976             this.setGroupValue(v, suppressEvent);
16977             return;
16978         }
16979         
16980         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16981         
16982         this.validate();
16983     },
16984     
16985     setGroupValue : function(v, suppressEvent)
16986     {
16987         this.startValue = this.getValue();
16988         
16989         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
16990             e.dom.checked = false;
16991             
16992             if(e.dom.value == v){
16993                 e.dom.checked = true;
16994             }
16995         });
16996         
16997         if(suppressEvent !== true){
16998             this.fireEvent('check', this, true);
16999         }
17000
17001         this.validate();
17002         
17003         return;
17004     },
17005     
17006     validate : function()
17007     {
17008         if(
17009                 this.disabled || 
17010                 (this.inputType == 'radio' && this.validateRadio()) ||
17011                 (this.inputType == 'checkbox' && this.validateCheckbox())
17012         ){
17013             this.markValid();
17014             return true;
17015         }
17016         
17017         this.markInvalid();
17018         return false;
17019     },
17020     
17021     validateRadio : function()
17022     {
17023         var valid = false;
17024         
17025         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17026             if(!e.dom.checked){
17027                 return;
17028             }
17029             
17030             valid = true;
17031             
17032             return false;
17033         });
17034         
17035         return valid;
17036     },
17037     
17038     validateCheckbox : function()
17039     {
17040         if(!this.groupId){
17041             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17042         }
17043         
17044         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17045         
17046         if(!group){
17047             return false;
17048         }
17049         
17050         var r = false;
17051         
17052         for(var i in group){
17053             if(r){
17054                 break;
17055             }
17056             
17057             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17058         }
17059         
17060         return r;
17061     },
17062     
17063     /**
17064      * Mark this field as valid
17065      */
17066     markValid : function()
17067     {
17068         var _this = this;
17069         
17070         this.fireEvent('valid', this);
17071         
17072         if(this.inputType == 'radio'){
17073             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17074                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17075                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17076             });
17077             
17078             return;
17079         }
17080         
17081         if(!this.groupId){
17082             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17083             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17084             return;
17085         }
17086         
17087         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17088             
17089         if(!group){
17090             return;
17091         }
17092         
17093         for(var i in group){
17094             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17095             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17096         }
17097     },
17098     
17099      /**
17100      * Mark this field as invalid
17101      * @param {String} msg The validation message
17102      */
17103     markInvalid : function(msg)
17104     {
17105         var _this = this;
17106         
17107         this.fireEvent('invalid', this, msg);
17108         
17109         if(this.inputType == 'radio'){
17110             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17111                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17112                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17113             });
17114             
17115             return;
17116         }
17117         
17118         if(!this.groupId){
17119             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17120             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17121             return;
17122         }
17123         
17124         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17125             
17126         if(!group){
17127             return;
17128         }
17129         
17130         for(var i in group){
17131             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17132             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17133         }
17134         
17135     }
17136     
17137 });
17138
17139 Roo.apply(Roo.bootstrap.CheckBox, {
17140     
17141     groups: {},
17142     
17143      /**
17144     * register a CheckBox Group
17145     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17146     */
17147     register : function(checkbox)
17148     {
17149         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17150             this.groups[checkbox.groupId] = {};
17151         }
17152         
17153         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17154             return;
17155         }
17156         
17157         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17158         
17159     },
17160     /**
17161     * fetch a CheckBox Group based on the group ID
17162     * @param {string} the group ID
17163     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17164     */
17165     get: function(groupId) {
17166         if (typeof(this.groups[groupId]) == 'undefined') {
17167             return false;
17168         }
17169         
17170         return this.groups[groupId] ;
17171     }
17172     
17173     
17174 });
17175 /*
17176  * - LGPL
17177  *
17178  * Radio
17179  *
17180  *
17181  * not inline
17182  *<div class="radio">
17183   <label>
17184     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17185     Option one is this and that&mdash;be sure to include why it's great
17186   </label>
17187 </div>
17188  *
17189  *
17190  *inline
17191  *<span>
17192  *<label class="radio-inline">fieldLabel</label>
17193  *<label class="radio-inline">
17194   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17195 </label>
17196 <span>
17197  * 
17198  * 
17199  */
17200
17201 /**
17202  * @class Roo.bootstrap.Radio
17203  * @extends Roo.bootstrap.CheckBox
17204  * Bootstrap Radio class
17205
17206  * @constructor
17207  * Create a new Radio
17208  * @param {Object} config The config object
17209  */
17210
17211 Roo.bootstrap.Radio = function(config){
17212     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17213    
17214 };
17215
17216 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17217     
17218     inputType: 'radio',
17219     inputValue: '',
17220     valueOff: '',
17221     
17222     getAutoCreate : function()
17223     {
17224         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17225         align = align || 'left'; // default...
17226         
17227         
17228         
17229         var id = Roo.id();
17230         
17231         var cfg = {
17232                 tag : this.inline ? 'span' : 'div',
17233                 cls : '',
17234                 cn : []
17235         };
17236         
17237         var inline = this.inline ? ' radio-inline' : '';
17238         
17239         var lbl = {
17240                 tag: 'label' ,
17241                 // does not need for, as we wrap the input with it..
17242                 'for' : id,
17243                 cls : 'control-label box-label' + inline,
17244                 cn : []
17245         };
17246         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17247         
17248         var fieldLabel = {
17249             tag: 'label' ,
17250             //cls : 'control-label' + inline,
17251             html : this.fieldLabel,
17252             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17253         };
17254         
17255  
17256         
17257         
17258         var input =  {
17259             tag: 'input',
17260             id : id,
17261             type : this.inputType,
17262             //value : (!this.checked) ? this.valueOff : this.inputValue,
17263             value : this.inputValue,
17264             cls : 'roo-radio',
17265             placeholder : this.placeholder || '' // ?? needed????
17266             
17267         };
17268         if (this.weight) { // Validity check?
17269             input.cls += " radio-" + this.weight;
17270         }
17271         if (this.disabled) {
17272             input.disabled=true;
17273         }
17274         
17275         if(this.checked){
17276             input.checked = this.checked;
17277         }
17278         
17279         if (this.name) {
17280             input.name = this.name;
17281         }
17282         
17283         if (this.size) {
17284             input.cls += ' input-' + this.size;
17285         }
17286         
17287         //?? can span's inline have a width??
17288         
17289         var settings=this;
17290         ['xs','sm','md','lg'].map(function(size){
17291             if (settings[size]) {
17292                 cfg.cls += ' col-' + size + '-' + settings[size];
17293             }
17294         });
17295         
17296         var inputblock = input;
17297         
17298         if (this.before || this.after) {
17299             
17300             inputblock = {
17301                 cls : 'input-group',
17302                 tag : 'span',
17303                 cn :  [] 
17304             };
17305             if (this.before) {
17306                 inputblock.cn.push({
17307                     tag :'span',
17308                     cls : 'input-group-addon',
17309                     html : this.before
17310                 });
17311             }
17312             inputblock.cn.push(input);
17313             if (this.after) {
17314                 inputblock.cn.push({
17315                     tag :'span',
17316                     cls : 'input-group-addon',
17317                     html : this.after
17318                 });
17319             }
17320             
17321         };
17322         
17323         
17324         if (this.fieldLabel && this.fieldLabel.length) {
17325             cfg.cn.push(fieldLabel);
17326         }
17327        
17328         // normal bootstrap puts the input inside the label.
17329         // however with our styled version - it has to go after the input.
17330        
17331         //lbl.cn.push(inputblock);
17332         
17333         var lblwrap =  {
17334             tag: 'span',
17335             cls: 'radio' + inline,
17336             cn: [
17337                 inputblock,
17338                 lbl
17339             ]
17340         };
17341         
17342         cfg.cn.push( lblwrap);
17343         
17344         if(this.boxLabel){
17345             lbl.cn.push({
17346                 tag: 'span',
17347                 html: this.boxLabel
17348             })
17349         }
17350          
17351         
17352         return cfg;
17353         
17354     },
17355     
17356     initEvents : function()
17357     {
17358 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17359         
17360         this.inputEl().on('click', this.onClick,  this);
17361         if (this.boxLabel) {
17362             Roo.log('find label')
17363             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17364         }
17365         
17366     },
17367     
17368     inputEl: function ()
17369     {
17370         return this.el.select('input.roo-radio',true).first();
17371     },
17372     onClick : function()
17373     {   
17374         Roo.log("click");
17375         this.setChecked(true);
17376     },
17377     
17378     setChecked : function(state,suppressEvent)
17379     {
17380         if(state){
17381             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17382                 v.dom.checked = false;
17383             });
17384         }
17385         Roo.log(this.inputEl().dom);
17386         this.checked = state;
17387         this.inputEl().dom.checked = state;
17388         
17389         if(suppressEvent !== true){
17390             this.fireEvent('check', this, state);
17391         }
17392         
17393         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17394         
17395     },
17396     
17397     getGroupValue : function()
17398     {
17399         var value = '';
17400         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17401             if(v.dom.checked == true){
17402                 value = v.dom.value;
17403             }
17404         });
17405         
17406         return value;
17407     },
17408     
17409     /**
17410      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17411      * @return {Mixed} value The field value
17412      */
17413     getValue : function(){
17414         return this.getGroupValue();
17415     }
17416     
17417 });
17418
17419  
17420 //<script type="text/javascript">
17421
17422 /*
17423  * Based  Ext JS Library 1.1.1
17424  * Copyright(c) 2006-2007, Ext JS, LLC.
17425  * LGPL
17426  *
17427  */
17428  
17429 /**
17430  * @class Roo.HtmlEditorCore
17431  * @extends Roo.Component
17432  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17433  *
17434  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17435  */
17436
17437 Roo.HtmlEditorCore = function(config){
17438     
17439     
17440     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17441     
17442     
17443     this.addEvents({
17444         /**
17445          * @event initialize
17446          * Fires when the editor is fully initialized (including the iframe)
17447          * @param {Roo.HtmlEditorCore} this
17448          */
17449         initialize: true,
17450         /**
17451          * @event activate
17452          * Fires when the editor is first receives the focus. Any insertion must wait
17453          * until after this event.
17454          * @param {Roo.HtmlEditorCore} this
17455          */
17456         activate: true,
17457          /**
17458          * @event beforesync
17459          * Fires before the textarea is updated with content from the editor iframe. Return false
17460          * to cancel the sync.
17461          * @param {Roo.HtmlEditorCore} this
17462          * @param {String} html
17463          */
17464         beforesync: true,
17465          /**
17466          * @event beforepush
17467          * Fires before the iframe editor is updated with content from the textarea. Return false
17468          * to cancel the push.
17469          * @param {Roo.HtmlEditorCore} this
17470          * @param {String} html
17471          */
17472         beforepush: true,
17473          /**
17474          * @event sync
17475          * Fires when the textarea is updated with content from the editor iframe.
17476          * @param {Roo.HtmlEditorCore} this
17477          * @param {String} html
17478          */
17479         sync: true,
17480          /**
17481          * @event push
17482          * Fires when the iframe editor is updated with content from the textarea.
17483          * @param {Roo.HtmlEditorCore} this
17484          * @param {String} html
17485          */
17486         push: true,
17487         
17488         /**
17489          * @event editorevent
17490          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17491          * @param {Roo.HtmlEditorCore} this
17492          */
17493         editorevent: true
17494         
17495     });
17496     
17497     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
17498     
17499     // defaults : white / black...
17500     this.applyBlacklists();
17501     
17502     
17503     
17504 };
17505
17506
17507 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
17508
17509
17510      /**
17511      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
17512      */
17513     
17514     owner : false,
17515     
17516      /**
17517      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17518      *                        Roo.resizable.
17519      */
17520     resizable : false,
17521      /**
17522      * @cfg {Number} height (in pixels)
17523      */   
17524     height: 300,
17525    /**
17526      * @cfg {Number} width (in pixels)
17527      */   
17528     width: 500,
17529     
17530     /**
17531      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17532      * 
17533      */
17534     stylesheets: false,
17535     
17536     // id of frame..
17537     frameId: false,
17538     
17539     // private properties
17540     validationEvent : false,
17541     deferHeight: true,
17542     initialized : false,
17543     activated : false,
17544     sourceEditMode : false,
17545     onFocus : Roo.emptyFn,
17546     iframePad:3,
17547     hideMode:'offsets',
17548     
17549     clearUp: true,
17550     
17551     // blacklist + whitelisted elements..
17552     black: false,
17553     white: false,
17554      
17555     
17556
17557     /**
17558      * Protected method that will not generally be called directly. It
17559      * is called when the editor initializes the iframe with HTML contents. Override this method if you
17560      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
17561      */
17562     getDocMarkup : function(){
17563         // body styles..
17564         var st = '';
17565         
17566         // inherit styels from page...?? 
17567         if (this.stylesheets === false) {
17568             
17569             Roo.get(document.head).select('style').each(function(node) {
17570                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17571             });
17572             
17573             Roo.get(document.head).select('link').each(function(node) { 
17574                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17575             });
17576             
17577         } else if (!this.stylesheets.length) {
17578                 // simple..
17579                 st = '<style type="text/css">' +
17580                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17581                    '</style>';
17582         } else { 
17583             
17584         }
17585         
17586         st +=  '<style type="text/css">' +
17587             'IMG { cursor: pointer } ' +
17588         '</style>';
17589
17590         
17591         return '<html><head>' + st  +
17592             //<style type="text/css">' +
17593             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17594             //'</style>' +
17595             ' </head><body class="roo-htmleditor-body"></body></html>';
17596     },
17597
17598     // private
17599     onRender : function(ct, position)
17600     {
17601         var _t = this;
17602         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
17603         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
17604         
17605         
17606         this.el.dom.style.border = '0 none';
17607         this.el.dom.setAttribute('tabIndex', -1);
17608         this.el.addClass('x-hidden hide');
17609         
17610         
17611         
17612         if(Roo.isIE){ // fix IE 1px bogus margin
17613             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
17614         }
17615        
17616         
17617         this.frameId = Roo.id();
17618         
17619          
17620         
17621         var iframe = this.owner.wrap.createChild({
17622             tag: 'iframe',
17623             cls: 'form-control', // bootstrap..
17624             id: this.frameId,
17625             name: this.frameId,
17626             frameBorder : 'no',
17627             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
17628         }, this.el
17629         );
17630         
17631         
17632         this.iframe = iframe.dom;
17633
17634          this.assignDocWin();
17635         
17636         this.doc.designMode = 'on';
17637        
17638         this.doc.open();
17639         this.doc.write(this.getDocMarkup());
17640         this.doc.close();
17641
17642         
17643         var task = { // must defer to wait for browser to be ready
17644             run : function(){
17645                 //console.log("run task?" + this.doc.readyState);
17646                 this.assignDocWin();
17647                 if(this.doc.body || this.doc.readyState == 'complete'){
17648                     try {
17649                         this.doc.designMode="on";
17650                     } catch (e) {
17651                         return;
17652                     }
17653                     Roo.TaskMgr.stop(task);
17654                     this.initEditor.defer(10, this);
17655                 }
17656             },
17657             interval : 10,
17658             duration: 10000,
17659             scope: this
17660         };
17661         Roo.TaskMgr.start(task);
17662
17663     },
17664
17665     // private
17666     onResize : function(w, h)
17667     {
17668          Roo.log('resize: ' +w + ',' + h );
17669         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
17670         if(!this.iframe){
17671             return;
17672         }
17673         if(typeof w == 'number'){
17674             
17675             this.iframe.style.width = w + 'px';
17676         }
17677         if(typeof h == 'number'){
17678             
17679             this.iframe.style.height = h + 'px';
17680             if(this.doc){
17681                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
17682             }
17683         }
17684         
17685     },
17686
17687     /**
17688      * Toggles the editor between standard and source edit mode.
17689      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17690      */
17691     toggleSourceEdit : function(sourceEditMode){
17692         
17693         this.sourceEditMode = sourceEditMode === true;
17694         
17695         if(this.sourceEditMode){
17696  
17697             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
17698             
17699         }else{
17700             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
17701             //this.iframe.className = '';
17702             this.deferFocus();
17703         }
17704         //this.setSize(this.owner.wrap.getSize());
17705         //this.fireEvent('editmodechange', this, this.sourceEditMode);
17706     },
17707
17708     
17709   
17710
17711     /**
17712      * Protected method that will not generally be called directly. If you need/want
17713      * custom HTML cleanup, this is the method you should override.
17714      * @param {String} html The HTML to be cleaned
17715      * return {String} The cleaned HTML
17716      */
17717     cleanHtml : function(html){
17718         html = String(html);
17719         if(html.length > 5){
17720             if(Roo.isSafari){ // strip safari nonsense
17721                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
17722             }
17723         }
17724         if(html == '&nbsp;'){
17725             html = '';
17726         }
17727         return html;
17728     },
17729
17730     /**
17731      * HTML Editor -> Textarea
17732      * Protected method that will not generally be called directly. Syncs the contents
17733      * of the editor iframe with the textarea.
17734      */
17735     syncValue : function(){
17736         if(this.initialized){
17737             var bd = (this.doc.body || this.doc.documentElement);
17738             //this.cleanUpPaste(); -- this is done else where and causes havoc..
17739             var html = bd.innerHTML;
17740             if(Roo.isSafari){
17741                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
17742                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
17743                 if(m && m[1]){
17744                     html = '<div style="'+m[0]+'">' + html + '</div>';
17745                 }
17746             }
17747             html = this.cleanHtml(html);
17748             // fix up the special chars.. normaly like back quotes in word...
17749             // however we do not want to do this with chinese..
17750             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
17751                 var cc = b.charCodeAt();
17752                 if (
17753                     (cc >= 0x4E00 && cc < 0xA000 ) ||
17754                     (cc >= 0x3400 && cc < 0x4E00 ) ||
17755                     (cc >= 0xf900 && cc < 0xfb00 )
17756                 ) {
17757                         return b;
17758                 }
17759                 return "&#"+cc+";" 
17760             });
17761             if(this.owner.fireEvent('beforesync', this, html) !== false){
17762                 this.el.dom.value = html;
17763                 this.owner.fireEvent('sync', this, html);
17764             }
17765         }
17766     },
17767
17768     /**
17769      * Protected method that will not generally be called directly. Pushes the value of the textarea
17770      * into the iframe editor.
17771      */
17772     pushValue : function(){
17773         if(this.initialized){
17774             var v = this.el.dom.value.trim();
17775             
17776 //            if(v.length < 1){
17777 //                v = '&#160;';
17778 //            }
17779             
17780             if(this.owner.fireEvent('beforepush', this, v) !== false){
17781                 var d = (this.doc.body || this.doc.documentElement);
17782                 d.innerHTML = v;
17783                 this.cleanUpPaste();
17784                 this.el.dom.value = d.innerHTML;
17785                 this.owner.fireEvent('push', this, v);
17786             }
17787         }
17788     },
17789
17790     // private
17791     deferFocus : function(){
17792         this.focus.defer(10, this);
17793     },
17794
17795     // doc'ed in Field
17796     focus : function(){
17797         if(this.win && !this.sourceEditMode){
17798             this.win.focus();
17799         }else{
17800             this.el.focus();
17801         }
17802     },
17803     
17804     assignDocWin: function()
17805     {
17806         var iframe = this.iframe;
17807         
17808          if(Roo.isIE){
17809             this.doc = iframe.contentWindow.document;
17810             this.win = iframe.contentWindow;
17811         } else {
17812 //            if (!Roo.get(this.frameId)) {
17813 //                return;
17814 //            }
17815 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
17816 //            this.win = Roo.get(this.frameId).dom.contentWindow;
17817             
17818             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
17819                 return;
17820             }
17821             
17822             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
17823             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
17824         }
17825     },
17826     
17827     // private
17828     initEditor : function(){
17829         //console.log("INIT EDITOR");
17830         this.assignDocWin();
17831         
17832         
17833         
17834         this.doc.designMode="on";
17835         this.doc.open();
17836         this.doc.write(this.getDocMarkup());
17837         this.doc.close();
17838         
17839         var dbody = (this.doc.body || this.doc.documentElement);
17840         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
17841         // this copies styles from the containing element into thsi one..
17842         // not sure why we need all of this..
17843         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
17844         
17845         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
17846         //ss['background-attachment'] = 'fixed'; // w3c
17847         dbody.bgProperties = 'fixed'; // ie
17848         //Roo.DomHelper.applyStyles(dbody, ss);
17849         Roo.EventManager.on(this.doc, {
17850             //'mousedown': this.onEditorEvent,
17851             'mouseup': this.onEditorEvent,
17852             'dblclick': this.onEditorEvent,
17853             'click': this.onEditorEvent,
17854             'keyup': this.onEditorEvent,
17855             buffer:100,
17856             scope: this
17857         });
17858         if(Roo.isGecko){
17859             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
17860         }
17861         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
17862             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
17863         }
17864         this.initialized = true;
17865
17866         this.owner.fireEvent('initialize', this);
17867         this.pushValue();
17868     },
17869
17870     // private
17871     onDestroy : function(){
17872         
17873         
17874         
17875         if(this.rendered){
17876             
17877             //for (var i =0; i < this.toolbars.length;i++) {
17878             //    // fixme - ask toolbars for heights?
17879             //    this.toolbars[i].onDestroy();
17880            // }
17881             
17882             //this.wrap.dom.innerHTML = '';
17883             //this.wrap.remove();
17884         }
17885     },
17886
17887     // private
17888     onFirstFocus : function(){
17889         
17890         this.assignDocWin();
17891         
17892         
17893         this.activated = true;
17894          
17895     
17896         if(Roo.isGecko){ // prevent silly gecko errors
17897             this.win.focus();
17898             var s = this.win.getSelection();
17899             if(!s.focusNode || s.focusNode.nodeType != 3){
17900                 var r = s.getRangeAt(0);
17901                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
17902                 r.collapse(true);
17903                 this.deferFocus();
17904             }
17905             try{
17906                 this.execCmd('useCSS', true);
17907                 this.execCmd('styleWithCSS', false);
17908             }catch(e){}
17909         }
17910         this.owner.fireEvent('activate', this);
17911     },
17912
17913     // private
17914     adjustFont: function(btn){
17915         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
17916         //if(Roo.isSafari){ // safari
17917         //    adjust *= 2;
17918        // }
17919         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
17920         if(Roo.isSafari){ // safari
17921             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
17922             v =  (v < 10) ? 10 : v;
17923             v =  (v > 48) ? 48 : v;
17924             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
17925             
17926         }
17927         
17928         
17929         v = Math.max(1, v+adjust);
17930         
17931         this.execCmd('FontSize', v  );
17932     },
17933
17934     onEditorEvent : function(e){
17935         this.owner.fireEvent('editorevent', this, e);
17936       //  this.updateToolbar();
17937         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
17938     },
17939
17940     insertTag : function(tg)
17941     {
17942         // could be a bit smarter... -> wrap the current selected tRoo..
17943         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
17944             
17945             range = this.createRange(this.getSelection());
17946             var wrappingNode = this.doc.createElement(tg.toLowerCase());
17947             wrappingNode.appendChild(range.extractContents());
17948             range.insertNode(wrappingNode);
17949
17950             return;
17951             
17952             
17953             
17954         }
17955         this.execCmd("formatblock",   tg);
17956         
17957     },
17958     
17959     insertText : function(txt)
17960     {
17961         
17962         
17963         var range = this.createRange();
17964         range.deleteContents();
17965                //alert(Sender.getAttribute('label'));
17966                
17967         range.insertNode(this.doc.createTextNode(txt));
17968     } ,
17969     
17970      
17971
17972     /**
17973      * Executes a Midas editor command on the editor document and performs necessary focus and
17974      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
17975      * @param {String} cmd The Midas command
17976      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17977      */
17978     relayCmd : function(cmd, value){
17979         this.win.focus();
17980         this.execCmd(cmd, value);
17981         this.owner.fireEvent('editorevent', this);
17982         //this.updateToolbar();
17983         this.owner.deferFocus();
17984     },
17985
17986     /**
17987      * Executes a Midas editor command directly on the editor document.
17988      * For visual commands, you should use {@link #relayCmd} instead.
17989      * <b>This should only be called after the editor is initialized.</b>
17990      * @param {String} cmd The Midas command
17991      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17992      */
17993     execCmd : function(cmd, value){
17994         this.doc.execCommand(cmd, false, value === undefined ? null : value);
17995         this.syncValue();
17996     },
17997  
17998  
17999    
18000     /**
18001      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18002      * to insert tRoo.
18003      * @param {String} text | dom node.. 
18004      */
18005     insertAtCursor : function(text)
18006     {
18007         
18008         
18009         
18010         if(!this.activated){
18011             return;
18012         }
18013         /*
18014         if(Roo.isIE){
18015             this.win.focus();
18016             var r = this.doc.selection.createRange();
18017             if(r){
18018                 r.collapse(true);
18019                 r.pasteHTML(text);
18020                 this.syncValue();
18021                 this.deferFocus();
18022             
18023             }
18024             return;
18025         }
18026         */
18027         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18028             this.win.focus();
18029             
18030             
18031             // from jquery ui (MIT licenced)
18032             var range, node;
18033             var win = this.win;
18034             
18035             if (win.getSelection && win.getSelection().getRangeAt) {
18036                 range = win.getSelection().getRangeAt(0);
18037                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18038                 range.insertNode(node);
18039             } else if (win.document.selection && win.document.selection.createRange) {
18040                 // no firefox support
18041                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18042                 win.document.selection.createRange().pasteHTML(txt);
18043             } else {
18044                 // no firefox support
18045                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18046                 this.execCmd('InsertHTML', txt);
18047             } 
18048             
18049             this.syncValue();
18050             
18051             this.deferFocus();
18052         }
18053     },
18054  // private
18055     mozKeyPress : function(e){
18056         if(e.ctrlKey){
18057             var c = e.getCharCode(), cmd;
18058           
18059             if(c > 0){
18060                 c = String.fromCharCode(c).toLowerCase();
18061                 switch(c){
18062                     case 'b':
18063                         cmd = 'bold';
18064                         break;
18065                     case 'i':
18066                         cmd = 'italic';
18067                         break;
18068                     
18069                     case 'u':
18070                         cmd = 'underline';
18071                         break;
18072                     
18073                     case 'v':
18074                         this.cleanUpPaste.defer(100, this);
18075                         return;
18076                         
18077                 }
18078                 if(cmd){
18079                     this.win.focus();
18080                     this.execCmd(cmd);
18081                     this.deferFocus();
18082                     e.preventDefault();
18083                 }
18084                 
18085             }
18086         }
18087     },
18088
18089     // private
18090     fixKeys : function(){ // load time branching for fastest keydown performance
18091         if(Roo.isIE){
18092             return function(e){
18093                 var k = e.getKey(), r;
18094                 if(k == e.TAB){
18095                     e.stopEvent();
18096                     r = this.doc.selection.createRange();
18097                     if(r){
18098                         r.collapse(true);
18099                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18100                         this.deferFocus();
18101                     }
18102                     return;
18103                 }
18104                 
18105                 if(k == e.ENTER){
18106                     r = this.doc.selection.createRange();
18107                     if(r){
18108                         var target = r.parentElement();
18109                         if(!target || target.tagName.toLowerCase() != 'li'){
18110                             e.stopEvent();
18111                             r.pasteHTML('<br />');
18112                             r.collapse(false);
18113                             r.select();
18114                         }
18115                     }
18116                 }
18117                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18118                     this.cleanUpPaste.defer(100, this);
18119                     return;
18120                 }
18121                 
18122                 
18123             };
18124         }else if(Roo.isOpera){
18125             return function(e){
18126                 var k = e.getKey();
18127                 if(k == e.TAB){
18128                     e.stopEvent();
18129                     this.win.focus();
18130                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18131                     this.deferFocus();
18132                 }
18133                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18134                     this.cleanUpPaste.defer(100, this);
18135                     return;
18136                 }
18137                 
18138             };
18139         }else if(Roo.isSafari){
18140             return function(e){
18141                 var k = e.getKey();
18142                 
18143                 if(k == e.TAB){
18144                     e.stopEvent();
18145                     this.execCmd('InsertText','\t');
18146                     this.deferFocus();
18147                     return;
18148                 }
18149                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18150                     this.cleanUpPaste.defer(100, this);
18151                     return;
18152                 }
18153                 
18154              };
18155         }
18156     }(),
18157     
18158     getAllAncestors: function()
18159     {
18160         var p = this.getSelectedNode();
18161         var a = [];
18162         if (!p) {
18163             a.push(p); // push blank onto stack..
18164             p = this.getParentElement();
18165         }
18166         
18167         
18168         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18169             a.push(p);
18170             p = p.parentNode;
18171         }
18172         a.push(this.doc.body);
18173         return a;
18174     },
18175     lastSel : false,
18176     lastSelNode : false,
18177     
18178     
18179     getSelection : function() 
18180     {
18181         this.assignDocWin();
18182         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18183     },
18184     
18185     getSelectedNode: function() 
18186     {
18187         // this may only work on Gecko!!!
18188         
18189         // should we cache this!!!!
18190         
18191         
18192         
18193          
18194         var range = this.createRange(this.getSelection()).cloneRange();
18195         
18196         if (Roo.isIE) {
18197             var parent = range.parentElement();
18198             while (true) {
18199                 var testRange = range.duplicate();
18200                 testRange.moveToElementText(parent);
18201                 if (testRange.inRange(range)) {
18202                     break;
18203                 }
18204                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18205                     break;
18206                 }
18207                 parent = parent.parentElement;
18208             }
18209             return parent;
18210         }
18211         
18212         // is ancestor a text element.
18213         var ac =  range.commonAncestorContainer;
18214         if (ac.nodeType == 3) {
18215             ac = ac.parentNode;
18216         }
18217         
18218         var ar = ac.childNodes;
18219          
18220         var nodes = [];
18221         var other_nodes = [];
18222         var has_other_nodes = false;
18223         for (var i=0;i<ar.length;i++) {
18224             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18225                 continue;
18226             }
18227             // fullly contained node.
18228             
18229             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18230                 nodes.push(ar[i]);
18231                 continue;
18232             }
18233             
18234             // probably selected..
18235             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18236                 other_nodes.push(ar[i]);
18237                 continue;
18238             }
18239             // outer..
18240             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18241                 continue;
18242             }
18243             
18244             
18245             has_other_nodes = true;
18246         }
18247         if (!nodes.length && other_nodes.length) {
18248             nodes= other_nodes;
18249         }
18250         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18251             return false;
18252         }
18253         
18254         return nodes[0];
18255     },
18256     createRange: function(sel)
18257     {
18258         // this has strange effects when using with 
18259         // top toolbar - not sure if it's a great idea.
18260         //this.editor.contentWindow.focus();
18261         if (typeof sel != "undefined") {
18262             try {
18263                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18264             } catch(e) {
18265                 return this.doc.createRange();
18266             }
18267         } else {
18268             return this.doc.createRange();
18269         }
18270     },
18271     getParentElement: function()
18272     {
18273         
18274         this.assignDocWin();
18275         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18276         
18277         var range = this.createRange(sel);
18278          
18279         try {
18280             var p = range.commonAncestorContainer;
18281             while (p.nodeType == 3) { // text node
18282                 p = p.parentNode;
18283             }
18284             return p;
18285         } catch (e) {
18286             return null;
18287         }
18288     
18289     },
18290     /***
18291      *
18292      * Range intersection.. the hard stuff...
18293      *  '-1' = before
18294      *  '0' = hits..
18295      *  '1' = after.
18296      *         [ -- selected range --- ]
18297      *   [fail]                        [fail]
18298      *
18299      *    basically..
18300      *      if end is before start or  hits it. fail.
18301      *      if start is after end or hits it fail.
18302      *
18303      *   if either hits (but other is outside. - then it's not 
18304      *   
18305      *    
18306      **/
18307     
18308     
18309     // @see http://www.thismuchiknow.co.uk/?p=64.
18310     rangeIntersectsNode : function(range, node)
18311     {
18312         var nodeRange = node.ownerDocument.createRange();
18313         try {
18314             nodeRange.selectNode(node);
18315         } catch (e) {
18316             nodeRange.selectNodeContents(node);
18317         }
18318     
18319         var rangeStartRange = range.cloneRange();
18320         rangeStartRange.collapse(true);
18321     
18322         var rangeEndRange = range.cloneRange();
18323         rangeEndRange.collapse(false);
18324     
18325         var nodeStartRange = nodeRange.cloneRange();
18326         nodeStartRange.collapse(true);
18327     
18328         var nodeEndRange = nodeRange.cloneRange();
18329         nodeEndRange.collapse(false);
18330     
18331         return rangeStartRange.compareBoundaryPoints(
18332                  Range.START_TO_START, nodeEndRange) == -1 &&
18333                rangeEndRange.compareBoundaryPoints(
18334                  Range.START_TO_START, nodeStartRange) == 1;
18335         
18336          
18337     },
18338     rangeCompareNode : function(range, node)
18339     {
18340         var nodeRange = node.ownerDocument.createRange();
18341         try {
18342             nodeRange.selectNode(node);
18343         } catch (e) {
18344             nodeRange.selectNodeContents(node);
18345         }
18346         
18347         
18348         range.collapse(true);
18349     
18350         nodeRange.collapse(true);
18351      
18352         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18353         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18354          
18355         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18356         
18357         var nodeIsBefore   =  ss == 1;
18358         var nodeIsAfter    = ee == -1;
18359         
18360         if (nodeIsBefore && nodeIsAfter)
18361             return 0; // outer
18362         if (!nodeIsBefore && nodeIsAfter)
18363             return 1; //right trailed.
18364         
18365         if (nodeIsBefore && !nodeIsAfter)
18366             return 2;  // left trailed.
18367         // fully contined.
18368         return 3;
18369     },
18370
18371     // private? - in a new class?
18372     cleanUpPaste :  function()
18373     {
18374         // cleans up the whole document..
18375         Roo.log('cleanuppaste');
18376         
18377         this.cleanUpChildren(this.doc.body);
18378         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18379         if (clean != this.doc.body.innerHTML) {
18380             this.doc.body.innerHTML = clean;
18381         }
18382         
18383     },
18384     
18385     cleanWordChars : function(input) {// change the chars to hex code
18386         var he = Roo.HtmlEditorCore;
18387         
18388         var output = input;
18389         Roo.each(he.swapCodes, function(sw) { 
18390             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18391             
18392             output = output.replace(swapper, sw[1]);
18393         });
18394         
18395         return output;
18396     },
18397     
18398     
18399     cleanUpChildren : function (n)
18400     {
18401         if (!n.childNodes.length) {
18402             return;
18403         }
18404         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18405            this.cleanUpChild(n.childNodes[i]);
18406         }
18407     },
18408     
18409     
18410         
18411     
18412     cleanUpChild : function (node)
18413     {
18414         var ed = this;
18415         //console.log(node);
18416         if (node.nodeName == "#text") {
18417             // clean up silly Windows -- stuff?
18418             return; 
18419         }
18420         if (node.nodeName == "#comment") {
18421             node.parentNode.removeChild(node);
18422             // clean up silly Windows -- stuff?
18423             return; 
18424         }
18425         var lcname = node.tagName.toLowerCase();
18426         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18427         // whitelist of tags..
18428         
18429         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18430             // remove node.
18431             node.parentNode.removeChild(node);
18432             return;
18433             
18434         }
18435         
18436         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18437         
18438         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18439         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18440         
18441         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18442         //    remove_keep_children = true;
18443         //}
18444         
18445         if (remove_keep_children) {
18446             this.cleanUpChildren(node);
18447             // inserts everything just before this node...
18448             while (node.childNodes.length) {
18449                 var cn = node.childNodes[0];
18450                 node.removeChild(cn);
18451                 node.parentNode.insertBefore(cn, node);
18452             }
18453             node.parentNode.removeChild(node);
18454             return;
18455         }
18456         
18457         if (!node.attributes || !node.attributes.length) {
18458             this.cleanUpChildren(node);
18459             return;
18460         }
18461         
18462         function cleanAttr(n,v)
18463         {
18464             
18465             if (v.match(/^\./) || v.match(/^\//)) {
18466                 return;
18467             }
18468             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18469                 return;
18470             }
18471             if (v.match(/^#/)) {
18472                 return;
18473             }
18474 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18475             node.removeAttribute(n);
18476             
18477         }
18478         
18479         var cwhite = this.cwhite;
18480         var cblack = this.cblack;
18481             
18482         function cleanStyle(n,v)
18483         {
18484             if (v.match(/expression/)) { //XSS?? should we even bother..
18485                 node.removeAttribute(n);
18486                 return;
18487             }
18488             
18489             var parts = v.split(/;/);
18490             var clean = [];
18491             
18492             Roo.each(parts, function(p) {
18493                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
18494                 if (!p.length) {
18495                     return true;
18496                 }
18497                 var l = p.split(':').shift().replace(/\s+/g,'');
18498                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
18499                 
18500                 if ( cwhite.length && cblack.indexOf(l) > -1) {
18501 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18502                     //node.removeAttribute(n);
18503                     return true;
18504                 }
18505                 //Roo.log()
18506                 // only allow 'c whitelisted system attributes'
18507                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
18508 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18509                     //node.removeAttribute(n);
18510                     return true;
18511                 }
18512                 
18513                 
18514                  
18515                 
18516                 clean.push(p);
18517                 return true;
18518             });
18519             if (clean.length) { 
18520                 node.setAttribute(n, clean.join(';'));
18521             } else {
18522                 node.removeAttribute(n);
18523             }
18524             
18525         }
18526         
18527         
18528         for (var i = node.attributes.length-1; i > -1 ; i--) {
18529             var a = node.attributes[i];
18530             //console.log(a);
18531             
18532             if (a.name.toLowerCase().substr(0,2)=='on')  {
18533                 node.removeAttribute(a.name);
18534                 continue;
18535             }
18536             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
18537                 node.removeAttribute(a.name);
18538                 continue;
18539             }
18540             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
18541                 cleanAttr(a.name,a.value); // fixme..
18542                 continue;
18543             }
18544             if (a.name == 'style') {
18545                 cleanStyle(a.name,a.value);
18546                 continue;
18547             }
18548             /// clean up MS crap..
18549             // tecnically this should be a list of valid class'es..
18550             
18551             
18552             if (a.name == 'class') {
18553                 if (a.value.match(/^Mso/)) {
18554                     node.className = '';
18555                 }
18556                 
18557                 if (a.value.match(/body/)) {
18558                     node.className = '';
18559                 }
18560                 continue;
18561             }
18562             
18563             // style cleanup!?
18564             // class cleanup?
18565             
18566         }
18567         
18568         
18569         this.cleanUpChildren(node);
18570         
18571         
18572     },
18573     /**
18574      * Clean up MS wordisms...
18575      */
18576     cleanWord : function(node)
18577     {
18578         var _t = this;
18579         var cleanWordChildren = function()
18580         {
18581             if (!node.childNodes.length) {
18582                 return;
18583             }
18584             for (var i = node.childNodes.length-1; i > -1 ; i--) {
18585                _t.cleanWord(node.childNodes[i]);
18586             }
18587         }
18588         
18589         
18590         if (!node) {
18591             this.cleanWord(this.doc.body);
18592             return;
18593         }
18594         if (node.nodeName == "#text") {
18595             // clean up silly Windows -- stuff?
18596             return; 
18597         }
18598         if (node.nodeName == "#comment") {
18599             node.parentNode.removeChild(node);
18600             // clean up silly Windows -- stuff?
18601             return; 
18602         }
18603         
18604         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
18605             node.parentNode.removeChild(node);
18606             return;
18607         }
18608         
18609         // remove - but keep children..
18610         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
18611             while (node.childNodes.length) {
18612                 var cn = node.childNodes[0];
18613                 node.removeChild(cn);
18614                 node.parentNode.insertBefore(cn, node);
18615             }
18616             node.parentNode.removeChild(node);
18617             cleanWordChildren();
18618             return;
18619         }
18620         // clean styles
18621         if (node.className.length) {
18622             
18623             var cn = node.className.split(/\W+/);
18624             var cna = [];
18625             Roo.each(cn, function(cls) {
18626                 if (cls.match(/Mso[a-zA-Z]+/)) {
18627                     return;
18628                 }
18629                 cna.push(cls);
18630             });
18631             node.className = cna.length ? cna.join(' ') : '';
18632             if (!cna.length) {
18633                 node.removeAttribute("class");
18634             }
18635         }
18636         
18637         if (node.hasAttribute("lang")) {
18638             node.removeAttribute("lang");
18639         }
18640         
18641         if (node.hasAttribute("style")) {
18642             
18643             var styles = node.getAttribute("style").split(";");
18644             var nstyle = [];
18645             Roo.each(styles, function(s) {
18646                 if (!s.match(/:/)) {
18647                     return;
18648                 }
18649                 var kv = s.split(":");
18650                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
18651                     return;
18652                 }
18653                 // what ever is left... we allow.
18654                 nstyle.push(s);
18655             });
18656             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
18657             if (!nstyle.length) {
18658                 node.removeAttribute('style');
18659             }
18660         }
18661         
18662         cleanWordChildren();
18663         
18664         
18665     },
18666     domToHTML : function(currentElement, depth, nopadtext) {
18667         
18668         depth = depth || 0;
18669         nopadtext = nopadtext || false;
18670     
18671         if (!currentElement) {
18672             return this.domToHTML(this.doc.body);
18673         }
18674         
18675         //Roo.log(currentElement);
18676         var j;
18677         var allText = false;
18678         var nodeName = currentElement.nodeName;
18679         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
18680         
18681         if  (nodeName == '#text') {
18682             
18683             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
18684         }
18685         
18686         
18687         var ret = '';
18688         if (nodeName != 'BODY') {
18689              
18690             var i = 0;
18691             // Prints the node tagName, such as <A>, <IMG>, etc
18692             if (tagName) {
18693                 var attr = [];
18694                 for(i = 0; i < currentElement.attributes.length;i++) {
18695                     // quoting?
18696                     var aname = currentElement.attributes.item(i).name;
18697                     if (!currentElement.attributes.item(i).value.length) {
18698                         continue;
18699                     }
18700                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
18701                 }
18702                 
18703                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
18704             } 
18705             else {
18706                 
18707                 // eack
18708             }
18709         } else {
18710             tagName = false;
18711         }
18712         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
18713             return ret;
18714         }
18715         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
18716             nopadtext = true;
18717         }
18718         
18719         
18720         // Traverse the tree
18721         i = 0;
18722         var currentElementChild = currentElement.childNodes.item(i);
18723         var allText = true;
18724         var innerHTML  = '';
18725         lastnode = '';
18726         while (currentElementChild) {
18727             // Formatting code (indent the tree so it looks nice on the screen)
18728             var nopad = nopadtext;
18729             if (lastnode == 'SPAN') {
18730                 nopad  = true;
18731             }
18732             // text
18733             if  (currentElementChild.nodeName == '#text') {
18734                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
18735                 toadd = nopadtext ? toadd : toadd.trim();
18736                 if (!nopad && toadd.length > 80) {
18737                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
18738                 }
18739                 innerHTML  += toadd;
18740                 
18741                 i++;
18742                 currentElementChild = currentElement.childNodes.item(i);
18743                 lastNode = '';
18744                 continue;
18745             }
18746             allText = false;
18747             
18748             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
18749                 
18750             // Recursively traverse the tree structure of the child node
18751             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
18752             lastnode = currentElementChild.nodeName;
18753             i++;
18754             currentElementChild=currentElement.childNodes.item(i);
18755         }
18756         
18757         ret += innerHTML;
18758         
18759         if (!allText) {
18760                 // The remaining code is mostly for formatting the tree
18761             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
18762         }
18763         
18764         
18765         if (tagName) {
18766             ret+= "</"+tagName+">";
18767         }
18768         return ret;
18769         
18770     },
18771         
18772     applyBlacklists : function()
18773     {
18774         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
18775         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
18776         
18777         this.white = [];
18778         this.black = [];
18779         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
18780             if (b.indexOf(tag) > -1) {
18781                 return;
18782             }
18783             this.white.push(tag);
18784             
18785         }, this);
18786         
18787         Roo.each(w, function(tag) {
18788             if (b.indexOf(tag) > -1) {
18789                 return;
18790             }
18791             if (this.white.indexOf(tag) > -1) {
18792                 return;
18793             }
18794             this.white.push(tag);
18795             
18796         }, this);
18797         
18798         
18799         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
18800             if (w.indexOf(tag) > -1) {
18801                 return;
18802             }
18803             this.black.push(tag);
18804             
18805         }, this);
18806         
18807         Roo.each(b, function(tag) {
18808             if (w.indexOf(tag) > -1) {
18809                 return;
18810             }
18811             if (this.black.indexOf(tag) > -1) {
18812                 return;
18813             }
18814             this.black.push(tag);
18815             
18816         }, this);
18817         
18818         
18819         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
18820         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
18821         
18822         this.cwhite = [];
18823         this.cblack = [];
18824         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
18825             if (b.indexOf(tag) > -1) {
18826                 return;
18827             }
18828             this.cwhite.push(tag);
18829             
18830         }, this);
18831         
18832         Roo.each(w, function(tag) {
18833             if (b.indexOf(tag) > -1) {
18834                 return;
18835             }
18836             if (this.cwhite.indexOf(tag) > -1) {
18837                 return;
18838             }
18839             this.cwhite.push(tag);
18840             
18841         }, this);
18842         
18843         
18844         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
18845             if (w.indexOf(tag) > -1) {
18846                 return;
18847             }
18848             this.cblack.push(tag);
18849             
18850         }, this);
18851         
18852         Roo.each(b, function(tag) {
18853             if (w.indexOf(tag) > -1) {
18854                 return;
18855             }
18856             if (this.cblack.indexOf(tag) > -1) {
18857                 return;
18858             }
18859             this.cblack.push(tag);
18860             
18861         }, this);
18862     },
18863     
18864     setStylesheets : function(stylesheets)
18865     {
18866         if(typeof(stylesheets) == 'string'){
18867             Roo.get(this.iframe.contentDocument.head).createChild({
18868                 tag : 'link',
18869                 rel : 'stylesheet',
18870                 type : 'text/css',
18871                 href : stylesheets
18872             });
18873             
18874             return;
18875         }
18876         var _this = this;
18877      
18878         Roo.each(stylesheets, function(s) {
18879             if(!s.length){
18880                 return;
18881             }
18882             
18883             Roo.get(_this.iframe.contentDocument.head).createChild({
18884                 tag : 'link',
18885                 rel : 'stylesheet',
18886                 type : 'text/css',
18887                 href : s
18888             });
18889         });
18890
18891         
18892     },
18893     
18894     removeStylesheets : function()
18895     {
18896         var _this = this;
18897         
18898         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
18899             s.remove();
18900         });
18901     }
18902     
18903     // hide stuff that is not compatible
18904     /**
18905      * @event blur
18906      * @hide
18907      */
18908     /**
18909      * @event change
18910      * @hide
18911      */
18912     /**
18913      * @event focus
18914      * @hide
18915      */
18916     /**
18917      * @event specialkey
18918      * @hide
18919      */
18920     /**
18921      * @cfg {String} fieldClass @hide
18922      */
18923     /**
18924      * @cfg {String} focusClass @hide
18925      */
18926     /**
18927      * @cfg {String} autoCreate @hide
18928      */
18929     /**
18930      * @cfg {String} inputType @hide
18931      */
18932     /**
18933      * @cfg {String} invalidClass @hide
18934      */
18935     /**
18936      * @cfg {String} invalidText @hide
18937      */
18938     /**
18939      * @cfg {String} msgFx @hide
18940      */
18941     /**
18942      * @cfg {String} validateOnBlur @hide
18943      */
18944 });
18945
18946 Roo.HtmlEditorCore.white = [
18947         'area', 'br', 'img', 'input', 'hr', 'wbr',
18948         
18949        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
18950        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
18951        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
18952        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
18953        'table',   'ul',         'xmp', 
18954        
18955        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
18956       'thead',   'tr', 
18957      
18958       'dir', 'menu', 'ol', 'ul', 'dl',
18959        
18960       'embed',  'object'
18961 ];
18962
18963
18964 Roo.HtmlEditorCore.black = [
18965     //    'embed',  'object', // enable - backend responsiblity to clean thiese
18966         'applet', // 
18967         'base',   'basefont', 'bgsound', 'blink',  'body', 
18968         'frame',  'frameset', 'head',    'html',   'ilayer', 
18969         'iframe', 'layer',  'link',     'meta',    'object',   
18970         'script', 'style' ,'title',  'xml' // clean later..
18971 ];
18972 Roo.HtmlEditorCore.clean = [
18973     'script', 'style', 'title', 'xml'
18974 ];
18975 Roo.HtmlEditorCore.remove = [
18976     'font'
18977 ];
18978 // attributes..
18979
18980 Roo.HtmlEditorCore.ablack = [
18981     'on'
18982 ];
18983     
18984 Roo.HtmlEditorCore.aclean = [ 
18985     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
18986 ];
18987
18988 // protocols..
18989 Roo.HtmlEditorCore.pwhite= [
18990         'http',  'https',  'mailto'
18991 ];
18992
18993 // white listed style attributes.
18994 Roo.HtmlEditorCore.cwhite= [
18995       //  'text-align', /// default is to allow most things..
18996       
18997          
18998 //        'font-size'//??
18999 ];
19000
19001 // black listed style attributes.
19002 Roo.HtmlEditorCore.cblack= [
19003       //  'font-size' -- this can be set by the project 
19004 ];
19005
19006
19007 Roo.HtmlEditorCore.swapCodes   =[ 
19008     [    8211, "--" ], 
19009     [    8212, "--" ], 
19010     [    8216,  "'" ],  
19011     [    8217, "'" ],  
19012     [    8220, '"' ],  
19013     [    8221, '"' ],  
19014     [    8226, "*" ],  
19015     [    8230, "..." ]
19016 ]; 
19017
19018     /*
19019  * - LGPL
19020  *
19021  * HtmlEditor
19022  * 
19023  */
19024
19025 /**
19026  * @class Roo.bootstrap.HtmlEditor
19027  * @extends Roo.bootstrap.TextArea
19028  * Bootstrap HtmlEditor class
19029
19030  * @constructor
19031  * Create a new HtmlEditor
19032  * @param {Object} config The config object
19033  */
19034
19035 Roo.bootstrap.HtmlEditor = function(config){
19036     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19037     if (!this.toolbars) {
19038         this.toolbars = [];
19039     }
19040     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19041     this.addEvents({
19042             /**
19043              * @event initialize
19044              * Fires when the editor is fully initialized (including the iframe)
19045              * @param {HtmlEditor} this
19046              */
19047             initialize: true,
19048             /**
19049              * @event activate
19050              * Fires when the editor is first receives the focus. Any insertion must wait
19051              * until after this event.
19052              * @param {HtmlEditor} this
19053              */
19054             activate: true,
19055              /**
19056              * @event beforesync
19057              * Fires before the textarea is updated with content from the editor iframe. Return false
19058              * to cancel the sync.
19059              * @param {HtmlEditor} this
19060              * @param {String} html
19061              */
19062             beforesync: true,
19063              /**
19064              * @event beforepush
19065              * Fires before the iframe editor is updated with content from the textarea. Return false
19066              * to cancel the push.
19067              * @param {HtmlEditor} this
19068              * @param {String} html
19069              */
19070             beforepush: true,
19071              /**
19072              * @event sync
19073              * Fires when the textarea is updated with content from the editor iframe.
19074              * @param {HtmlEditor} this
19075              * @param {String} html
19076              */
19077             sync: true,
19078              /**
19079              * @event push
19080              * Fires when the iframe editor is updated with content from the textarea.
19081              * @param {HtmlEditor} this
19082              * @param {String} html
19083              */
19084             push: true,
19085              /**
19086              * @event editmodechange
19087              * Fires when the editor switches edit modes
19088              * @param {HtmlEditor} this
19089              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19090              */
19091             editmodechange: true,
19092             /**
19093              * @event editorevent
19094              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19095              * @param {HtmlEditor} this
19096              */
19097             editorevent: true,
19098             /**
19099              * @event firstfocus
19100              * Fires when on first focus - needed by toolbars..
19101              * @param {HtmlEditor} this
19102              */
19103             firstfocus: true,
19104             /**
19105              * @event autosave
19106              * Auto save the htmlEditor value as a file into Events
19107              * @param {HtmlEditor} this
19108              */
19109             autosave: true,
19110             /**
19111              * @event savedpreview
19112              * preview the saved version of htmlEditor
19113              * @param {HtmlEditor} this
19114              */
19115             savedpreview: true
19116         });
19117 };
19118
19119
19120 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19121     
19122     
19123       /**
19124      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19125      */
19126     toolbars : false,
19127    
19128      /**
19129      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19130      *                        Roo.resizable.
19131      */
19132     resizable : false,
19133      /**
19134      * @cfg {Number} height (in pixels)
19135      */   
19136     height: 300,
19137    /**
19138      * @cfg {Number} width (in pixels)
19139      */   
19140     width: false,
19141     
19142     /**
19143      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19144      * 
19145      */
19146     stylesheets: false,
19147     
19148     // id of frame..
19149     frameId: false,
19150     
19151     // private properties
19152     validationEvent : false,
19153     deferHeight: true,
19154     initialized : false,
19155     activated : false,
19156     
19157     onFocus : Roo.emptyFn,
19158     iframePad:3,
19159     hideMode:'offsets',
19160     
19161     
19162     tbContainer : false,
19163     
19164     toolbarContainer :function() {
19165         return this.wrap.select('.x-html-editor-tb',true).first();
19166     },
19167
19168     /**
19169      * Protected method that will not generally be called directly. It
19170      * is called when the editor creates its toolbar. Override this method if you need to
19171      * add custom toolbar buttons.
19172      * @param {HtmlEditor} editor
19173      */
19174     createToolbar : function(){
19175         
19176         Roo.log("create toolbars");
19177         
19178         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19179         this.toolbars[0].render(this.toolbarContainer());
19180         
19181         return;
19182         
19183 //        if (!editor.toolbars || !editor.toolbars.length) {
19184 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19185 //        }
19186 //        
19187 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19188 //            editor.toolbars[i] = Roo.factory(
19189 //                    typeof(editor.toolbars[i]) == 'string' ?
19190 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19191 //                Roo.bootstrap.HtmlEditor);
19192 //            editor.toolbars[i].init(editor);
19193 //        }
19194     },
19195
19196      
19197     // private
19198     onRender : function(ct, position)
19199     {
19200        // Roo.log("Call onRender: " + this.xtype);
19201         var _t = this;
19202         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19203       
19204         this.wrap = this.inputEl().wrap({
19205             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19206         });
19207         
19208         this.editorcore.onRender(ct, position);
19209          
19210         if (this.resizable) {
19211             this.resizeEl = new Roo.Resizable(this.wrap, {
19212                 pinned : true,
19213                 wrap: true,
19214                 dynamic : true,
19215                 minHeight : this.height,
19216                 height: this.height,
19217                 handles : this.resizable,
19218                 width: this.width,
19219                 listeners : {
19220                     resize : function(r, w, h) {
19221                         _t.onResize(w,h); // -something
19222                     }
19223                 }
19224             });
19225             
19226         }
19227         this.createToolbar(this);
19228        
19229         
19230         if(!this.width && this.resizable){
19231             this.setSize(this.wrap.getSize());
19232         }
19233         if (this.resizeEl) {
19234             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19235             // should trigger onReize..
19236         }
19237         
19238     },
19239
19240     // private
19241     onResize : function(w, h)
19242     {
19243         Roo.log('resize: ' +w + ',' + h );
19244         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19245         var ew = false;
19246         var eh = false;
19247         
19248         if(this.inputEl() ){
19249             if(typeof w == 'number'){
19250                 var aw = w - this.wrap.getFrameWidth('lr');
19251                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19252                 ew = aw;
19253             }
19254             if(typeof h == 'number'){
19255                  var tbh = -11;  // fixme it needs to tool bar size!
19256                 for (var i =0; i < this.toolbars.length;i++) {
19257                     // fixme - ask toolbars for heights?
19258                     tbh += this.toolbars[i].el.getHeight();
19259                     //if (this.toolbars[i].footer) {
19260                     //    tbh += this.toolbars[i].footer.el.getHeight();
19261                     //}
19262                 }
19263               
19264                 
19265                 
19266                 
19267                 
19268                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19269                 ah -= 5; // knock a few pixes off for look..
19270                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19271                 var eh = ah;
19272             }
19273         }
19274         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19275         this.editorcore.onResize(ew,eh);
19276         
19277     },
19278
19279     /**
19280      * Toggles the editor between standard and source edit mode.
19281      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19282      */
19283     toggleSourceEdit : function(sourceEditMode)
19284     {
19285         this.editorcore.toggleSourceEdit(sourceEditMode);
19286         
19287         if(this.editorcore.sourceEditMode){
19288             Roo.log('editor - showing textarea');
19289             
19290 //            Roo.log('in');
19291 //            Roo.log(this.syncValue());
19292             this.syncValue();
19293             this.inputEl().removeClass(['hide', 'x-hidden']);
19294             this.inputEl().dom.removeAttribute('tabIndex');
19295             this.inputEl().focus();
19296         }else{
19297             Roo.log('editor - hiding textarea');
19298 //            Roo.log('out')
19299 //            Roo.log(this.pushValue()); 
19300             this.pushValue();
19301             
19302             this.inputEl().addClass(['hide', 'x-hidden']);
19303             this.inputEl().dom.setAttribute('tabIndex', -1);
19304             //this.deferFocus();
19305         }
19306          
19307         if(this.resizable){
19308             this.setSize(this.wrap.getSize());
19309         }
19310         
19311         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19312     },
19313  
19314     // private (for BoxComponent)
19315     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19316
19317     // private (for BoxComponent)
19318     getResizeEl : function(){
19319         return this.wrap;
19320     },
19321
19322     // private (for BoxComponent)
19323     getPositionEl : function(){
19324         return this.wrap;
19325     },
19326
19327     // private
19328     initEvents : function(){
19329         this.originalValue = this.getValue();
19330     },
19331
19332 //    /**
19333 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19334 //     * @method
19335 //     */
19336 //    markInvalid : Roo.emptyFn,
19337 //    /**
19338 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19339 //     * @method
19340 //     */
19341 //    clearInvalid : Roo.emptyFn,
19342
19343     setValue : function(v){
19344         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19345         this.editorcore.pushValue();
19346     },
19347
19348      
19349     // private
19350     deferFocus : function(){
19351         this.focus.defer(10, this);
19352     },
19353
19354     // doc'ed in Field
19355     focus : function(){
19356         this.editorcore.focus();
19357         
19358     },
19359       
19360
19361     // private
19362     onDestroy : function(){
19363         
19364         
19365         
19366         if(this.rendered){
19367             
19368             for (var i =0; i < this.toolbars.length;i++) {
19369                 // fixme - ask toolbars for heights?
19370                 this.toolbars[i].onDestroy();
19371             }
19372             
19373             this.wrap.dom.innerHTML = '';
19374             this.wrap.remove();
19375         }
19376     },
19377
19378     // private
19379     onFirstFocus : function(){
19380         //Roo.log("onFirstFocus");
19381         this.editorcore.onFirstFocus();
19382          for (var i =0; i < this.toolbars.length;i++) {
19383             this.toolbars[i].onFirstFocus();
19384         }
19385         
19386     },
19387     
19388     // private
19389     syncValue : function()
19390     {   
19391         this.editorcore.syncValue();
19392     },
19393     
19394     pushValue : function()
19395     {   
19396         this.editorcore.pushValue();
19397     }
19398      
19399     
19400     // hide stuff that is not compatible
19401     /**
19402      * @event blur
19403      * @hide
19404      */
19405     /**
19406      * @event change
19407      * @hide
19408      */
19409     /**
19410      * @event focus
19411      * @hide
19412      */
19413     /**
19414      * @event specialkey
19415      * @hide
19416      */
19417     /**
19418      * @cfg {String} fieldClass @hide
19419      */
19420     /**
19421      * @cfg {String} focusClass @hide
19422      */
19423     /**
19424      * @cfg {String} autoCreate @hide
19425      */
19426     /**
19427      * @cfg {String} inputType @hide
19428      */
19429     /**
19430      * @cfg {String} invalidClass @hide
19431      */
19432     /**
19433      * @cfg {String} invalidText @hide
19434      */
19435     /**
19436      * @cfg {String} msgFx @hide
19437      */
19438     /**
19439      * @cfg {String} validateOnBlur @hide
19440      */
19441 });
19442  
19443     
19444    
19445    
19446    
19447       
19448 Roo.namespace('Roo.bootstrap.htmleditor');
19449 /**
19450  * @class Roo.bootstrap.HtmlEditorToolbar1
19451  * Basic Toolbar
19452  * 
19453  * Usage:
19454  *
19455  new Roo.bootstrap.HtmlEditor({
19456     ....
19457     toolbars : [
19458         new Roo.bootstrap.HtmlEditorToolbar1({
19459             disable : { fonts: 1 , format: 1, ..., ... , ...],
19460             btns : [ .... ]
19461         })
19462     }
19463      
19464  * 
19465  * @cfg {Object} disable List of elements to disable..
19466  * @cfg {Array} btns List of additional buttons.
19467  * 
19468  * 
19469  * NEEDS Extra CSS? 
19470  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19471  */
19472  
19473 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19474 {
19475     
19476     Roo.apply(this, config);
19477     
19478     // default disabled, based on 'good practice'..
19479     this.disable = this.disable || {};
19480     Roo.applyIf(this.disable, {
19481         fontSize : true,
19482         colors : true,
19483         specialElements : true
19484     });
19485     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
19486     
19487     this.editor = config.editor;
19488     this.editorcore = config.editor.editorcore;
19489     
19490     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
19491     
19492     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
19493     // dont call parent... till later.
19494 }
19495 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
19496      
19497     bar : true,
19498     
19499     editor : false,
19500     editorcore : false,
19501     
19502     
19503     formats : [
19504         "p" ,  
19505         "h1","h2","h3","h4","h5","h6", 
19506         "pre", "code", 
19507         "abbr", "acronym", "address", "cite", "samp", "var",
19508         'div','span'
19509     ],
19510     
19511     onRender : function(ct, position)
19512     {
19513        // Roo.log("Call onRender: " + this.xtype);
19514         
19515        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
19516        Roo.log(this.el);
19517        this.el.dom.style.marginBottom = '0';
19518        var _this = this;
19519        var editorcore = this.editorcore;
19520        var editor= this.editor;
19521        
19522        var children = [];
19523        var btn = function(id,cmd , toggle, handler){
19524        
19525             var  event = toggle ? 'toggle' : 'click';
19526        
19527             var a = {
19528                 size : 'sm',
19529                 xtype: 'Button',
19530                 xns: Roo.bootstrap,
19531                 glyphicon : id,
19532                 cmd : id || cmd,
19533                 enableToggle:toggle !== false,
19534                 //html : 'submit'
19535                 pressed : toggle ? false : null,
19536                 listeners : {}
19537             }
19538             a.listeners[toggle ? 'toggle' : 'click'] = function() {
19539                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
19540             }
19541             children.push(a);
19542             return a;
19543        }
19544         
19545         var style = {
19546                 xtype: 'Button',
19547                 size : 'sm',
19548                 xns: Roo.bootstrap,
19549                 glyphicon : 'font',
19550                 //html : 'submit'
19551                 menu : {
19552                     xtype: 'Menu',
19553                     xns: Roo.bootstrap,
19554                     items:  []
19555                 }
19556         };
19557         Roo.each(this.formats, function(f) {
19558             style.menu.items.push({
19559                 xtype :'MenuItem',
19560                 xns: Roo.bootstrap,
19561                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
19562                 tagname : f,
19563                 listeners : {
19564                     click : function()
19565                     {
19566                         editorcore.insertTag(this.tagname);
19567                         editor.focus();
19568                     }
19569                 }
19570                 
19571             });
19572         });
19573          children.push(style);   
19574             
19575             
19576         btn('bold',false,true);
19577         btn('italic',false,true);
19578         btn('align-left', 'justifyleft',true);
19579         btn('align-center', 'justifycenter',true);
19580         btn('align-right' , 'justifyright',true);
19581         btn('link', false, false, function(btn) {
19582             //Roo.log("create link?");
19583             var url = prompt(this.createLinkText, this.defaultLinkValue);
19584             if(url && url != 'http:/'+'/'){
19585                 this.editorcore.relayCmd('createlink', url);
19586             }
19587         }),
19588         btn('list','insertunorderedlist',true);
19589         btn('pencil', false,true, function(btn){
19590                 Roo.log(this);
19591                 
19592                 this.toggleSourceEdit(btn.pressed);
19593         });
19594         /*
19595         var cog = {
19596                 xtype: 'Button',
19597                 size : 'sm',
19598                 xns: Roo.bootstrap,
19599                 glyphicon : 'cog',
19600                 //html : 'submit'
19601                 menu : {
19602                     xtype: 'Menu',
19603                     xns: Roo.bootstrap,
19604                     items:  []
19605                 }
19606         };
19607         
19608         cog.menu.items.push({
19609             xtype :'MenuItem',
19610             xns: Roo.bootstrap,
19611             html : Clean styles,
19612             tagname : f,
19613             listeners : {
19614                 click : function()
19615                 {
19616                     editorcore.insertTag(this.tagname);
19617                     editor.focus();
19618                 }
19619             }
19620             
19621         });
19622        */
19623         
19624          
19625        this.xtype = 'NavSimplebar';
19626         
19627         for(var i=0;i< children.length;i++) {
19628             
19629             this.buttons.add(this.addxtypeChild(children[i]));
19630             
19631         }
19632         
19633         editor.on('editorevent', this.updateToolbar, this);
19634     },
19635     onBtnClick : function(id)
19636     {
19637        this.editorcore.relayCmd(id);
19638        this.editorcore.focus();
19639     },
19640     
19641     /**
19642      * Protected method that will not generally be called directly. It triggers
19643      * a toolbar update by reading the markup state of the current selection in the editor.
19644      */
19645     updateToolbar: function(){
19646
19647         if(!this.editorcore.activated){
19648             this.editor.onFirstFocus(); // is this neeed?
19649             return;
19650         }
19651
19652         var btns = this.buttons; 
19653         var doc = this.editorcore.doc;
19654         btns.get('bold').setActive(doc.queryCommandState('bold'));
19655         btns.get('italic').setActive(doc.queryCommandState('italic'));
19656         //btns.get('underline').setActive(doc.queryCommandState('underline'));
19657         
19658         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
19659         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
19660         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
19661         
19662         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
19663         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
19664          /*
19665         
19666         var ans = this.editorcore.getAllAncestors();
19667         if (this.formatCombo) {
19668             
19669             
19670             var store = this.formatCombo.store;
19671             this.formatCombo.setValue("");
19672             for (var i =0; i < ans.length;i++) {
19673                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
19674                     // select it..
19675                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
19676                     break;
19677                 }
19678             }
19679         }
19680         
19681         
19682         
19683         // hides menus... - so this cant be on a menu...
19684         Roo.bootstrap.MenuMgr.hideAll();
19685         */
19686         Roo.bootstrap.MenuMgr.hideAll();
19687         //this.editorsyncValue();
19688     },
19689     onFirstFocus: function() {
19690         this.buttons.each(function(item){
19691            item.enable();
19692         });
19693     },
19694     toggleSourceEdit : function(sourceEditMode){
19695         
19696           
19697         if(sourceEditMode){
19698             Roo.log("disabling buttons");
19699            this.buttons.each( function(item){
19700                 if(item.cmd != 'pencil'){
19701                     item.disable();
19702                 }
19703             });
19704           
19705         }else{
19706             Roo.log("enabling buttons");
19707             if(this.editorcore.initialized){
19708                 this.buttons.each( function(item){
19709                     item.enable();
19710                 });
19711             }
19712             
19713         }
19714         Roo.log("calling toggole on editor");
19715         // tell the editor that it's been pressed..
19716         this.editor.toggleSourceEdit(sourceEditMode);
19717        
19718     }
19719 });
19720
19721
19722
19723
19724
19725 /**
19726  * @class Roo.bootstrap.Table.AbstractSelectionModel
19727  * @extends Roo.util.Observable
19728  * Abstract base class for grid SelectionModels.  It provides the interface that should be
19729  * implemented by descendant classes.  This class should not be directly instantiated.
19730  * @constructor
19731  */
19732 Roo.bootstrap.Table.AbstractSelectionModel = function(){
19733     this.locked = false;
19734     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
19735 };
19736
19737
19738 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
19739     /** @ignore Called by the grid automatically. Do not call directly. */
19740     init : function(grid){
19741         this.grid = grid;
19742         this.initEvents();
19743     },
19744
19745     /**
19746      * Locks the selections.
19747      */
19748     lock : function(){
19749         this.locked = true;
19750     },
19751
19752     /**
19753      * Unlocks the selections.
19754      */
19755     unlock : function(){
19756         this.locked = false;
19757     },
19758
19759     /**
19760      * Returns true if the selections are locked.
19761      * @return {Boolean}
19762      */
19763     isLocked : function(){
19764         return this.locked;
19765     }
19766 });
19767 /**
19768  * @extends Roo.bootstrap.Table.AbstractSelectionModel
19769  * @class Roo.bootstrap.Table.RowSelectionModel
19770  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
19771  * It supports multiple selections and keyboard selection/navigation. 
19772  * @constructor
19773  * @param {Object} config
19774  */
19775
19776 Roo.bootstrap.Table.RowSelectionModel = function(config){
19777     Roo.apply(this, config);
19778     this.selections = new Roo.util.MixedCollection(false, function(o){
19779         return o.id;
19780     });
19781
19782     this.last = false;
19783     this.lastActive = false;
19784
19785     this.addEvents({
19786         /**
19787              * @event selectionchange
19788              * Fires when the selection changes
19789              * @param {SelectionModel} this
19790              */
19791             "selectionchange" : true,
19792         /**
19793              * @event afterselectionchange
19794              * Fires after the selection changes (eg. by key press or clicking)
19795              * @param {SelectionModel} this
19796              */
19797             "afterselectionchange" : true,
19798         /**
19799              * @event beforerowselect
19800              * Fires when a row is selected being selected, return false to cancel.
19801              * @param {SelectionModel} this
19802              * @param {Number} rowIndex The selected index
19803              * @param {Boolean} keepExisting False if other selections will be cleared
19804              */
19805             "beforerowselect" : true,
19806         /**
19807              * @event rowselect
19808              * Fires when a row is selected.
19809              * @param {SelectionModel} this
19810              * @param {Number} rowIndex The selected index
19811              * @param {Roo.data.Record} r The record
19812              */
19813             "rowselect" : true,
19814         /**
19815              * @event rowdeselect
19816              * Fires when a row is deselected.
19817              * @param {SelectionModel} this
19818              * @param {Number} rowIndex The selected index
19819              */
19820         "rowdeselect" : true
19821     });
19822     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
19823     this.locked = false;
19824 };
19825
19826 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
19827     /**
19828      * @cfg {Boolean} singleSelect
19829      * True to allow selection of only one row at a time (defaults to false)
19830      */
19831     singleSelect : false,
19832
19833     // private
19834     initEvents : function(){
19835
19836         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
19837             this.grid.on("mousedown", this.handleMouseDown, this);
19838         }else{ // allow click to work like normal
19839             this.grid.on("rowclick", this.handleDragableRowClick, this);
19840         }
19841
19842         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
19843             "up" : function(e){
19844                 if(!e.shiftKey){
19845                     this.selectPrevious(e.shiftKey);
19846                 }else if(this.last !== false && this.lastActive !== false){
19847                     var last = this.last;
19848                     this.selectRange(this.last,  this.lastActive-1);
19849                     this.grid.getView().focusRow(this.lastActive);
19850                     if(last !== false){
19851                         this.last = last;
19852                     }
19853                 }else{
19854                     this.selectFirstRow();
19855                 }
19856                 this.fireEvent("afterselectionchange", this);
19857             },
19858             "down" : function(e){
19859                 if(!e.shiftKey){
19860                     this.selectNext(e.shiftKey);
19861                 }else if(this.last !== false && this.lastActive !== false){
19862                     var last = this.last;
19863                     this.selectRange(this.last,  this.lastActive+1);
19864                     this.grid.getView().focusRow(this.lastActive);
19865                     if(last !== false){
19866                         this.last = last;
19867                     }
19868                 }else{
19869                     this.selectFirstRow();
19870                 }
19871                 this.fireEvent("afterselectionchange", this);
19872             },
19873             scope: this
19874         });
19875
19876         var view = this.grid.view;
19877         view.on("refresh", this.onRefresh, this);
19878         view.on("rowupdated", this.onRowUpdated, this);
19879         view.on("rowremoved", this.onRemove, this);
19880     },
19881
19882     // private
19883     onRefresh : function(){
19884         var ds = this.grid.dataSource, i, v = this.grid.view;
19885         var s = this.selections;
19886         s.each(function(r){
19887             if((i = ds.indexOfId(r.id)) != -1){
19888                 v.onRowSelect(i);
19889             }else{
19890                 s.remove(r);
19891             }
19892         });
19893     },
19894
19895     // private
19896     onRemove : function(v, index, r){
19897         this.selections.remove(r);
19898     },
19899
19900     // private
19901     onRowUpdated : function(v, index, r){
19902         if(this.isSelected(r)){
19903             v.onRowSelect(index);
19904         }
19905     },
19906
19907     /**
19908      * Select records.
19909      * @param {Array} records The records to select
19910      * @param {Boolean} keepExisting (optional) True to keep existing selections
19911      */
19912     selectRecords : function(records, keepExisting){
19913         if(!keepExisting){
19914             this.clearSelections();
19915         }
19916         var ds = this.grid.dataSource;
19917         for(var i = 0, len = records.length; i < len; i++){
19918             this.selectRow(ds.indexOf(records[i]), true);
19919         }
19920     },
19921
19922     /**
19923      * Gets the number of selected rows.
19924      * @return {Number}
19925      */
19926     getCount : function(){
19927         return this.selections.length;
19928     },
19929
19930     /**
19931      * Selects the first row in the grid.
19932      */
19933     selectFirstRow : function(){
19934         this.selectRow(0);
19935     },
19936
19937     /**
19938      * Select the last row.
19939      * @param {Boolean} keepExisting (optional) True to keep existing selections
19940      */
19941     selectLastRow : function(keepExisting){
19942         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
19943     },
19944
19945     /**
19946      * Selects the row immediately following the last selected row.
19947      * @param {Boolean} keepExisting (optional) True to keep existing selections
19948      */
19949     selectNext : function(keepExisting){
19950         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
19951             this.selectRow(this.last+1, keepExisting);
19952             this.grid.getView().focusRow(this.last);
19953         }
19954     },
19955
19956     /**
19957      * Selects the row that precedes the last selected row.
19958      * @param {Boolean} keepExisting (optional) True to keep existing selections
19959      */
19960     selectPrevious : function(keepExisting){
19961         if(this.last){
19962             this.selectRow(this.last-1, keepExisting);
19963             this.grid.getView().focusRow(this.last);
19964         }
19965     },
19966
19967     /**
19968      * Returns the selected records
19969      * @return {Array} Array of selected records
19970      */
19971     getSelections : function(){
19972         return [].concat(this.selections.items);
19973     },
19974
19975     /**
19976      * Returns the first selected record.
19977      * @return {Record}
19978      */
19979     getSelected : function(){
19980         return this.selections.itemAt(0);
19981     },
19982
19983
19984     /**
19985      * Clears all selections.
19986      */
19987     clearSelections : function(fast){
19988         if(this.locked) return;
19989         if(fast !== true){
19990             var ds = this.grid.dataSource;
19991             var s = this.selections;
19992             s.each(function(r){
19993                 this.deselectRow(ds.indexOfId(r.id));
19994             }, this);
19995             s.clear();
19996         }else{
19997             this.selections.clear();
19998         }
19999         this.last = false;
20000     },
20001
20002
20003     /**
20004      * Selects all rows.
20005      */
20006     selectAll : function(){
20007         if(this.locked) return;
20008         this.selections.clear();
20009         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20010             this.selectRow(i, true);
20011         }
20012     },
20013
20014     /**
20015      * Returns True if there is a selection.
20016      * @return {Boolean}
20017      */
20018     hasSelection : function(){
20019         return this.selections.length > 0;
20020     },
20021
20022     /**
20023      * Returns True if the specified row is selected.
20024      * @param {Number/Record} record The record or index of the record to check
20025      * @return {Boolean}
20026      */
20027     isSelected : function(index){
20028         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20029         return (r && this.selections.key(r.id) ? true : false);
20030     },
20031
20032     /**
20033      * Returns True if the specified record id is selected.
20034      * @param {String} id The id of record to check
20035      * @return {Boolean}
20036      */
20037     isIdSelected : function(id){
20038         return (this.selections.key(id) ? true : false);
20039     },
20040
20041     // private
20042     handleMouseDown : function(e, t){
20043         var view = this.grid.getView(), rowIndex;
20044         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20045             return;
20046         };
20047         if(e.shiftKey && this.last !== false){
20048             var last = this.last;
20049             this.selectRange(last, rowIndex, e.ctrlKey);
20050             this.last = last; // reset the last
20051             view.focusRow(rowIndex);
20052         }else{
20053             var isSelected = this.isSelected(rowIndex);
20054             if(e.button !== 0 && isSelected){
20055                 view.focusRow(rowIndex);
20056             }else if(e.ctrlKey && isSelected){
20057                 this.deselectRow(rowIndex);
20058             }else if(!isSelected){
20059                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20060                 view.focusRow(rowIndex);
20061             }
20062         }
20063         this.fireEvent("afterselectionchange", this);
20064     },
20065     // private
20066     handleDragableRowClick :  function(grid, rowIndex, e) 
20067     {
20068         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20069             this.selectRow(rowIndex, false);
20070             grid.view.focusRow(rowIndex);
20071              this.fireEvent("afterselectionchange", this);
20072         }
20073     },
20074     
20075     /**
20076      * Selects multiple rows.
20077      * @param {Array} rows Array of the indexes of the row to select
20078      * @param {Boolean} keepExisting (optional) True to keep existing selections
20079      */
20080     selectRows : function(rows, keepExisting){
20081         if(!keepExisting){
20082             this.clearSelections();
20083         }
20084         for(var i = 0, len = rows.length; i < len; i++){
20085             this.selectRow(rows[i], true);
20086         }
20087     },
20088
20089     /**
20090      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20091      * @param {Number} startRow The index of the first row in the range
20092      * @param {Number} endRow The index of the last row in the range
20093      * @param {Boolean} keepExisting (optional) True to retain existing selections
20094      */
20095     selectRange : function(startRow, endRow, keepExisting){
20096         if(this.locked) return;
20097         if(!keepExisting){
20098             this.clearSelections();
20099         }
20100         if(startRow <= endRow){
20101             for(var i = startRow; i <= endRow; i++){
20102                 this.selectRow(i, true);
20103             }
20104         }else{
20105             for(var i = startRow; i >= endRow; i--){
20106                 this.selectRow(i, true);
20107             }
20108         }
20109     },
20110
20111     /**
20112      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20113      * @param {Number} startRow The index of the first row in the range
20114      * @param {Number} endRow The index of the last row in the range
20115      */
20116     deselectRange : function(startRow, endRow, preventViewNotify){
20117         if(this.locked) return;
20118         for(var i = startRow; i <= endRow; i++){
20119             this.deselectRow(i, preventViewNotify);
20120         }
20121     },
20122
20123     /**
20124      * Selects a row.
20125      * @param {Number} row The index of the row to select
20126      * @param {Boolean} keepExisting (optional) True to keep existing selections
20127      */
20128     selectRow : function(index, keepExisting, preventViewNotify){
20129         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20130         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20131             if(!keepExisting || this.singleSelect){
20132                 this.clearSelections();
20133             }
20134             var r = this.grid.dataSource.getAt(index);
20135             this.selections.add(r);
20136             this.last = this.lastActive = index;
20137             if(!preventViewNotify){
20138                 this.grid.getView().onRowSelect(index);
20139             }
20140             this.fireEvent("rowselect", this, index, r);
20141             this.fireEvent("selectionchange", this);
20142         }
20143     },
20144
20145     /**
20146      * Deselects a row.
20147      * @param {Number} row The index of the row to deselect
20148      */
20149     deselectRow : function(index, preventViewNotify){
20150         if(this.locked) return;
20151         if(this.last == index){
20152             this.last = false;
20153         }
20154         if(this.lastActive == index){
20155             this.lastActive = false;
20156         }
20157         var r = this.grid.dataSource.getAt(index);
20158         this.selections.remove(r);
20159         if(!preventViewNotify){
20160             this.grid.getView().onRowDeselect(index);
20161         }
20162         this.fireEvent("rowdeselect", this, index);
20163         this.fireEvent("selectionchange", this);
20164     },
20165
20166     // private
20167     restoreLast : function(){
20168         if(this._last){
20169             this.last = this._last;
20170         }
20171     },
20172
20173     // private
20174     acceptsNav : function(row, col, cm){
20175         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20176     },
20177
20178     // private
20179     onEditorKey : function(field, e){
20180         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20181         if(k == e.TAB){
20182             e.stopEvent();
20183             ed.completeEdit();
20184             if(e.shiftKey){
20185                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20186             }else{
20187                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20188             }
20189         }else if(k == e.ENTER && !e.ctrlKey){
20190             e.stopEvent();
20191             ed.completeEdit();
20192             if(e.shiftKey){
20193                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20194             }else{
20195                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20196             }
20197         }else if(k == e.ESC){
20198             ed.cancelEdit();
20199         }
20200         if(newCell){
20201             g.startEditing(newCell[0], newCell[1]);
20202         }
20203     }
20204 });/*
20205  * Based on:
20206  * Ext JS Library 1.1.1
20207  * Copyright(c) 2006-2007, Ext JS, LLC.
20208  *
20209  * Originally Released Under LGPL - original licence link has changed is not relivant.
20210  *
20211  * Fork - LGPL
20212  * <script type="text/javascript">
20213  */
20214  
20215 /**
20216  * @class Roo.bootstrap.PagingToolbar
20217  * @extends Roo.Row
20218  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20219  * @constructor
20220  * Create a new PagingToolbar
20221  * @param {Object} config The config object
20222  */
20223 Roo.bootstrap.PagingToolbar = function(config)
20224 {
20225     // old args format still supported... - xtype is prefered..
20226         // created from xtype...
20227     var ds = config.dataSource;
20228     this.toolbarItems = [];
20229     if (config.items) {
20230         this.toolbarItems = config.items;
20231 //        config.items = [];
20232     }
20233     
20234     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20235     this.ds = ds;
20236     this.cursor = 0;
20237     if (ds) { 
20238         this.bind(ds);
20239     }
20240     
20241     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20242     
20243 };
20244
20245 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20246     /**
20247      * @cfg {Roo.data.Store} dataSource
20248      * The underlying data store providing the paged data
20249      */
20250     /**
20251      * @cfg {String/HTMLElement/Element} container
20252      * container The id or element that will contain the toolbar
20253      */
20254     /**
20255      * @cfg {Boolean} displayInfo
20256      * True to display the displayMsg (defaults to false)
20257      */
20258     /**
20259      * @cfg {Number} pageSize
20260      * The number of records to display per page (defaults to 20)
20261      */
20262     pageSize: 20,
20263     /**
20264      * @cfg {String} displayMsg
20265      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20266      */
20267     displayMsg : 'Displaying {0} - {1} of {2}',
20268     /**
20269      * @cfg {String} emptyMsg
20270      * The message to display when no records are found (defaults to "No data to display")
20271      */
20272     emptyMsg : 'No data to display',
20273     /**
20274      * Customizable piece of the default paging text (defaults to "Page")
20275      * @type String
20276      */
20277     beforePageText : "Page",
20278     /**
20279      * Customizable piece of the default paging text (defaults to "of %0")
20280      * @type String
20281      */
20282     afterPageText : "of {0}",
20283     /**
20284      * Customizable piece of the default paging text (defaults to "First Page")
20285      * @type String
20286      */
20287     firstText : "First Page",
20288     /**
20289      * Customizable piece of the default paging text (defaults to "Previous Page")
20290      * @type String
20291      */
20292     prevText : "Previous Page",
20293     /**
20294      * Customizable piece of the default paging text (defaults to "Next Page")
20295      * @type String
20296      */
20297     nextText : "Next Page",
20298     /**
20299      * Customizable piece of the default paging text (defaults to "Last Page")
20300      * @type String
20301      */
20302     lastText : "Last Page",
20303     /**
20304      * Customizable piece of the default paging text (defaults to "Refresh")
20305      * @type String
20306      */
20307     refreshText : "Refresh",
20308
20309     buttons : false,
20310     // private
20311     onRender : function(ct, position) 
20312     {
20313         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20314         this.navgroup.parentId = this.id;
20315         this.navgroup.onRender(this.el, null);
20316         // add the buttons to the navgroup
20317         
20318         if(this.displayInfo){
20319             Roo.log(this.el.select('ul.navbar-nav',true).first());
20320             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20321             this.displayEl = this.el.select('.x-paging-info', true).first();
20322 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20323 //            this.displayEl = navel.el.select('span',true).first();
20324         }
20325         
20326         var _this = this;
20327         
20328         if(this.buttons){
20329             Roo.each(_this.buttons, function(e){
20330                Roo.factory(e).onRender(_this.el, null);
20331             });
20332         }
20333             
20334         Roo.each(_this.toolbarItems, function(e) {
20335             _this.navgroup.addItem(e);
20336         });
20337         
20338         
20339         this.first = this.navgroup.addItem({
20340             tooltip: this.firstText,
20341             cls: "prev",
20342             icon : 'fa fa-backward',
20343             disabled: true,
20344             preventDefault: true,
20345             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20346         });
20347         
20348         this.prev =  this.navgroup.addItem({
20349             tooltip: this.prevText,
20350             cls: "prev",
20351             icon : 'fa fa-step-backward',
20352             disabled: true,
20353             preventDefault: true,
20354             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20355         });
20356     //this.addSeparator();
20357         
20358         
20359         var field = this.navgroup.addItem( {
20360             tagtype : 'span',
20361             cls : 'x-paging-position',
20362             
20363             html : this.beforePageText  +
20364                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20365                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20366          } ); //?? escaped?
20367         
20368         this.field = field.el.select('input', true).first();
20369         this.field.on("keydown", this.onPagingKeydown, this);
20370         this.field.on("focus", function(){this.dom.select();});
20371     
20372     
20373         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20374         //this.field.setHeight(18);
20375         //this.addSeparator();
20376         this.next = this.navgroup.addItem({
20377             tooltip: this.nextText,
20378             cls: "next",
20379             html : ' <i class="fa fa-step-forward">',
20380             disabled: true,
20381             preventDefault: true,
20382             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20383         });
20384         this.last = this.navgroup.addItem({
20385             tooltip: this.lastText,
20386             icon : 'fa fa-forward',
20387             cls: "next",
20388             disabled: true,
20389             preventDefault: true,
20390             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20391         });
20392     //this.addSeparator();
20393         this.loading = this.navgroup.addItem({
20394             tooltip: this.refreshText,
20395             icon: 'fa fa-refresh',
20396             preventDefault: true,
20397             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20398         });
20399
20400     },
20401
20402     // private
20403     updateInfo : function(){
20404         if(this.displayEl){
20405             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20406             var msg = count == 0 ?
20407                 this.emptyMsg :
20408                 String.format(
20409                     this.displayMsg,
20410                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20411                 );
20412             this.displayEl.update(msg);
20413         }
20414     },
20415
20416     // private
20417     onLoad : function(ds, r, o){
20418        this.cursor = o.params ? o.params.start : 0;
20419        var d = this.getPageData(),
20420             ap = d.activePage,
20421             ps = d.pages;
20422         
20423        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20424        this.field.dom.value = ap;
20425        this.first.setDisabled(ap == 1);
20426        this.prev.setDisabled(ap == 1);
20427        this.next.setDisabled(ap == ps);
20428        this.last.setDisabled(ap == ps);
20429        this.loading.enable();
20430        this.updateInfo();
20431     },
20432
20433     // private
20434     getPageData : function(){
20435         var total = this.ds.getTotalCount();
20436         return {
20437             total : total,
20438             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20439             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20440         };
20441     },
20442
20443     // private
20444     onLoadError : function(){
20445         this.loading.enable();
20446     },
20447
20448     // private
20449     onPagingKeydown : function(e){
20450         var k = e.getKey();
20451         var d = this.getPageData();
20452         if(k == e.RETURN){
20453             var v = this.field.dom.value, pageNum;
20454             if(!v || isNaN(pageNum = parseInt(v, 10))){
20455                 this.field.dom.value = d.activePage;
20456                 return;
20457             }
20458             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20459             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20460             e.stopEvent();
20461         }
20462         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))
20463         {
20464           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20465           this.field.dom.value = pageNum;
20466           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20467           e.stopEvent();
20468         }
20469         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20470         {
20471           var v = this.field.dom.value, pageNum; 
20472           var increment = (e.shiftKey) ? 10 : 1;
20473           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20474             increment *= -1;
20475           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20476             this.field.dom.value = d.activePage;
20477             return;
20478           }
20479           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
20480           {
20481             this.field.dom.value = parseInt(v, 10) + increment;
20482             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
20483             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20484           }
20485           e.stopEvent();
20486         }
20487     },
20488
20489     // private
20490     beforeLoad : function(){
20491         if(this.loading){
20492             this.loading.disable();
20493         }
20494     },
20495
20496     // private
20497     onClick : function(which){
20498         
20499         var ds = this.ds;
20500         if (!ds) {
20501             return;
20502         }
20503         
20504         switch(which){
20505             case "first":
20506                 ds.load({params:{start: 0, limit: this.pageSize}});
20507             break;
20508             case "prev":
20509                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
20510             break;
20511             case "next":
20512                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
20513             break;
20514             case "last":
20515                 var total = ds.getTotalCount();
20516                 var extra = total % this.pageSize;
20517                 var lastStart = extra ? (total - extra) : total-this.pageSize;
20518                 ds.load({params:{start: lastStart, limit: this.pageSize}});
20519             break;
20520             case "refresh":
20521                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
20522             break;
20523         }
20524     },
20525
20526     /**
20527      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
20528      * @param {Roo.data.Store} store The data store to unbind
20529      */
20530     unbind : function(ds){
20531         ds.un("beforeload", this.beforeLoad, this);
20532         ds.un("load", this.onLoad, this);
20533         ds.un("loadexception", this.onLoadError, this);
20534         ds.un("remove", this.updateInfo, this);
20535         ds.un("add", this.updateInfo, this);
20536         this.ds = undefined;
20537     },
20538
20539     /**
20540      * Binds the paging toolbar to the specified {@link Roo.data.Store}
20541      * @param {Roo.data.Store} store The data store to bind
20542      */
20543     bind : function(ds){
20544         ds.on("beforeload", this.beforeLoad, this);
20545         ds.on("load", this.onLoad, this);
20546         ds.on("loadexception", this.onLoadError, this);
20547         ds.on("remove", this.updateInfo, this);
20548         ds.on("add", this.updateInfo, this);
20549         this.ds = ds;
20550     }
20551 });/*
20552  * - LGPL
20553  *
20554  * element
20555  * 
20556  */
20557
20558 /**
20559  * @class Roo.bootstrap.MessageBar
20560  * @extends Roo.bootstrap.Component
20561  * Bootstrap MessageBar class
20562  * @cfg {String} html contents of the MessageBar
20563  * @cfg {String} weight (info | success | warning | danger) default info
20564  * @cfg {String} beforeClass insert the bar before the given class
20565  * @cfg {Boolean} closable (true | false) default false
20566  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
20567  * 
20568  * @constructor
20569  * Create a new Element
20570  * @param {Object} config The config object
20571  */
20572
20573 Roo.bootstrap.MessageBar = function(config){
20574     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
20575 };
20576
20577 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
20578     
20579     html: '',
20580     weight: 'info',
20581     closable: false,
20582     fixed: false,
20583     beforeClass: 'bootstrap-sticky-wrap',
20584     
20585     getAutoCreate : function(){
20586         
20587         var cfg = {
20588             tag: 'div',
20589             cls: 'alert alert-dismissable alert-' + this.weight,
20590             cn: [
20591                 {
20592                     tag: 'span',
20593                     cls: 'message',
20594                     html: this.html || ''
20595                 }
20596             ]
20597         }
20598         
20599         if(this.fixed){
20600             cfg.cls += ' alert-messages-fixed';
20601         }
20602         
20603         if(this.closable){
20604             cfg.cn.push({
20605                 tag: 'button',
20606                 cls: 'close',
20607                 html: 'x'
20608             });
20609         }
20610         
20611         return cfg;
20612     },
20613     
20614     onRender : function(ct, position)
20615     {
20616         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20617         
20618         if(!this.el){
20619             var cfg = Roo.apply({},  this.getAutoCreate());
20620             cfg.id = Roo.id();
20621             
20622             if (this.cls) {
20623                 cfg.cls += ' ' + this.cls;
20624             }
20625             if (this.style) {
20626                 cfg.style = this.style;
20627             }
20628             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
20629             
20630             this.el.setVisibilityMode(Roo.Element.DISPLAY);
20631         }
20632         
20633         this.el.select('>button.close').on('click', this.hide, this);
20634         
20635     },
20636     
20637     show : function()
20638     {
20639         if (!this.rendered) {
20640             this.render();
20641         }
20642         
20643         this.el.show();
20644         
20645         this.fireEvent('show', this);
20646         
20647     },
20648     
20649     hide : function()
20650     {
20651         if (!this.rendered) {
20652             this.render();
20653         }
20654         
20655         this.el.hide();
20656         
20657         this.fireEvent('hide', this);
20658     },
20659     
20660     update : function()
20661     {
20662 //        var e = this.el.dom.firstChild;
20663 //        
20664 //        if(this.closable){
20665 //            e = e.nextSibling;
20666 //        }
20667 //        
20668 //        e.data = this.html || '';
20669
20670         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
20671     }
20672    
20673 });
20674
20675  
20676
20677      /*
20678  * - LGPL
20679  *
20680  * Graph
20681  * 
20682  */
20683
20684
20685 /**
20686  * @class Roo.bootstrap.Graph
20687  * @extends Roo.bootstrap.Component
20688  * Bootstrap Graph class
20689 > Prameters
20690  -sm {number} sm 4
20691  -md {number} md 5
20692  @cfg {String} graphtype  bar | vbar | pie
20693  @cfg {number} g_x coodinator | centre x (pie)
20694  @cfg {number} g_y coodinator | centre y (pie)
20695  @cfg {number} g_r radius (pie)
20696  @cfg {number} g_height height of the chart (respected by all elements in the set)
20697  @cfg {number} g_width width of the chart (respected by all elements in the set)
20698  @cfg {Object} title The title of the chart
20699     
20700  -{Array}  values
20701  -opts (object) options for the chart 
20702      o {
20703      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
20704      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
20705      o vgutter (number)
20706      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.
20707      o stacked (boolean) whether or not to tread values as in a stacked bar chart
20708      o to
20709      o stretch (boolean)
20710      o }
20711  -opts (object) options for the pie
20712      o{
20713      o cut
20714      o startAngle (number)
20715      o endAngle (number)
20716      } 
20717  *
20718  * @constructor
20719  * Create a new Input
20720  * @param {Object} config The config object
20721  */
20722
20723 Roo.bootstrap.Graph = function(config){
20724     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
20725     
20726     this.addEvents({
20727         // img events
20728         /**
20729          * @event click
20730          * The img click event for the img.
20731          * @param {Roo.EventObject} e
20732          */
20733         "click" : true
20734     });
20735 };
20736
20737 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
20738     
20739     sm: 4,
20740     md: 5,
20741     graphtype: 'bar',
20742     g_height: 250,
20743     g_width: 400,
20744     g_x: 50,
20745     g_y: 50,
20746     g_r: 30,
20747     opts:{
20748         //g_colors: this.colors,
20749         g_type: 'soft',
20750         g_gutter: '20%'
20751
20752     },
20753     title : false,
20754
20755     getAutoCreate : function(){
20756         
20757         var cfg = {
20758             tag: 'div',
20759             html : null
20760         }
20761         
20762         
20763         return  cfg;
20764     },
20765
20766     onRender : function(ct,position){
20767         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
20768         this.raphael = Raphael(this.el.dom);
20769         
20770                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
20771                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
20772                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
20773                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
20774                 /*
20775                 r.text(160, 10, "Single Series Chart").attr(txtattr);
20776                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
20777                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
20778                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
20779                 
20780                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
20781                 r.barchart(330, 10, 300, 220, data1);
20782                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
20783                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
20784                 */
20785                 
20786                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
20787                 // r.barchart(30, 30, 560, 250,  xdata, {
20788                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
20789                 //     axis : "0 0 1 1",
20790                 //     axisxlabels :  xdata
20791                 //     //yvalues : cols,
20792                    
20793                 // });
20794 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
20795 //        
20796 //        this.load(null,xdata,{
20797 //                axis : "0 0 1 1",
20798 //                axisxlabels :  xdata
20799 //                });
20800
20801     },
20802
20803     load : function(graphtype,xdata,opts){
20804         this.raphael.clear();
20805         if(!graphtype) {
20806             graphtype = this.graphtype;
20807         }
20808         if(!opts){
20809             opts = this.opts;
20810         }
20811         var r = this.raphael,
20812             fin = function () {
20813                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
20814             },
20815             fout = function () {
20816                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
20817             },
20818             pfin = function() {
20819                 this.sector.stop();
20820                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
20821
20822                 if (this.label) {
20823                     this.label[0].stop();
20824                     this.label[0].attr({ r: 7.5 });
20825                     this.label[1].attr({ "font-weight": 800 });
20826                 }
20827             },
20828             pfout = function() {
20829                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
20830
20831                 if (this.label) {
20832                     this.label[0].animate({ r: 5 }, 500, "bounce");
20833                     this.label[1].attr({ "font-weight": 400 });
20834                 }
20835             };
20836
20837         switch(graphtype){
20838             case 'bar':
20839                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
20840                 break;
20841             case 'hbar':
20842                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
20843                 break;
20844             case 'pie':
20845 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
20846 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
20847 //            
20848                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
20849                 
20850                 break;
20851
20852         }
20853         
20854         if(this.title){
20855             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
20856         }
20857         
20858     },
20859     
20860     setTitle: function(o)
20861     {
20862         this.title = o;
20863     },
20864     
20865     initEvents: function() {
20866         
20867         if(!this.href){
20868             this.el.on('click', this.onClick, this);
20869         }
20870     },
20871     
20872     onClick : function(e)
20873     {
20874         Roo.log('img onclick');
20875         this.fireEvent('click', this, e);
20876     }
20877    
20878 });
20879
20880  
20881 /*
20882  * - LGPL
20883  *
20884  * numberBox
20885  * 
20886  */
20887 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20888
20889 /**
20890  * @class Roo.bootstrap.dash.NumberBox
20891  * @extends Roo.bootstrap.Component
20892  * Bootstrap NumberBox class
20893  * @cfg {String} headline Box headline
20894  * @cfg {String} content Box content
20895  * @cfg {String} icon Box icon
20896  * @cfg {String} footer Footer text
20897  * @cfg {String} fhref Footer href
20898  * 
20899  * @constructor
20900  * Create a new NumberBox
20901  * @param {Object} config The config object
20902  */
20903
20904
20905 Roo.bootstrap.dash.NumberBox = function(config){
20906     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
20907     
20908 };
20909
20910 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
20911     
20912     headline : '',
20913     content : '',
20914     icon : '',
20915     footer : '',
20916     fhref : '',
20917     ficon : '',
20918     
20919     getAutoCreate : function(){
20920         
20921         var cfg = {
20922             tag : 'div',
20923             cls : 'small-box ',
20924             cn : [
20925                 {
20926                     tag : 'div',
20927                     cls : 'inner',
20928                     cn :[
20929                         {
20930                             tag : 'h3',
20931                             cls : 'roo-headline',
20932                             html : this.headline
20933                         },
20934                         {
20935                             tag : 'p',
20936                             cls : 'roo-content',
20937                             html : this.content
20938                         }
20939                     ]
20940                 }
20941             ]
20942         }
20943         
20944         if(this.icon){
20945             cfg.cn.push({
20946                 tag : 'div',
20947                 cls : 'icon',
20948                 cn :[
20949                     {
20950                         tag : 'i',
20951                         cls : 'ion ' + this.icon
20952                     }
20953                 ]
20954             });
20955         }
20956         
20957         if(this.footer){
20958             var footer = {
20959                 tag : 'a',
20960                 cls : 'small-box-footer',
20961                 href : this.fhref || '#',
20962                 html : this.footer
20963             };
20964             
20965             cfg.cn.push(footer);
20966             
20967         }
20968         
20969         return  cfg;
20970     },
20971
20972     onRender : function(ct,position){
20973         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
20974
20975
20976        
20977                 
20978     },
20979
20980     setHeadline: function (value)
20981     {
20982         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
20983     },
20984     
20985     setFooter: function (value, href)
20986     {
20987         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
20988         
20989         if(href){
20990             this.el.select('a.small-box-footer',true).first().attr('href', href);
20991         }
20992         
20993     },
20994
20995     setContent: function (value)
20996     {
20997         this.el.select('.roo-content',true).first().dom.innerHTML = value;
20998     },
20999
21000     initEvents: function() 
21001     {   
21002         
21003     }
21004     
21005 });
21006
21007  
21008 /*
21009  * - LGPL
21010  *
21011  * TabBox
21012  * 
21013  */
21014 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21015
21016 /**
21017  * @class Roo.bootstrap.dash.TabBox
21018  * @extends Roo.bootstrap.Component
21019  * Bootstrap TabBox class
21020  * @cfg {String} title Title of the TabBox
21021  * @cfg {String} icon Icon of the TabBox
21022  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21023  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21024  * 
21025  * @constructor
21026  * Create a new TabBox
21027  * @param {Object} config The config object
21028  */
21029
21030
21031 Roo.bootstrap.dash.TabBox = function(config){
21032     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21033     this.addEvents({
21034         // raw events
21035         /**
21036          * @event addpane
21037          * When a pane is added
21038          * @param {Roo.bootstrap.dash.TabPane} pane
21039          */
21040         "addpane" : true,
21041         /**
21042          * @event activatepane
21043          * When a pane is activated
21044          * @param {Roo.bootstrap.dash.TabPane} pane
21045          */
21046         "activatepane" : true
21047         
21048          
21049     });
21050     
21051     this.panes = [];
21052 };
21053
21054 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21055
21056     title : '',
21057     icon : false,
21058     showtabs : true,
21059     tabScrollable : false,
21060     
21061     getChildContainer : function()
21062     {
21063         return this.el.select('.tab-content', true).first();
21064     },
21065     
21066     getAutoCreate : function(){
21067         
21068         var header = {
21069             tag: 'li',
21070             cls: 'pull-left header',
21071             html: this.title,
21072             cn : []
21073         };
21074         
21075         if(this.icon){
21076             header.cn.push({
21077                 tag: 'i',
21078                 cls: 'fa ' + this.icon
21079             });
21080         }
21081         
21082         var h = {
21083             tag: 'ul',
21084             cls: 'nav nav-tabs pull-right',
21085             cn: [
21086                 header
21087             ]
21088         };
21089         
21090         if(this.tabScrollable){
21091             h = {
21092                 tag: 'div',
21093                 cls: 'tab-header',
21094                 cn: [
21095                     {
21096                         tag: 'ul',
21097                         cls: 'nav nav-tabs pull-right',
21098                         cn: [
21099                             header
21100                         ]
21101                     }
21102                 ]
21103             }
21104         }
21105         
21106         var cfg = {
21107             tag: 'div',
21108             cls: 'nav-tabs-custom',
21109             cn: [
21110                 h,
21111                 {
21112                     tag: 'div',
21113                     cls: 'tab-content no-padding',
21114                     cn: []
21115                 }
21116             ]
21117         }
21118
21119         return  cfg;
21120     },
21121     initEvents : function()
21122     {
21123         //Roo.log('add add pane handler');
21124         this.on('addpane', this.onAddPane, this);
21125     },
21126      /**
21127      * Updates the box title
21128      * @param {String} html to set the title to.
21129      */
21130     setTitle : function(value)
21131     {
21132         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21133     },
21134     onAddPane : function(pane)
21135     {
21136         this.panes.push(pane);
21137         //Roo.log('addpane');
21138         //Roo.log(pane);
21139         // tabs are rendere left to right..
21140         if(!this.showtabs){
21141             return;
21142         }
21143         
21144         var ctr = this.el.select('.nav-tabs', true).first();
21145          
21146          
21147         var existing = ctr.select('.nav-tab',true);
21148         var qty = existing.getCount();;
21149         
21150         
21151         var tab = ctr.createChild({
21152             tag : 'li',
21153             cls : 'nav-tab' + (qty ? '' : ' active'),
21154             cn : [
21155                 {
21156                     tag : 'a',
21157                     href:'#',
21158                     html : pane.title
21159                 }
21160             ]
21161         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21162         pane.tab = tab;
21163         
21164         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21165         if (!qty) {
21166             pane.el.addClass('active');
21167         }
21168         
21169                 
21170     },
21171     onTabClick : function(ev,un,ob,pane)
21172     {
21173         //Roo.log('tab - prev default');
21174         ev.preventDefault();
21175         
21176         
21177         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21178         pane.tab.addClass('active');
21179         //Roo.log(pane.title);
21180         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21181         // technically we should have a deactivate event.. but maybe add later.
21182         // and it should not de-activate the selected tab...
21183         this.fireEvent('activatepane', pane);
21184         pane.el.addClass('active');
21185         pane.fireEvent('activate');
21186         
21187         
21188     },
21189     
21190     getActivePane : function()
21191     {
21192         var r = false;
21193         Roo.each(this.panes, function(p) {
21194             if(p.el.hasClass('active')){
21195                 r = p;
21196                 return false;
21197             }
21198             
21199             return;
21200         });
21201         
21202         return r;
21203     }
21204     
21205     
21206 });
21207
21208  
21209 /*
21210  * - LGPL
21211  *
21212  * Tab pane
21213  * 
21214  */
21215 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21216 /**
21217  * @class Roo.bootstrap.TabPane
21218  * @extends Roo.bootstrap.Component
21219  * Bootstrap TabPane class
21220  * @cfg {Boolean} active (false | true) Default false
21221  * @cfg {String} title title of panel
21222
21223  * 
21224  * @constructor
21225  * Create a new TabPane
21226  * @param {Object} config The config object
21227  */
21228
21229 Roo.bootstrap.dash.TabPane = function(config){
21230     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21231     
21232     this.addEvents({
21233         // raw events
21234         /**
21235          * @event activate
21236          * When a pane is activated
21237          * @param {Roo.bootstrap.dash.TabPane} pane
21238          */
21239         "activate" : true
21240          
21241     });
21242 };
21243
21244 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21245     
21246     active : false,
21247     title : '',
21248     
21249     // the tabBox that this is attached to.
21250     tab : false,
21251      
21252     getAutoCreate : function() 
21253     {
21254         var cfg = {
21255             tag: 'div',
21256             cls: 'tab-pane'
21257         }
21258         
21259         if(this.active){
21260             cfg.cls += ' active';
21261         }
21262         
21263         return cfg;
21264     },
21265     initEvents  : function()
21266     {
21267         //Roo.log('trigger add pane handler');
21268         this.parent().fireEvent('addpane', this)
21269     },
21270     
21271      /**
21272      * Updates the tab title 
21273      * @param {String} html to set the title to.
21274      */
21275     setTitle: function(str)
21276     {
21277         if (!this.tab) {
21278             return;
21279         }
21280         this.title = str;
21281         this.tab.select('a', true).first().dom.innerHTML = str;
21282         
21283     }
21284     
21285     
21286     
21287 });
21288
21289  
21290
21291
21292  /*
21293  * - LGPL
21294  *
21295  * menu
21296  * 
21297  */
21298 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21299
21300 /**
21301  * @class Roo.bootstrap.menu.Menu
21302  * @extends Roo.bootstrap.Component
21303  * Bootstrap Menu class - container for Menu
21304  * @cfg {String} html Text of the menu
21305  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21306  * @cfg {String} icon Font awesome icon
21307  * @cfg {String} pos Menu align to (top | bottom) default bottom
21308  * 
21309  * 
21310  * @constructor
21311  * Create a new Menu
21312  * @param {Object} config The config object
21313  */
21314
21315
21316 Roo.bootstrap.menu.Menu = function(config){
21317     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21318     
21319     this.addEvents({
21320         /**
21321          * @event beforeshow
21322          * Fires before this menu is displayed
21323          * @param {Roo.bootstrap.menu.Menu} this
21324          */
21325         beforeshow : true,
21326         /**
21327          * @event beforehide
21328          * Fires before this menu is hidden
21329          * @param {Roo.bootstrap.menu.Menu} this
21330          */
21331         beforehide : true,
21332         /**
21333          * @event show
21334          * Fires after this menu is displayed
21335          * @param {Roo.bootstrap.menu.Menu} this
21336          */
21337         show : true,
21338         /**
21339          * @event hide
21340          * Fires after this menu is hidden
21341          * @param {Roo.bootstrap.menu.Menu} this
21342          */
21343         hide : true,
21344         /**
21345          * @event click
21346          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21347          * @param {Roo.bootstrap.menu.Menu} this
21348          * @param {Roo.EventObject} e
21349          */
21350         click : true
21351     });
21352     
21353 };
21354
21355 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21356     
21357     submenu : false,
21358     html : '',
21359     weight : 'default',
21360     icon : false,
21361     pos : 'bottom',
21362     
21363     
21364     getChildContainer : function() {
21365         if(this.isSubMenu){
21366             return this.el;
21367         }
21368         
21369         return this.el.select('ul.dropdown-menu', true).first();  
21370     },
21371     
21372     getAutoCreate : function()
21373     {
21374         var text = [
21375             {
21376                 tag : 'span',
21377                 cls : 'roo-menu-text',
21378                 html : this.html
21379             }
21380         ];
21381         
21382         if(this.icon){
21383             text.unshift({
21384                 tag : 'i',
21385                 cls : 'fa ' + this.icon
21386             })
21387         }
21388         
21389         
21390         var cfg = {
21391             tag : 'div',
21392             cls : 'btn-group',
21393             cn : [
21394                 {
21395                     tag : 'button',
21396                     cls : 'dropdown-button btn btn-' + this.weight,
21397                     cn : text
21398                 },
21399                 {
21400                     tag : 'button',
21401                     cls : 'dropdown-toggle btn btn-' + this.weight,
21402                     cn : [
21403                         {
21404                             tag : 'span',
21405                             cls : 'caret'
21406                         }
21407                     ]
21408                 },
21409                 {
21410                     tag : 'ul',
21411                     cls : 'dropdown-menu'
21412                 }
21413             ]
21414             
21415         };
21416         
21417         if(this.pos == 'top'){
21418             cfg.cls += ' dropup';
21419         }
21420         
21421         if(this.isSubMenu){
21422             cfg = {
21423                 tag : 'ul',
21424                 cls : 'dropdown-menu'
21425             }
21426         }
21427         
21428         return cfg;
21429     },
21430     
21431     onRender : function(ct, position)
21432     {
21433         this.isSubMenu = ct.hasClass('dropdown-submenu');
21434         
21435         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21436     },
21437     
21438     initEvents : function() 
21439     {
21440         if(this.isSubMenu){
21441             return;
21442         }
21443         
21444         this.hidden = true;
21445         
21446         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21447         this.triggerEl.on('click', this.onTriggerPress, this);
21448         
21449         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21450         this.buttonEl.on('click', this.onClick, this);
21451         
21452     },
21453     
21454     list : function()
21455     {
21456         if(this.isSubMenu){
21457             return this.el;
21458         }
21459         
21460         return this.el.select('ul.dropdown-menu', true).first();
21461     },
21462     
21463     onClick : function(e)
21464     {
21465         this.fireEvent("click", this, e);
21466     },
21467     
21468     onTriggerPress  : function(e)
21469     {   
21470         if (this.isVisible()) {
21471             this.hide();
21472         } else {
21473             this.show();
21474         }
21475     },
21476     
21477     isVisible : function(){
21478         return !this.hidden;
21479     },
21480     
21481     show : function()
21482     {
21483         this.fireEvent("beforeshow", this);
21484         
21485         this.hidden = false;
21486         this.el.addClass('open');
21487         
21488         Roo.get(document).on("mouseup", this.onMouseUp, this);
21489         
21490         this.fireEvent("show", this);
21491         
21492         
21493     },
21494     
21495     hide : function()
21496     {
21497         this.fireEvent("beforehide", this);
21498         
21499         this.hidden = true;
21500         this.el.removeClass('open');
21501         
21502         Roo.get(document).un("mouseup", this.onMouseUp);
21503         
21504         this.fireEvent("hide", this);
21505     },
21506     
21507     onMouseUp : function()
21508     {
21509         this.hide();
21510     }
21511     
21512 });
21513
21514  
21515  /*
21516  * - LGPL
21517  *
21518  * menu item
21519  * 
21520  */
21521 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21522
21523 /**
21524  * @class Roo.bootstrap.menu.Item
21525  * @extends Roo.bootstrap.Component
21526  * Bootstrap MenuItem class
21527  * @cfg {Boolean} submenu (true | false) default false
21528  * @cfg {String} html text of the item
21529  * @cfg {String} href the link
21530  * @cfg {Boolean} disable (true | false) default false
21531  * @cfg {Boolean} preventDefault (true | false) default true
21532  * @cfg {String} icon Font awesome icon
21533  * @cfg {String} pos Submenu align to (left | right) default right 
21534  * 
21535  * 
21536  * @constructor
21537  * Create a new Item
21538  * @param {Object} config The config object
21539  */
21540
21541
21542 Roo.bootstrap.menu.Item = function(config){
21543     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
21544     this.addEvents({
21545         /**
21546          * @event mouseover
21547          * Fires when the mouse is hovering over this menu
21548          * @param {Roo.bootstrap.menu.Item} this
21549          * @param {Roo.EventObject} e
21550          */
21551         mouseover : true,
21552         /**
21553          * @event mouseout
21554          * Fires when the mouse exits this menu
21555          * @param {Roo.bootstrap.menu.Item} this
21556          * @param {Roo.EventObject} e
21557          */
21558         mouseout : true,
21559         // raw events
21560         /**
21561          * @event click
21562          * The raw click event for the entire grid.
21563          * @param {Roo.EventObject} e
21564          */
21565         click : true
21566     });
21567 };
21568
21569 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
21570     
21571     submenu : false,
21572     href : '',
21573     html : '',
21574     preventDefault: true,
21575     disable : false,
21576     icon : false,
21577     pos : 'right',
21578     
21579     getAutoCreate : function()
21580     {
21581         var text = [
21582             {
21583                 tag : 'span',
21584                 cls : 'roo-menu-item-text',
21585                 html : this.html
21586             }
21587         ];
21588         
21589         if(this.icon){
21590             text.unshift({
21591                 tag : 'i',
21592                 cls : 'fa ' + this.icon
21593             })
21594         }
21595         
21596         var cfg = {
21597             tag : 'li',
21598             cn : [
21599                 {
21600                     tag : 'a',
21601                     href : this.href || '#',
21602                     cn : text
21603                 }
21604             ]
21605         };
21606         
21607         if(this.disable){
21608             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
21609         }
21610         
21611         if(this.submenu){
21612             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
21613             
21614             if(this.pos == 'left'){
21615                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
21616             }
21617         }
21618         
21619         return cfg;
21620     },
21621     
21622     initEvents : function() 
21623     {
21624         this.el.on('mouseover', this.onMouseOver, this);
21625         this.el.on('mouseout', this.onMouseOut, this);
21626         
21627         this.el.select('a', true).first().on('click', this.onClick, this);
21628         
21629     },
21630     
21631     onClick : function(e)
21632     {
21633         if(this.preventDefault){
21634             e.preventDefault();
21635         }
21636         
21637         this.fireEvent("click", this, e);
21638     },
21639     
21640     onMouseOver : function(e)
21641     {
21642         if(this.submenu && this.pos == 'left'){
21643             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
21644         }
21645         
21646         this.fireEvent("mouseover", this, e);
21647     },
21648     
21649     onMouseOut : function(e)
21650     {
21651         this.fireEvent("mouseout", this, e);
21652     }
21653 });
21654
21655  
21656
21657  /*
21658  * - LGPL
21659  *
21660  * menu separator
21661  * 
21662  */
21663 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21664
21665 /**
21666  * @class Roo.bootstrap.menu.Separator
21667  * @extends Roo.bootstrap.Component
21668  * Bootstrap Separator class
21669  * 
21670  * @constructor
21671  * Create a new Separator
21672  * @param {Object} config The config object
21673  */
21674
21675
21676 Roo.bootstrap.menu.Separator = function(config){
21677     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
21678 };
21679
21680 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
21681     
21682     getAutoCreate : function(){
21683         var cfg = {
21684             tag : 'li',
21685             cls: 'divider'
21686         };
21687         
21688         return cfg;
21689     }
21690    
21691 });
21692
21693  
21694
21695  /*
21696  * - LGPL
21697  *
21698  * Tooltip
21699  * 
21700  */
21701
21702 /**
21703  * @class Roo.bootstrap.Tooltip
21704  * Bootstrap Tooltip class
21705  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
21706  * to determine which dom element triggers the tooltip.
21707  * 
21708  * It needs to add support for additional attributes like tooltip-position
21709  * 
21710  * @constructor
21711  * Create a new Toolti
21712  * @param {Object} config The config object
21713  */
21714
21715 Roo.bootstrap.Tooltip = function(config){
21716     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
21717 };
21718
21719 Roo.apply(Roo.bootstrap.Tooltip, {
21720     /**
21721      * @function init initialize tooltip monitoring.
21722      * @static
21723      */
21724     currentEl : false,
21725     currentTip : false,
21726     currentRegion : false,
21727     
21728     //  init : delay?
21729     
21730     init : function()
21731     {
21732         Roo.get(document).on('mouseover', this.enter ,this);
21733         Roo.get(document).on('mouseout', this.leave, this);
21734          
21735         
21736         this.currentTip = new Roo.bootstrap.Tooltip();
21737     },
21738     
21739     enter : function(ev)
21740     {
21741         var dom = ev.getTarget();
21742         //Roo.log(['enter',dom]);
21743         var el = Roo.fly(dom);
21744         if (this.currentEl) {
21745             //Roo.log(dom);
21746             //Roo.log(this.currentEl);
21747             //Roo.log(this.currentEl.contains(dom));
21748             if (this.currentEl == el) {
21749                 return;
21750             }
21751             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
21752                 return;
21753             }
21754
21755         }
21756         
21757         
21758         
21759         if (this.currentTip.el) {
21760             this.currentTip.el.hide(); // force hiding...
21761         }    
21762         //Roo.log(el);
21763         if (!el.attr('tooltip')) { // parents who have tip?
21764             return;
21765         }
21766         this.currentEl = el;
21767         this.currentTip.bind(el);
21768         this.currentRegion = Roo.lib.Region.getRegion(dom);
21769         this.currentTip.enter();
21770         
21771     },
21772     leave : function(ev)
21773     {
21774         var dom = ev.getTarget();
21775         //Roo.log(['leave',dom]);
21776         if (!this.currentEl) {
21777             return;
21778         }
21779         
21780         
21781         if (dom != this.currentEl.dom) {
21782             return;
21783         }
21784         var xy = ev.getXY();
21785         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
21786             return;
21787         }
21788         // only activate leave if mouse cursor is outside... bounding box..
21789         
21790         
21791         
21792         
21793         if (this.currentTip) {
21794             this.currentTip.leave();
21795         }
21796         //Roo.log('clear currentEl');
21797         this.currentEl = false;
21798         
21799         
21800     },
21801     alignment : {
21802         'left' : ['r-l', [-2,0], 'right'],
21803         'right' : ['l-r', [2,0], 'left'],
21804         'bottom' : ['t-b', [0,2], 'top'],
21805         'top' : [ 'b-t', [0,-2], 'bottom']
21806     }
21807     
21808 });
21809
21810
21811 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
21812     
21813     
21814     bindEl : false,
21815     
21816     delay : null, // can be { show : 300 , hide: 500}
21817     
21818     timeout : null,
21819     
21820     hoverState : null, //???
21821     
21822     placement : 'bottom', 
21823     
21824     getAutoCreate : function(){
21825     
21826         var cfg = {
21827            cls : 'tooltip',
21828            role : 'tooltip',
21829            cn : [
21830                 {
21831                     cls : 'tooltip-arrow'
21832                 },
21833                 {
21834                     cls : 'tooltip-inner'
21835                 }
21836            ]
21837         };
21838         
21839         return cfg;
21840     },
21841     bind : function(el)
21842     {
21843         this.bindEl = el;
21844     },
21845       
21846     
21847     enter : function () {
21848        
21849         if (this.timeout != null) {
21850             clearTimeout(this.timeout);
21851         }
21852         
21853         this.hoverState = 'in';
21854          //Roo.log("enter - show");
21855         if (!this.delay || !this.delay.show) {
21856             this.show();
21857             return;
21858         }
21859         var _t = this;
21860         this.timeout = setTimeout(function () {
21861             if (_t.hoverState == 'in') {
21862                 _t.show();
21863             }
21864         }, this.delay.show);
21865     },
21866     leave : function()
21867     {
21868         clearTimeout(this.timeout);
21869     
21870         this.hoverState = 'out';
21871          if (!this.delay || !this.delay.hide) {
21872             this.hide();
21873             return 
21874         }
21875        
21876         var _t = this;
21877         this.timeout = setTimeout(function () {
21878             //Roo.log("leave - timeout");
21879             
21880             if (_t.hoverState == 'out') {
21881                 _t.hide();
21882                 Roo.bootstrap.Tooltip.currentEl = false;
21883             }
21884         }, delay)
21885     },
21886     
21887     show : function ()
21888     {
21889         if (!this.el) {
21890             this.render(document.body);
21891         }
21892         // set content.
21893         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
21894         this.el.select('.tooltip-inner',true).first().dom.innerHTML = this.bindEl.attr('tooltip');
21895         
21896         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
21897         
21898         var placement = typeof this.placement == 'function' ?
21899             this.placement.call(this, this.el, on_el) :
21900             this.placement;
21901             
21902         var autoToken = /\s?auto?\s?/i;
21903         var autoPlace = autoToken.test(placement);
21904         if (autoPlace) {
21905             placement = placement.replace(autoToken, '') || 'top';
21906         }
21907         
21908         //this.el.detach()
21909         //this.el.setXY([0,0]);
21910         this.el.show();
21911         //this.el.dom.style.display='block';
21912         this.el.addClass(placement);
21913         
21914         //this.el.appendTo(on_el);
21915         
21916         var p = this.getPosition();
21917         var box = this.el.getBox();
21918         
21919         if (autoPlace) {
21920             // fixme..
21921         }
21922         var align = Roo.bootstrap.Tooltip.alignment[placement];
21923         this.el.alignTo(this.bindEl, align[0],align[1]);
21924         //var arrow = this.el.select('.arrow',true).first();
21925         //arrow.set(align[2], 
21926         
21927         this.el.addClass('in fade');
21928         this.hoverState = null;
21929         
21930         if (this.el.hasClass('fade')) {
21931             // fade it?
21932         }
21933         
21934     },
21935     hide : function()
21936     {
21937          
21938         if (!this.el) {
21939             return;
21940         }
21941         //this.el.setXY([0,0]);
21942         this.el.removeClass('in');
21943         //this.el.hide();
21944         
21945     }
21946     
21947 });
21948  
21949
21950  /*
21951  * - LGPL
21952  *
21953  * Location Picker
21954  * 
21955  */
21956
21957 /**
21958  * @class Roo.bootstrap.LocationPicker
21959  * @extends Roo.bootstrap.Component
21960  * Bootstrap LocationPicker class
21961  * @cfg {Number} latitude Position when init default 0
21962  * @cfg {Number} longitude Position when init default 0
21963  * @cfg {Number} zoom default 15
21964  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
21965  * @cfg {Boolean} mapTypeControl default false
21966  * @cfg {Boolean} disableDoubleClickZoom default false
21967  * @cfg {Boolean} scrollwheel default true
21968  * @cfg {Boolean} streetViewControl default false
21969  * @cfg {Number} radius default 0
21970  * @cfg {String} locationName
21971  * @cfg {Boolean} draggable default true
21972  * @cfg {Boolean} enableAutocomplete default false
21973  * @cfg {Boolean} enableReverseGeocode default true
21974  * @cfg {String} markerTitle
21975  * 
21976  * @constructor
21977  * Create a new LocationPicker
21978  * @param {Object} config The config object
21979  */
21980
21981
21982 Roo.bootstrap.LocationPicker = function(config){
21983     
21984     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
21985     
21986     this.addEvents({
21987         /**
21988          * @event initial
21989          * Fires when the picker initialized.
21990          * @param {Roo.bootstrap.LocationPicker} this
21991          * @param {Google Location} location
21992          */
21993         initial : true,
21994         /**
21995          * @event positionchanged
21996          * Fires when the picker position changed.
21997          * @param {Roo.bootstrap.LocationPicker} this
21998          * @param {Google Location} location
21999          */
22000         positionchanged : true,
22001         /**
22002          * @event resize
22003          * Fires when the map resize.
22004          * @param {Roo.bootstrap.LocationPicker} this
22005          */
22006         resize : true,
22007         /**
22008          * @event show
22009          * Fires when the map show.
22010          * @param {Roo.bootstrap.LocationPicker} this
22011          */
22012         show : true,
22013         /**
22014          * @event hide
22015          * Fires when the map hide.
22016          * @param {Roo.bootstrap.LocationPicker} this
22017          */
22018         hide : true,
22019         /**
22020          * @event mapClick
22021          * Fires when click the map.
22022          * @param {Roo.bootstrap.LocationPicker} this
22023          * @param {Map event} e
22024          */
22025         mapClick : true,
22026         /**
22027          * @event mapRightClick
22028          * Fires when right click the map.
22029          * @param {Roo.bootstrap.LocationPicker} this
22030          * @param {Map event} e
22031          */
22032         mapRightClick : true,
22033         /**
22034          * @event markerClick
22035          * Fires when click the marker.
22036          * @param {Roo.bootstrap.LocationPicker} this
22037          * @param {Map event} e
22038          */
22039         markerClick : true,
22040         /**
22041          * @event markerRightClick
22042          * Fires when right click the marker.
22043          * @param {Roo.bootstrap.LocationPicker} this
22044          * @param {Map event} e
22045          */
22046         markerRightClick : true,
22047         /**
22048          * @event OverlayViewDraw
22049          * Fires when OverlayView Draw
22050          * @param {Roo.bootstrap.LocationPicker} this
22051          */
22052         OverlayViewDraw : true,
22053         /**
22054          * @event OverlayViewOnAdd
22055          * Fires when OverlayView Draw
22056          * @param {Roo.bootstrap.LocationPicker} this
22057          */
22058         OverlayViewOnAdd : true,
22059         /**
22060          * @event OverlayViewOnRemove
22061          * Fires when OverlayView Draw
22062          * @param {Roo.bootstrap.LocationPicker} this
22063          */
22064         OverlayViewOnRemove : true,
22065         /**
22066          * @event OverlayViewShow
22067          * Fires when OverlayView Draw
22068          * @param {Roo.bootstrap.LocationPicker} this
22069          * @param {Pixel} cpx
22070          */
22071         OverlayViewShow : true,
22072         /**
22073          * @event OverlayViewHide
22074          * Fires when OverlayView Draw
22075          * @param {Roo.bootstrap.LocationPicker} this
22076          */
22077         OverlayViewHide : true
22078     });
22079         
22080 };
22081
22082 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22083     
22084     gMapContext: false,
22085     
22086     latitude: 0,
22087     longitude: 0,
22088     zoom: 15,
22089     mapTypeId: false,
22090     mapTypeControl: false,
22091     disableDoubleClickZoom: false,
22092     scrollwheel: true,
22093     streetViewControl: false,
22094     radius: 0,
22095     locationName: '',
22096     draggable: true,
22097     enableAutocomplete: false,
22098     enableReverseGeocode: true,
22099     markerTitle: '',
22100     
22101     getAutoCreate: function()
22102     {
22103
22104         var cfg = {
22105             tag: 'div',
22106             cls: 'roo-location-picker'
22107         };
22108         
22109         return cfg
22110     },
22111     
22112     initEvents: function(ct, position)
22113     {       
22114         if(!this.el.getWidth() || this.isApplied()){
22115             return;
22116         }
22117         
22118         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22119         
22120         this.initial();
22121     },
22122     
22123     initial: function()
22124     {
22125         if(!this.mapTypeId){
22126             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22127         }
22128         
22129         this.gMapContext = this.GMapContext();
22130         
22131         this.initOverlayView();
22132         
22133         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22134         
22135         var _this = this;
22136                 
22137         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22138             _this.setPosition(_this.gMapContext.marker.position);
22139         });
22140         
22141         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22142             _this.fireEvent('mapClick', this, event);
22143             
22144         });
22145
22146         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22147             _this.fireEvent('mapRightClick', this, event);
22148             
22149         });
22150         
22151         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22152             _this.fireEvent('markerClick', this, event);
22153             
22154         });
22155
22156         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22157             _this.fireEvent('markerRightClick', this, event);
22158             
22159         });
22160         
22161         this.setPosition(this.gMapContext.location);
22162         
22163         this.fireEvent('initial', this, this.gMapContext.location);
22164     },
22165     
22166     initOverlayView: function()
22167     {
22168         var _this = this;
22169         
22170         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22171             
22172             draw: function()
22173             {
22174                 _this.fireEvent('OverlayViewDraw', _this);
22175             },
22176             
22177             onAdd: function()
22178             {
22179                 _this.fireEvent('OverlayViewOnAdd', _this);
22180             },
22181             
22182             onRemove: function()
22183             {
22184                 _this.fireEvent('OverlayViewOnRemove', _this);
22185             },
22186             
22187             show: function(cpx)
22188             {
22189                 _this.fireEvent('OverlayViewShow', _this, cpx);
22190             },
22191             
22192             hide: function()
22193             {
22194                 _this.fireEvent('OverlayViewHide', _this);
22195             }
22196             
22197         });
22198     },
22199     
22200     fromLatLngToContainerPixel: function(event)
22201     {
22202         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22203     },
22204     
22205     isApplied: function() 
22206     {
22207         return this.getGmapContext() == false ? false : true;
22208     },
22209     
22210     getGmapContext: function() 
22211     {
22212         return this.gMapContext
22213     },
22214     
22215     GMapContext: function() 
22216     {
22217         var position = new google.maps.LatLng(this.latitude, this.longitude);
22218         
22219         var _map = new google.maps.Map(this.el.dom, {
22220             center: position,
22221             zoom: this.zoom,
22222             mapTypeId: this.mapTypeId,
22223             mapTypeControl: this.mapTypeControl,
22224             disableDoubleClickZoom: this.disableDoubleClickZoom,
22225             scrollwheel: this.scrollwheel,
22226             streetViewControl: this.streetViewControl,
22227             locationName: this.locationName,
22228             draggable: this.draggable,
22229             enableAutocomplete: this.enableAutocomplete,
22230             enableReverseGeocode: this.enableReverseGeocode
22231         });
22232         
22233         var _marker = new google.maps.Marker({
22234             position: position,
22235             map: _map,
22236             title: this.markerTitle,
22237             draggable: this.draggable
22238         });
22239         
22240         return {
22241             map: _map,
22242             marker: _marker,
22243             circle: null,
22244             location: position,
22245             radius: this.radius,
22246             locationName: this.locationName,
22247             addressComponents: {
22248                 formatted_address: null,
22249                 addressLine1: null,
22250                 addressLine2: null,
22251                 streetName: null,
22252                 streetNumber: null,
22253                 city: null,
22254                 district: null,
22255                 state: null,
22256                 stateOrProvince: null
22257             },
22258             settings: this,
22259             domContainer: this.el.dom,
22260             geodecoder: new google.maps.Geocoder()
22261         };
22262     },
22263     
22264     drawCircle: function(center, radius, options) 
22265     {
22266         if (this.gMapContext.circle != null) {
22267             this.gMapContext.circle.setMap(null);
22268         }
22269         if (radius > 0) {
22270             radius *= 1;
22271             options = Roo.apply({}, options, {
22272                 strokeColor: "#0000FF",
22273                 strokeOpacity: .35,
22274                 strokeWeight: 2,
22275                 fillColor: "#0000FF",
22276                 fillOpacity: .2
22277             });
22278             
22279             options.map = this.gMapContext.map;
22280             options.radius = radius;
22281             options.center = center;
22282             this.gMapContext.circle = new google.maps.Circle(options);
22283             return this.gMapContext.circle;
22284         }
22285         
22286         return null;
22287     },
22288     
22289     setPosition: function(location) 
22290     {
22291         this.gMapContext.location = location;
22292         this.gMapContext.marker.setPosition(location);
22293         this.gMapContext.map.panTo(location);
22294         this.drawCircle(location, this.gMapContext.radius, {});
22295         
22296         var _this = this;
22297         
22298         if (this.gMapContext.settings.enableReverseGeocode) {
22299             this.gMapContext.geodecoder.geocode({
22300                 latLng: this.gMapContext.location
22301             }, function(results, status) {
22302                 
22303                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22304                     _this.gMapContext.locationName = results[0].formatted_address;
22305                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22306                     
22307                     _this.fireEvent('positionchanged', this, location);
22308                 }
22309             });
22310             
22311             return;
22312         }
22313         
22314         this.fireEvent('positionchanged', this, location);
22315     },
22316     
22317     resize: function()
22318     {
22319         google.maps.event.trigger(this.gMapContext.map, "resize");
22320         
22321         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22322         
22323         this.fireEvent('resize', this);
22324     },
22325     
22326     setPositionByLatLng: function(latitude, longitude)
22327     {
22328         this.setPosition(new google.maps.LatLng(latitude, longitude));
22329     },
22330     
22331     getCurrentPosition: function() 
22332     {
22333         return {
22334             latitude: this.gMapContext.location.lat(),
22335             longitude: this.gMapContext.location.lng()
22336         };
22337     },
22338     
22339     getAddressName: function() 
22340     {
22341         return this.gMapContext.locationName;
22342     },
22343     
22344     getAddressComponents: function() 
22345     {
22346         return this.gMapContext.addressComponents;
22347     },
22348     
22349     address_component_from_google_geocode: function(address_components) 
22350     {
22351         var result = {};
22352         
22353         for (var i = 0; i < address_components.length; i++) {
22354             var component = address_components[i];
22355             if (component.types.indexOf("postal_code") >= 0) {
22356                 result.postalCode = component.short_name;
22357             } else if (component.types.indexOf("street_number") >= 0) {
22358                 result.streetNumber = component.short_name;
22359             } else if (component.types.indexOf("route") >= 0) {
22360                 result.streetName = component.short_name;
22361             } else if (component.types.indexOf("neighborhood") >= 0) {
22362                 result.city = component.short_name;
22363             } else if (component.types.indexOf("locality") >= 0) {
22364                 result.city = component.short_name;
22365             } else if (component.types.indexOf("sublocality") >= 0) {
22366                 result.district = component.short_name;
22367             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22368                 result.stateOrProvince = component.short_name;
22369             } else if (component.types.indexOf("country") >= 0) {
22370                 result.country = component.short_name;
22371             }
22372         }
22373         
22374         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22375         result.addressLine2 = "";
22376         return result;
22377     },
22378     
22379     setZoomLevel: function(zoom)
22380     {
22381         this.gMapContext.map.setZoom(zoom);
22382     },
22383     
22384     show: function()
22385     {
22386         if(!this.el){
22387             return;
22388         }
22389         
22390         this.el.show();
22391         
22392         this.resize();
22393         
22394         this.fireEvent('show', this);
22395     },
22396     
22397     hide: function()
22398     {
22399         if(!this.el){
22400             return;
22401         }
22402         
22403         this.el.hide();
22404         
22405         this.fireEvent('hide', this);
22406     }
22407     
22408 });
22409
22410 Roo.apply(Roo.bootstrap.LocationPicker, {
22411     
22412     OverlayView : function(map, options)
22413     {
22414         options = options || {};
22415         
22416         this.setMap(map);
22417     }
22418     
22419     
22420 });/*
22421  * - LGPL
22422  *
22423  * Alert
22424  * 
22425  */
22426
22427 /**
22428  * @class Roo.bootstrap.Alert
22429  * @extends Roo.bootstrap.Component
22430  * Bootstrap Alert class
22431  * @cfg {String} title The title of alert
22432  * @cfg {String} html The content of alert
22433  * @cfg {String} weight (  success | info | warning | danger )
22434  * @cfg {String} faicon font-awesomeicon
22435  * 
22436  * @constructor
22437  * Create a new alert
22438  * @param {Object} config The config object
22439  */
22440
22441
22442 Roo.bootstrap.Alert = function(config){
22443     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22444     
22445 };
22446
22447 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22448     
22449     title: '',
22450     html: '',
22451     weight: false,
22452     faicon: false,
22453     
22454     getAutoCreate : function()
22455     {
22456         
22457         var cfg = {
22458             tag : 'div',
22459             cls : 'alert',
22460             cn : [
22461                 {
22462                     tag : 'i',
22463                     cls : 'roo-alert-icon'
22464                     
22465                 },
22466                 {
22467                     tag : 'b',
22468                     cls : 'roo-alert-title',
22469                     html : this.title
22470                 },
22471                 {
22472                     tag : 'span',
22473                     cls : 'roo-alert-text',
22474                     html : this.html
22475                 }
22476             ]
22477         };
22478         
22479         if(this.faicon){
22480             cfg.cn[0].cls += ' fa ' + this.faicon;
22481         }
22482         
22483         if(this.weight){
22484             cfg.cls += ' alert-' + this.weight;
22485         }
22486         
22487         return cfg;
22488     },
22489     
22490     initEvents: function() 
22491     {
22492         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22493     },
22494     
22495     setTitle : function(str)
22496     {
22497         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
22498     },
22499     
22500     setText : function(str)
22501     {
22502         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
22503     },
22504     
22505     setWeight : function(weight)
22506     {
22507         if(this.weight){
22508             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
22509         }
22510         
22511         this.weight = weight;
22512         
22513         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
22514     },
22515     
22516     setIcon : function(icon)
22517     {
22518         if(this.faicon){
22519             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
22520         }
22521         
22522         this.faicon = icon
22523         
22524         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
22525     },
22526     
22527     hide: function() 
22528     {
22529         this.el.hide();   
22530     },
22531     
22532     show: function() 
22533     {  
22534         this.el.show();   
22535     }
22536     
22537 });
22538
22539