roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31 };
32
33 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
34     
35     
36     allowDomMove : false, // to stop relocations in parent onRender...
37     
38     cls : false,
39     
40     style : false,
41     
42     autoCreate : false,
43     
44     tooltip : null,
45     /**
46      * Initialize Events for the element
47      */
48     initEvents : function() { },
49     
50     xattr : false,
51     
52     parentId : false,
53     
54     can_build_overlaid : true,
55     
56     container_method : false,
57     
58     dataId : false,
59     
60     name : false,
61     
62     parent: function() {
63         // returns the parent component..
64         return Roo.ComponentMgr.get(this.parentId)
65         
66         
67     },
68     
69     // private
70     onRender : function(ct, position)
71     {
72        // Roo.log("Call onRender: " + this.xtype);
73         
74         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
75         
76         if(this.el){
77             if (this.el.attr('xtype')) {
78                 this.el.attr('xtypex', this.el.attr('xtype'));
79                 this.el.dom.removeAttribute('xtype');
80                 
81                 this.initEvents();
82             }
83             
84             return;
85         }
86         
87          
88         
89         var cfg = Roo.apply({},  this.getAutoCreate());
90         cfg.id = Roo.id();
91         
92         // fill in the extra attributes 
93         if (this.xattr && typeof(this.xattr) =='object') {
94             for (var i in this.xattr) {
95                 cfg[i] = this.xattr[i];
96             }
97         }
98         
99         if(this.dataId){
100             cfg.dataId = this.dataId;
101         }
102         
103         if (this.cls) {
104             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
105         }
106         
107         if (this.style) { // fixme needs to support more complex style data.
108             cfg.style = this.style;
109         }
110         
111         if(this.name){
112             cfg.name = this.name;
113         }
114         
115        
116         
117         this.el = ct.createChild(cfg, position);
118         
119         if (this.tooltip) {
120             this.tooltipEl().attr('tooltip', this.tooltip);
121         }
122         
123         if(this.tabIndex !== undefined){
124             this.el.dom.setAttribute('tabIndex', this.tabIndex);
125         }
126         this.initEvents();
127         
128         
129     },
130     /**
131      * Fetch the element to add children to
132      * @return {Roo.Element} defaults to this.el
133      */
134     getChildContainer : function()
135     {
136         return this.el;
137     },
138     /**
139      * Fetch the element to display the tooltip on.
140      * @return {Roo.Element} defaults to this.el
141      */
142     tooltipEl : function()
143     {
144         return this.el;
145     },
146         
147     addxtype  : function(tree,cntr)
148     {
149         var cn = this;
150         
151         cn = Roo.factory(tree);
152            
153         cn.parentType = this.xtype; //??
154         cn.parentId = this.id;
155         
156         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
157         if (typeof(cn.container_method) == 'string') {
158             cntr = cn.container_method;
159         }
160         
161         
162         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
163         
164         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
165         
166         var build_from_html =  Roo.XComponent.build_from_html;
167           
168         var is_body  = (tree.xtype == 'Body') ;
169           
170         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
171           
172         var self_cntr_el = Roo.get(this[cntr](false));
173         
174         // do not try and build conditional elements 
175         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
176             return false;
177         }
178         
179         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
180             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
181                 return this.addxtypeChild(tree,cntr);
182             }
183             
184             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
185                 
186             if(echild){
187                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
188             }
189             
190             Roo.log('skipping render');
191             return cn;
192             
193         }
194         
195         var ret = false;
196         
197         while (true) {
198             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
199             
200             if (!echild) {
201                 break;
202             }
203             
204             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
205                 break;
206             }
207             
208             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
209         }
210         return ret;
211     },
212     
213     addxtypeChild : function (tree, cntr)
214     {
215         Roo.debug && Roo.log('addxtypeChild:' + cntr);
216         var cn = this;
217         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
218         
219         
220         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
221                     (typeof(tree['flexy:foreach']) != 'undefined');
222           
223         
224         
225          skip_children = false;
226         // render the element if it's not BODY.
227         if (tree.xtype != 'Body') {
228            
229             cn = Roo.factory(tree);
230            
231             cn.parentType = this.xtype; //??
232             cn.parentId = this.id;
233             
234             var build_from_html =  Roo.XComponent.build_from_html;
235             
236             
237             // does the container contain child eleemnts with 'xtype' attributes.
238             // that match this xtype..
239             // note - when we render we create these as well..
240             // so we should check to see if body has xtype set.
241             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
242                
243                 var self_cntr_el = Roo.get(this[cntr](false));
244                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
245                 
246                 
247                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
248                 // and are not displayed -this causes this to use up the wrong element when matching.
249                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
250                 
251                 
252                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
253                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
254                   
255                   
256                   
257                     cn.el = echild;
258                   //  Roo.log("GOT");
259                     //echild.dom.removeAttribute('xtype');
260                 } else {
261                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
262                     Roo.debug && Roo.log(self_cntr_el);
263                     Roo.debug && Roo.log(echild);
264                     Roo.debug && Roo.log(cn);
265                 }
266             }
267            
268             
269            
270             // if object has flexy:if - then it may or may not be rendered.
271             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
272                 // skip a flexy if element.
273                 Roo.debug && Roo.log('skipping render');
274                 Roo.debug && Roo.log(tree);
275                 if (!cn.el) {
276                     Roo.debug && Roo.log('skipping all children');
277                     skip_children = true;
278                 }
279                 
280              } else {
281                  
282                 // actually if flexy:foreach is found, we really want to create 
283                 // multiple copies here...
284                 //Roo.log('render');
285                 //Roo.log(this[cntr]());
286                 cn.render(this[cntr](true));
287              }
288             // then add the element..
289         }
290         
291         
292         // handle the kids..
293         
294         var nitems = [];
295         /*
296         if (typeof (tree.menu) != 'undefined') {
297             tree.menu.parentType = cn.xtype;
298             tree.menu.triggerEl = cn.el;
299             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
300             
301         }
302         */
303         if (!tree.items || !tree.items.length) {
304             cn.items = nitems;
305             return cn;
306         }
307         var items = tree.items;
308         delete tree.items;
309         
310         //Roo.log(items.length);
311             // add the items..
312         if (!skip_children) {    
313             for(var i =0;i < items.length;i++) {
314                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
315             }
316         }
317         
318         cn.items = nitems;
319         
320         return cn;
321     }
322     
323     
324     
325     
326 });
327
328  /*
329  * - LGPL
330  *
331  * Body
332  * 
333  */
334
335 /**
336  * @class Roo.bootstrap.Body
337  * @extends Roo.bootstrap.Component
338  * Bootstrap Body class
339  * 
340  * @constructor
341  * Create a new body
342  * @param {Object} config The config object
343  */
344
345 Roo.bootstrap.Body = function(config){
346     Roo.bootstrap.Body.superclass.constructor.call(this, config);
347     this.el = Roo.get(document.body);
348     if (this.cls && this.cls.length) {
349         Roo.get(document.body).addClass(this.cls);
350     }
351 };
352
353 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
354       
355         autoCreate : {
356         cls: 'container'
357     },
358     onRender : function(ct, position)
359     {
360        /* Roo.log("Roo.bootstrap.Body - onRender");
361         if (this.cls && this.cls.length) {
362             Roo.get(document.body).addClass(this.cls);
363         }
364         // style??? xttr???
365         */
366     }
367     
368     
369  
370    
371 });
372
373  /*
374  * - LGPL
375  *
376  * button group
377  * 
378  */
379
380
381 /**
382  * @class Roo.bootstrap.ButtonGroup
383  * @extends Roo.bootstrap.Component
384  * Bootstrap ButtonGroup class
385  * @cfg {String} size lg | sm | xs (default empty normal)
386  * @cfg {String} align vertical | justified  (default none)
387  * @cfg {String} direction up | down (default down)
388  * @cfg {Boolean} toolbar false | true
389  * @cfg {Boolean} btn true | false
390  * 
391  * 
392  * @constructor
393  * Create a new Input
394  * @param {Object} config The config object
395  */
396
397 Roo.bootstrap.ButtonGroup = function(config){
398     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
399 };
400
401 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
402     
403     size: '',
404     align: '',
405     direction: '',
406     toolbar: false,
407     btn: true,
408
409     getAutoCreate : function(){
410         var cfg = {
411             cls: 'btn-group',
412             html : null
413         }
414         
415         cfg.html = this.html || cfg.html;
416         
417         if (this.toolbar) {
418             cfg = {
419                 cls: 'btn-toolbar',
420                 html: null
421             }
422             
423             return cfg;
424         }
425         
426         if (['vertical','justified'].indexOf(this.align)!==-1) {
427             cfg.cls = 'btn-group-' + this.align;
428             
429             if (this.align == 'justified') {
430                 console.log(this.items);
431             }
432         }
433         
434         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
435             cfg.cls += ' btn-group-' + this.size;
436         }
437         
438         if (this.direction == 'up') {
439             cfg.cls += ' dropup' ;
440         }
441         
442         return cfg;
443     }
444    
445 });
446
447  /*
448  * - LGPL
449  *
450  * button
451  * 
452  */
453
454 /**
455  * @class Roo.bootstrap.Button
456  * @extends Roo.bootstrap.Component
457  * Bootstrap Button class
458  * @cfg {String} html The button content
459  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
460  * @cfg {String} size ( lg | sm | xs)
461  * @cfg {String} tag ( a | input | submit)
462  * @cfg {String} href empty or href
463  * @cfg {Boolean} disabled default false;
464  * @cfg {Boolean} isClose default false;
465  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
466  * @cfg {String} badge text for badge
467  * @cfg {String} theme default 
468  * @cfg {Boolean} inverse 
469  * @cfg {Boolean} toggle 
470  * @cfg {String} ontext text for on toggle state
471  * @cfg {String} offtext text for off toggle state
472  * @cfg {Boolean} defaulton 
473  * @cfg {Boolean} preventDefault  default true
474  * @cfg {Boolean} removeClass remove the standard class..
475  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
476  * 
477  * @constructor
478  * Create a new button
479  * @param {Object} config The config object
480  */
481
482
483 Roo.bootstrap.Button = function(config){
484     Roo.bootstrap.Button.superclass.constructor.call(this, config);
485     this.addEvents({
486         // raw events
487         /**
488          * @event click
489          * When a butotn is pressed
490          * @param {Roo.EventObject} e
491          */
492         "click" : true,
493          /**
494          * @event toggle
495          * After the button has been toggles
496          * @param {Roo.EventObject} e
497          * @param {boolean} pressed (also available as button.pressed)
498          */
499         "toggle" : true
500     });
501 };
502
503 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
504     html: false,
505     active: false,
506     weight: '',
507     size: '',
508     tag: 'button',
509     href: '',
510     disabled: false,
511     isClose: false,
512     glyphicon: '',
513     badge: '',
514     theme: 'default',
515     inverse: false,
516     
517     toggle: false,
518     ontext: 'ON',
519     offtext: 'OFF',
520     defaulton: true,
521     preventDefault: true,
522     removeClass: false,
523     name: false,
524     target: false,
525     
526     
527     pressed : null,
528      
529     
530     getAutoCreate : function(){
531         
532         var cfg = {
533             tag : 'button',
534             cls : 'roo-button',
535             html: ''
536         };
537         
538         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
539             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
540             this.tag = 'button';
541         } else {
542             cfg.tag = this.tag;
543         }
544         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
545         
546         if (this.toggle == true) {
547             cfg={
548                 tag: 'div',
549                 cls: 'slider-frame roo-button',
550                 cn: [
551                     {
552                         tag: 'span',
553                         'data-on-text':'ON',
554                         'data-off-text':'OFF',
555                         cls: 'slider-button',
556                         html: this.offtext
557                     }
558                 ]
559             };
560             
561             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
562                 cfg.cls += ' '+this.weight;
563             }
564             
565             return cfg;
566         }
567         
568         if (this.isClose) {
569             cfg.cls += ' close';
570             
571             cfg["aria-hidden"] = true;
572             
573             cfg.html = "&times;";
574             
575             return cfg;
576         }
577         
578          
579         if (this.theme==='default') {
580             cfg.cls = 'btn roo-button';
581             
582             //if (this.parentType != 'Navbar') {
583             this.weight = this.weight.length ?  this.weight : 'default';
584             //}
585             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
586                 
587                 cfg.cls += ' btn-' + this.weight;
588             }
589         } else if (this.theme==='glow') {
590             
591             cfg.tag = 'a';
592             cfg.cls = 'btn-glow roo-button';
593             
594             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
595                 
596                 cfg.cls += ' ' + this.weight;
597             }
598         }
599    
600         
601         if (this.inverse) {
602             this.cls += ' inverse';
603         }
604         
605         
606         if (this.active) {
607             cfg.cls += ' active';
608         }
609         
610         if (this.disabled) {
611             cfg.disabled = 'disabled';
612         }
613         
614         if (this.items) {
615             Roo.log('changing to ul' );
616             cfg.tag = 'ul';
617             this.glyphicon = 'caret';
618         }
619         
620         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
621          
622         //gsRoo.log(this.parentType);
623         if (this.parentType === 'Navbar' && !this.parent().bar) {
624             Roo.log('changing to li?');
625             
626             cfg.tag = 'li';
627             
628             cfg.cls = '';
629             cfg.cn =  [{
630                 tag : 'a',
631                 cls : 'roo-button',
632                 html : this.html,
633                 href : this.href || '#'
634             }];
635             if (this.menu) {
636                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
637                 cfg.cls += ' dropdown';
638             }   
639             
640             delete cfg.html;
641             
642         }
643         
644        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
645         
646         if (this.glyphicon) {
647             cfg.html = ' ' + cfg.html;
648             
649             cfg.cn = [
650                 {
651                     tag: 'span',
652                     cls: 'glyphicon glyphicon-' + this.glyphicon
653                 }
654             ];
655         }
656         
657         if (this.badge) {
658             cfg.html += ' ';
659             
660             cfg.tag = 'a';
661             
662 //            cfg.cls='btn roo-button';
663             
664             cfg.href=this.href;
665             
666             var value = cfg.html;
667             
668             if(this.glyphicon){
669                 value = {
670                             tag: 'span',
671                             cls: 'glyphicon glyphicon-' + this.glyphicon,
672                             html: this.html
673                         };
674                 
675             }
676             
677             cfg.cn = [
678                 value,
679                 {
680                     tag: 'span',
681                     cls: 'badge',
682                     html: this.badge
683                 }
684             ];
685             
686             cfg.html='';
687         }
688         
689         if (this.menu) {
690             cfg.cls += ' dropdown';
691             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
692         }
693         
694         if (cfg.tag !== 'a' && this.href !== '') {
695             throw "Tag must be a to set href.";
696         } else if (this.href.length > 0) {
697             cfg.href = this.href;
698         }
699         
700         if(this.removeClass){
701             cfg.cls = '';
702         }
703         
704         if(this.target){
705             cfg.target = this.target;
706         }
707         
708         return cfg;
709     },
710     initEvents: function() {
711        // Roo.log('init events?');
712 //        Roo.log(this.el.dom);
713         // add the menu...
714         
715         if (typeof (this.menu) != 'undefined') {
716             this.menu.parentType = this.xtype;
717             this.menu.triggerEl = this.el;
718             this.addxtype(Roo.apply({}, this.menu));
719         }
720
721
722        if (this.el.hasClass('roo-button')) {
723             this.el.on('click', this.onClick, this);
724        } else {
725             this.el.select('.roo-button').on('click', this.onClick, this);
726        }
727        
728        if(this.removeClass){
729            this.el.on('click', this.onClick, this);
730        }
731        
732        this.el.enableDisplayMode();
733         
734     },
735     onClick : function(e)
736     {
737         if (this.disabled) {
738             return;
739         }
740         
741         
742         Roo.log('button on click ');
743         if(this.preventDefault){
744             e.preventDefault();
745         }
746         if (this.pressed === true || this.pressed === false) {
747             this.pressed = !this.pressed;
748             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
749             this.fireEvent('toggle', this, e, this.pressed);
750         }
751         
752         
753         this.fireEvent('click', this, e);
754     },
755     
756     /**
757      * Enables this button
758      */
759     enable : function()
760     {
761         this.disabled = false;
762         this.el.removeClass('disabled');
763     },
764     
765     /**
766      * Disable this button
767      */
768     disable : function()
769     {
770         this.disabled = true;
771         this.el.addClass('disabled');
772     },
773      /**
774      * sets the active state on/off, 
775      * @param {Boolean} state (optional) Force a particular state
776      */
777     setActive : function(v) {
778         
779         this.el[v ? 'addClass' : 'removeClass']('active');
780     },
781      /**
782      * toggles the current active state 
783      */
784     toggleActive : function()
785     {
786        var active = this.el.hasClass('active');
787        this.setActive(!active);
788        
789         
790     },
791     setText : function(str)
792     {
793         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
794     },
795     getText : function()
796     {
797         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
798     },
799     hide: function() {
800        
801      
802         this.el.hide();   
803     },
804     show: function() {
805        
806         this.el.show();   
807     }
808     
809     
810 });
811
812  /*
813  * - LGPL
814  *
815  * column
816  * 
817  */
818
819 /**
820  * @class Roo.bootstrap.Column
821  * @extends Roo.bootstrap.Component
822  * Bootstrap Column class
823  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
824  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
825  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
826  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
827  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
828  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
829  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
830  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
831  *
832  * 
833  * @cfg {Boolean} hidden (true|false) hide the element
834  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
835  * @cfg {String} fa (ban|check|...) font awesome icon
836  * @cfg {Number} fasize (1|2|....) font awsome size
837
838  * @cfg {String} icon (info-sign|check|...) glyphicon name
839
840  * @cfg {String} html content of column.
841  * 
842  * @constructor
843  * Create a new Column
844  * @param {Object} config The config object
845  */
846
847 Roo.bootstrap.Column = function(config){
848     Roo.bootstrap.Column.superclass.constructor.call(this, config);
849 };
850
851 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
852     
853     xs: false,
854     sm: false,
855     md: false,
856     lg: false,
857     xsoff: false,
858     smoff: false,
859     mdoff: false,
860     lgoff: false,
861     html: '',
862     offset: 0,
863     alert: false,
864     fa: false,
865     icon : false,
866     hidden : false,
867     fasize : 1,
868     
869     getAutoCreate : function(){
870         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
871         
872         cfg = {
873             tag: 'div',
874             cls: 'column'
875         };
876         
877         var settings=this;
878         ['xs','sm','md','lg'].map(function(size){
879             //Roo.log( size + ':' + settings[size]);
880             
881             if (settings[size+'off'] !== false) {
882                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
883             }
884             
885             if (settings[size] === false) {
886                 return;
887             }
888             Roo.log(settings[size]);
889             if (!settings[size]) { // 0 = hidden
890                 cfg.cls += ' hidden-' + size;
891                 return;
892             }
893             cfg.cls += ' col-' + size + '-' + settings[size];
894             
895         });
896         
897         if (this.hidden) {
898             cfg.cls += ' hidden';
899         }
900         
901         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
902             cfg.cls +=' alert alert-' + this.alert;
903         }
904         
905         
906         if (this.html.length) {
907             cfg.html = this.html;
908         }
909         if (this.fa) {
910             var fasize = '';
911             if (this.fasize > 1) {
912                 fasize = ' fa-' + this.fasize + 'x';
913             }
914             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
915             
916             
917         }
918         if (this.icon) {
919             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
920         }
921         
922         return cfg;
923     }
924    
925 });
926
927  
928
929  /*
930  * - LGPL
931  *
932  * page container.
933  * 
934  */
935
936
937 /**
938  * @class Roo.bootstrap.Container
939  * @extends Roo.bootstrap.Component
940  * Bootstrap Container class
941  * @cfg {Boolean} jumbotron is it a jumbotron element
942  * @cfg {String} html content of element
943  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
944  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
945  * @cfg {String} header content of header (for panel)
946  * @cfg {String} footer content of footer (for panel)
947  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
948  * @cfg {String} tag (header|aside|section) type of HTML tag.
949  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
950  * @cfg {String} fa (ban|check|...) font awesome icon
951  * @cfg {String} icon (info-sign|check|...) glyphicon name
952  * @cfg {Boolean} hidden (true|false) hide the element
953
954  *     
955  * @constructor
956  * Create a new Container
957  * @param {Object} config The config object
958  */
959
960 Roo.bootstrap.Container = function(config){
961     Roo.bootstrap.Container.superclass.constructor.call(this, config);
962 };
963
964 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
965     
966     jumbotron : false,
967     well: '',
968     panel : '',
969     header: '',
970     footer : '',
971     sticky: '',
972     tag : false,
973     alert : false,
974     fa: false,
975     icon : false,
976   
977      
978     getChildContainer : function() {
979         
980         if(!this.el){
981             return false;
982         }
983         
984         if (this.panel.length) {
985             return this.el.select('.panel-body',true).first();
986         }
987         
988         return this.el;
989     },
990     
991     
992     getAutoCreate : function(){
993         
994         var cfg = {
995             tag : this.tag || 'div',
996             html : '',
997             cls : ''
998         };
999         if (this.jumbotron) {
1000             cfg.cls = 'jumbotron';
1001         }
1002         
1003         
1004         
1005         // - this is applied by the parent..
1006         //if (this.cls) {
1007         //    cfg.cls = this.cls + '';
1008         //}
1009         
1010         if (this.sticky.length) {
1011             
1012             var bd = Roo.get(document.body);
1013             if (!bd.hasClass('bootstrap-sticky')) {
1014                 bd.addClass('bootstrap-sticky');
1015                 Roo.select('html',true).setStyle('height', '100%');
1016             }
1017              
1018             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1019         }
1020         
1021         
1022         if (this.well.length) {
1023             switch (this.well) {
1024                 case 'lg':
1025                 case 'sm':
1026                     cfg.cls +=' well well-' +this.well;
1027                     break;
1028                 default:
1029                     cfg.cls +=' well';
1030                     break;
1031             }
1032         }
1033         
1034         if (this.hidden) {
1035             cfg.cls += ' hidden';
1036         }
1037         
1038         
1039         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1040             cfg.cls +=' alert alert-' + this.alert;
1041         }
1042         
1043         var body = cfg;
1044         
1045         if (this.panel.length) {
1046             cfg.cls += ' panel panel-' + this.panel;
1047             cfg.cn = [];
1048             if (this.header.length) {
1049                 cfg.cn.push({
1050                     
1051                     cls : 'panel-heading',
1052                     cn : [{
1053                         tag: 'h3',
1054                         cls : 'panel-title',
1055                         html : this.header
1056                     }]
1057                     
1058                 });
1059             }
1060             body = false;
1061             cfg.cn.push({
1062                 cls : 'panel-body',
1063                 html : this.html
1064             });
1065             
1066             
1067             if (this.footer.length) {
1068                 cfg.cn.push({
1069                     cls : 'panel-footer',
1070                     html : this.footer
1071                     
1072                 });
1073             }
1074             
1075         }
1076         
1077         if (body) {
1078             body.html = this.html || cfg.html;
1079             // prefix with the icons..
1080             if (this.fa) {
1081                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1082             }
1083             if (this.icon) {
1084                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1085             }
1086             
1087             
1088         }
1089         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1090             cfg.cls =  'container';
1091         }
1092         
1093         return cfg;
1094     },
1095     
1096     titleEl : function()
1097     {
1098         if(!this.el || !this.panel.length || !this.header.length){
1099             return;
1100         }
1101         
1102         return this.el.select('.panel-title',true).first();
1103     },
1104     
1105     setTitle : function(v)
1106     {
1107         var titleEl = this.titleEl();
1108         
1109         if(!titleEl){
1110             return;
1111         }
1112         
1113         titleEl.dom.innerHTML = v;
1114     },
1115     
1116     getTitle : function()
1117     {
1118         
1119         var titleEl = this.titleEl();
1120         
1121         if(!titleEl){
1122             return '';
1123         }
1124         
1125         return titleEl.dom.innerHTML;
1126     },
1127     
1128     show : function() {
1129         this.el.removeClass('hidden');
1130     },
1131     hide: function() {
1132         if (!this.el.hasClass('hidden')) {
1133             this.el.addClass('hidden');
1134         }
1135         
1136     }
1137    
1138 });
1139
1140  /*
1141  * - LGPL
1142  *
1143  * image
1144  * 
1145  */
1146
1147
1148 /**
1149  * @class Roo.bootstrap.Img
1150  * @extends Roo.bootstrap.Component
1151  * Bootstrap Img class
1152  * @cfg {Boolean} imgResponsive false | true
1153  * @cfg {String} border rounded | circle | thumbnail
1154  * @cfg {String} src image source
1155  * @cfg {String} alt image alternative text
1156  * @cfg {String} href a tag href
1157  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1158  * 
1159  * @constructor
1160  * Create a new Input
1161  * @param {Object} config The config object
1162  */
1163
1164 Roo.bootstrap.Img = function(config){
1165     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1166     
1167     this.addEvents({
1168         // img events
1169         /**
1170          * @event click
1171          * The img click event for the img.
1172          * @param {Roo.EventObject} e
1173          */
1174         "click" : true
1175     });
1176 };
1177
1178 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1179     
1180     imgResponsive: true,
1181     border: '',
1182     src: '',
1183     href: false,
1184     target: false,
1185
1186     getAutoCreate : function(){
1187         
1188         var cfg = {
1189             tag: 'img',
1190             cls: (this.imgResponsive) ? 'img-responsive' : '',
1191             html : null
1192         }
1193         
1194         cfg.html = this.html || cfg.html;
1195         
1196         cfg.src = this.src || cfg.src;
1197         
1198         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1199             cfg.cls += ' img-' + this.border;
1200         }
1201         
1202         if(this.alt){
1203             cfg.alt = this.alt;
1204         }
1205         
1206         if(this.href){
1207             var a = {
1208                 tag: 'a',
1209                 href: this.href,
1210                 cn: [
1211                     cfg
1212                 ]
1213             }
1214             
1215             if(this.target){
1216                 a.target = this.target;
1217             }
1218             
1219         }
1220         
1221         
1222         return (this.href) ? a : cfg;
1223     },
1224     
1225     initEvents: function() {
1226         
1227         if(!this.href){
1228             this.el.on('click', this.onClick, this);
1229         }
1230     },
1231     
1232     onClick : function(e)
1233     {
1234         Roo.log('img onclick');
1235         this.fireEvent('click', this, e);
1236     }
1237    
1238 });
1239
1240  /*
1241  * - LGPL
1242  *
1243  * image
1244  * 
1245  */
1246
1247
1248 /**
1249  * @class Roo.bootstrap.Link
1250  * @extends Roo.bootstrap.Component
1251  * Bootstrap Link Class
1252  * @cfg {String} alt image alternative text
1253  * @cfg {String} href a tag href
1254  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1255  * @cfg {String} html the content of the link.
1256  * @cfg {String} anchor name for the anchor link
1257
1258  * @cfg {Boolean} preventDefault (true | false) default false
1259
1260  * 
1261  * @constructor
1262  * Create a new Input
1263  * @param {Object} config The config object
1264  */
1265
1266 Roo.bootstrap.Link = function(config){
1267     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1268     
1269     this.addEvents({
1270         // img events
1271         /**
1272          * @event click
1273          * The img click event for the img.
1274          * @param {Roo.EventObject} e
1275          */
1276         "click" : true
1277     });
1278 };
1279
1280 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1281     
1282     href: false,
1283     target: false,
1284     preventDefault: false,
1285     anchor : false,
1286     alt : false,
1287
1288     getAutoCreate : function()
1289     {
1290         
1291         var cfg = {
1292             tag: 'a'
1293         };
1294         // anchor's do not require html/href...
1295         if (this.anchor === false) {
1296             cfg.html = this.html || 'html-missing';
1297             cfg.href = this.href || '#';
1298         } else {
1299             cfg.name = this.anchor;
1300             if (this.html !== false) {
1301                 cfg.html = this.html;
1302             }
1303             if (this.href !== false) {
1304                 cfg.href = this.href;
1305             }
1306         }
1307         
1308         if(this.alt !== false){
1309             cfg.alt = this.alt;
1310         }
1311         
1312         
1313         if(this.target !== false) {
1314             cfg.target = this.target;
1315         }
1316         
1317         return cfg;
1318     },
1319     
1320     initEvents: function() {
1321         
1322         if(!this.href || this.preventDefault){
1323             this.el.on('click', this.onClick, this);
1324         }
1325     },
1326     
1327     onClick : function(e)
1328     {
1329         if(this.preventDefault){
1330             e.preventDefault();
1331         }
1332         //Roo.log('img onclick');
1333         this.fireEvent('click', this, e);
1334     }
1335    
1336 });
1337
1338  /*
1339  * - LGPL
1340  *
1341  * header
1342  * 
1343  */
1344
1345 /**
1346  * @class Roo.bootstrap.Header
1347  * @extends Roo.bootstrap.Component
1348  * Bootstrap Header class
1349  * @cfg {String} html content of header
1350  * @cfg {Number} level (1|2|3|4|5|6) default 1
1351  * 
1352  * @constructor
1353  * Create a new Header
1354  * @param {Object} config The config object
1355  */
1356
1357
1358 Roo.bootstrap.Header  = function(config){
1359     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1360 };
1361
1362 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1363     
1364     //href : false,
1365     html : false,
1366     level : 1,
1367     
1368     
1369     
1370     getAutoCreate : function(){
1371         
1372         var cfg = {
1373             tag: 'h' + (1 *this.level),
1374             html: this.html || 'fill in html'
1375         } ;
1376         
1377         return cfg;
1378     }
1379    
1380 });
1381
1382  
1383
1384  /*
1385  * Based on:
1386  * Ext JS Library 1.1.1
1387  * Copyright(c) 2006-2007, Ext JS, LLC.
1388  *
1389  * Originally Released Under LGPL - original licence link has changed is not relivant.
1390  *
1391  * Fork - LGPL
1392  * <script type="text/javascript">
1393  */
1394  
1395 /**
1396  * @class Roo.bootstrap.MenuMgr
1397  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1398  * @singleton
1399  */
1400 Roo.bootstrap.MenuMgr = function(){
1401    var menus, active, groups = {}, attached = false, lastShow = new Date();
1402
1403    // private - called when first menu is created
1404    function init(){
1405        menus = {};
1406        active = new Roo.util.MixedCollection();
1407        Roo.get(document).addKeyListener(27, function(){
1408            if(active.length > 0){
1409                hideAll();
1410            }
1411        });
1412    }
1413
1414    // private
1415    function hideAll(){
1416        if(active && active.length > 0){
1417            var c = active.clone();
1418            c.each(function(m){
1419                m.hide();
1420            });
1421        }
1422    }
1423
1424    // private
1425    function onHide(m){
1426        active.remove(m);
1427        if(active.length < 1){
1428            Roo.get(document).un("mouseup", onMouseDown);
1429             
1430            attached = false;
1431        }
1432    }
1433
1434    // private
1435    function onShow(m){
1436        var last = active.last();
1437        lastShow = new Date();
1438        active.add(m);
1439        if(!attached){
1440           Roo.get(document).on("mouseup", onMouseDown);
1441            
1442            attached = true;
1443        }
1444        if(m.parentMenu){
1445           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1446           m.parentMenu.activeChild = m;
1447        }else if(last && last.isVisible()){
1448           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1449        }
1450    }
1451
1452    // private
1453    function onBeforeHide(m){
1454        if(m.activeChild){
1455            m.activeChild.hide();
1456        }
1457        if(m.autoHideTimer){
1458            clearTimeout(m.autoHideTimer);
1459            delete m.autoHideTimer;
1460        }
1461    }
1462
1463    // private
1464    function onBeforeShow(m){
1465        var pm = m.parentMenu;
1466        if(!pm && !m.allowOtherMenus){
1467            hideAll();
1468        }else if(pm && pm.activeChild && active != m){
1469            pm.activeChild.hide();
1470        }
1471    }
1472
1473    // private
1474    function onMouseDown(e){
1475         Roo.log("on MouseDown");
1476         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1477            hideAll();
1478         }
1479         
1480         
1481    }
1482
1483    // private
1484    function onBeforeCheck(mi, state){
1485        if(state){
1486            var g = groups[mi.group];
1487            for(var i = 0, l = g.length; i < l; i++){
1488                if(g[i] != mi){
1489                    g[i].setChecked(false);
1490                }
1491            }
1492        }
1493    }
1494
1495    return {
1496
1497        /**
1498         * Hides all menus that are currently visible
1499         */
1500        hideAll : function(){
1501             hideAll();  
1502        },
1503
1504        // private
1505        register : function(menu){
1506            if(!menus){
1507                init();
1508            }
1509            menus[menu.id] = menu;
1510            menu.on("beforehide", onBeforeHide);
1511            menu.on("hide", onHide);
1512            menu.on("beforeshow", onBeforeShow);
1513            menu.on("show", onShow);
1514            var g = menu.group;
1515            if(g && menu.events["checkchange"]){
1516                if(!groups[g]){
1517                    groups[g] = [];
1518                }
1519                groups[g].push(menu);
1520                menu.on("checkchange", onCheck);
1521            }
1522        },
1523
1524         /**
1525          * Returns a {@link Roo.menu.Menu} object
1526          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1527          * be used to generate and return a new Menu instance.
1528          */
1529        get : function(menu){
1530            if(typeof menu == "string"){ // menu id
1531                return menus[menu];
1532            }else if(menu.events){  // menu instance
1533                return menu;
1534            }
1535            /*else if(typeof menu.length == 'number'){ // array of menu items?
1536                return new Roo.bootstrap.Menu({items:menu});
1537            }else{ // otherwise, must be a config
1538                return new Roo.bootstrap.Menu(menu);
1539            }
1540            */
1541            return false;
1542        },
1543
1544        // private
1545        unregister : function(menu){
1546            delete menus[menu.id];
1547            menu.un("beforehide", onBeforeHide);
1548            menu.un("hide", onHide);
1549            menu.un("beforeshow", onBeforeShow);
1550            menu.un("show", onShow);
1551            var g = menu.group;
1552            if(g && menu.events["checkchange"]){
1553                groups[g].remove(menu);
1554                menu.un("checkchange", onCheck);
1555            }
1556        },
1557
1558        // private
1559        registerCheckable : function(menuItem){
1560            var g = menuItem.group;
1561            if(g){
1562                if(!groups[g]){
1563                    groups[g] = [];
1564                }
1565                groups[g].push(menuItem);
1566                menuItem.on("beforecheckchange", onBeforeCheck);
1567            }
1568        },
1569
1570        // private
1571        unregisterCheckable : function(menuItem){
1572            var g = menuItem.group;
1573            if(g){
1574                groups[g].remove(menuItem);
1575                menuItem.un("beforecheckchange", onBeforeCheck);
1576            }
1577        }
1578    };
1579 }();/*
1580  * - LGPL
1581  *
1582  * menu
1583  * 
1584  */
1585
1586 /**
1587  * @class Roo.bootstrap.Menu
1588  * @extends Roo.bootstrap.Component
1589  * Bootstrap Menu class - container for MenuItems
1590  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1591  * 
1592  * @constructor
1593  * Create a new Menu
1594  * @param {Object} config The config object
1595  */
1596
1597
1598 Roo.bootstrap.Menu = function(config){
1599     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1600     if (this.registerMenu) {
1601         Roo.bootstrap.MenuMgr.register(this);
1602     }
1603     this.addEvents({
1604         /**
1605          * @event beforeshow
1606          * Fires before this menu is displayed
1607          * @param {Roo.menu.Menu} this
1608          */
1609         beforeshow : true,
1610         /**
1611          * @event beforehide
1612          * Fires before this menu is hidden
1613          * @param {Roo.menu.Menu} this
1614          */
1615         beforehide : true,
1616         /**
1617          * @event show
1618          * Fires after this menu is displayed
1619          * @param {Roo.menu.Menu} this
1620          */
1621         show : true,
1622         /**
1623          * @event hide
1624          * Fires after this menu is hidden
1625          * @param {Roo.menu.Menu} this
1626          */
1627         hide : true,
1628         /**
1629          * @event click
1630          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1631          * @param {Roo.menu.Menu} this
1632          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1633          * @param {Roo.EventObject} e
1634          */
1635         click : true,
1636         /**
1637          * @event mouseover
1638          * Fires when the mouse is hovering over this menu
1639          * @param {Roo.menu.Menu} this
1640          * @param {Roo.EventObject} e
1641          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1642          */
1643         mouseover : true,
1644         /**
1645          * @event mouseout
1646          * Fires when the mouse exits this menu
1647          * @param {Roo.menu.Menu} this
1648          * @param {Roo.EventObject} e
1649          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1650          */
1651         mouseout : true,
1652         /**
1653          * @event itemclick
1654          * Fires when a menu item contained in this menu is clicked
1655          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1656          * @param {Roo.EventObject} e
1657          */
1658         itemclick: true
1659     });
1660     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1661 };
1662
1663 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1664     
1665    /// html : false,
1666     //align : '',
1667     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1668     type: false,
1669     /**
1670      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1671      */
1672     registerMenu : true,
1673     
1674     menuItems :false, // stores the menu items..
1675     
1676     hidden:true,
1677     
1678     parentMenu : false,
1679     
1680     getChildContainer : function() {
1681         return this.el;  
1682     },
1683     
1684     getAutoCreate : function(){
1685          
1686         //if (['right'].indexOf(this.align)!==-1) {
1687         //    cfg.cn[1].cls += ' pull-right'
1688         //}
1689         
1690         
1691         var cfg = {
1692             tag : 'ul',
1693             cls : 'dropdown-menu' ,
1694             style : 'z-index:1000'
1695             
1696         }
1697         
1698         if (this.type === 'submenu') {
1699             cfg.cls = 'submenu active';
1700         }
1701         if (this.type === 'treeview') {
1702             cfg.cls = 'treeview-menu';
1703         }
1704         
1705         return cfg;
1706     },
1707     initEvents : function() {
1708         
1709        // Roo.log("ADD event");
1710        // Roo.log(this.triggerEl.dom);
1711         this.triggerEl.on('click', this.onTriggerPress, this);
1712         this.triggerEl.addClass('dropdown-toggle');
1713         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1714
1715         this.el.on("mouseover", this.onMouseOver, this);
1716         this.el.on("mouseout", this.onMouseOut, this);
1717         
1718         
1719     },
1720     findTargetItem : function(e){
1721         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1722         if(!t){
1723             return false;
1724         }
1725         //Roo.log(t);         Roo.log(t.id);
1726         if(t && t.id){
1727             //Roo.log(this.menuitems);
1728             return this.menuitems.get(t.id);
1729             
1730             //return this.items.get(t.menuItemId);
1731         }
1732         
1733         return false;
1734     },
1735     onClick : function(e){
1736         Roo.log("menu.onClick");
1737         var t = this.findTargetItem(e);
1738         if(!t || t.isContainer){
1739             return;
1740         }
1741         Roo.log(e);
1742         /*
1743         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1744             if(t == this.activeItem && t.shouldDeactivate(e)){
1745                 this.activeItem.deactivate();
1746                 delete this.activeItem;
1747                 return;
1748             }
1749             if(t.canActivate){
1750                 this.setActiveItem(t, true);
1751             }
1752             return;
1753             
1754             
1755         }
1756         */
1757        
1758         Roo.log('pass click event');
1759         
1760         t.onClick(e);
1761         
1762         this.fireEvent("click", this, t, e);
1763         
1764         this.hide();
1765     },
1766      onMouseOver : function(e){
1767         var t  = this.findTargetItem(e);
1768         //Roo.log(t);
1769         //if(t){
1770         //    if(t.canActivate && !t.disabled){
1771         //        this.setActiveItem(t, true);
1772         //    }
1773         //}
1774         
1775         this.fireEvent("mouseover", this, e, t);
1776     },
1777     isVisible : function(){
1778         return !this.hidden;
1779     },
1780      onMouseOut : function(e){
1781         var t  = this.findTargetItem(e);
1782         
1783         //if(t ){
1784         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1785         //        this.activeItem.deactivate();
1786         //        delete this.activeItem;
1787         //    }
1788         //}
1789         this.fireEvent("mouseout", this, e, t);
1790     },
1791     
1792     
1793     /**
1794      * Displays this menu relative to another element
1795      * @param {String/HTMLElement/Roo.Element} element The element to align to
1796      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1797      * the element (defaults to this.defaultAlign)
1798      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1799      */
1800     show : function(el, pos, parentMenu){
1801         this.parentMenu = parentMenu;
1802         if(!this.el){
1803             this.render();
1804         }
1805         this.fireEvent("beforeshow", this);
1806         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1807     },
1808      /**
1809      * Displays this menu at a specific xy position
1810      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1811      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1812      */
1813     showAt : function(xy, parentMenu, /* private: */_e){
1814         this.parentMenu = parentMenu;
1815         if(!this.el){
1816             this.render();
1817         }
1818         if(_e !== false){
1819             this.fireEvent("beforeshow", this);
1820             
1821             //xy = this.el.adjustForConstraints(xy);
1822         }
1823         //this.el.setXY(xy);
1824         //this.el.show();
1825         this.hideMenuItems();
1826         this.hidden = false;
1827         this.triggerEl.addClass('open');
1828         this.focus();
1829         this.fireEvent("show", this);
1830     },
1831     
1832     focus : function(){
1833         return;
1834         if(!this.hidden){
1835             this.doFocus.defer(50, this);
1836         }
1837     },
1838
1839     doFocus : function(){
1840         if(!this.hidden){
1841             this.focusEl.focus();
1842         }
1843     },
1844
1845     /**
1846      * Hides this menu and optionally all parent menus
1847      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1848      */
1849     hide : function(deep){
1850         
1851         this.hideMenuItems();
1852         if(this.el && this.isVisible()){
1853             this.fireEvent("beforehide", this);
1854             if(this.activeItem){
1855                 this.activeItem.deactivate();
1856                 this.activeItem = null;
1857             }
1858             this.triggerEl.removeClass('open');;
1859             this.hidden = true;
1860             this.fireEvent("hide", this);
1861         }
1862         if(deep === true && this.parentMenu){
1863             this.parentMenu.hide(true);
1864         }
1865     },
1866     
1867     onTriggerPress  : function(e)
1868     {
1869         
1870         Roo.log('trigger press');
1871         //Roo.log(e.getTarget());
1872        // Roo.log(this.triggerEl.dom);
1873         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1874             return;
1875         }
1876         if (this.isVisible()) {
1877             Roo.log('hide');
1878             this.hide();
1879         } else {
1880             this.show(this.triggerEl, false, false);
1881         }
1882         
1883         
1884     },
1885     
1886          
1887        
1888     
1889     hideMenuItems : function()
1890     {
1891         //$(backdrop).remove()
1892         Roo.select('.open',true).each(function(aa) {
1893             
1894             aa.removeClass('open');
1895           //var parent = getParent($(this))
1896           //var relatedTarget = { relatedTarget: this }
1897           
1898            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1899           //if (e.isDefaultPrevented()) return
1900            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1901         })
1902     },
1903     addxtypeChild : function (tree, cntr) {
1904         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1905           
1906         this.menuitems.add(comp);
1907         return comp;
1908
1909     },
1910     getEl : function()
1911     {
1912         Roo.log(this.el);
1913         return this.el;
1914     }
1915 });
1916
1917  
1918  /*
1919  * - LGPL
1920  *
1921  * menu item
1922  * 
1923  */
1924
1925
1926 /**
1927  * @class Roo.bootstrap.MenuItem
1928  * @extends Roo.bootstrap.Component
1929  * Bootstrap MenuItem class
1930  * @cfg {String} html the menu label
1931  * @cfg {String} href the link
1932  * @cfg {Boolean} preventDefault (true | false) default true
1933  * @cfg {Boolean} isContainer (true | false) default false
1934  * 
1935  * 
1936  * @constructor
1937  * Create a new MenuItem
1938  * @param {Object} config The config object
1939  */
1940
1941
1942 Roo.bootstrap.MenuItem = function(config){
1943     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1944     this.addEvents({
1945         // raw events
1946         /**
1947          * @event click
1948          * The raw click event for the entire grid.
1949          * @param {Roo.EventObject} e
1950          */
1951         "click" : true
1952     });
1953 };
1954
1955 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1956     
1957     href : false,
1958     html : false,
1959     preventDefault: true,
1960     isContainer : false,
1961     
1962     getAutoCreate : function(){
1963         
1964         if(this.isContainer){
1965             return {
1966                 tag: 'li',
1967                 cls: 'dropdown-menu-item'
1968             };
1969         }
1970         
1971         var cfg= {
1972             tag: 'li',
1973             cls: 'dropdown-menu-item',
1974             cn: [
1975                     {
1976                         tag : 'a',
1977                         href : '#',
1978                         html : 'Link'
1979                     }
1980                 ]
1981         };
1982         if (this.parent().type == 'treeview') {
1983             cfg.cls = 'treeview-menu';
1984         }
1985         
1986         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1987         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1988         return cfg;
1989     },
1990     
1991     initEvents: function() {
1992         
1993         //this.el.select('a').on('click', this.onClick, this);
1994         
1995     },
1996     onClick : function(e)
1997     {
1998         Roo.log('item on click ');
1999         //if(this.preventDefault){
2000         //    e.preventDefault();
2001         //}
2002         //this.parent().hideMenuItems();
2003         
2004         this.fireEvent('click', this, e);
2005     },
2006     getEl : function()
2007     {
2008         return this.el;
2009     }
2010 });
2011
2012  
2013
2014  /*
2015  * - LGPL
2016  *
2017  * menu separator
2018  * 
2019  */
2020
2021
2022 /**
2023  * @class Roo.bootstrap.MenuSeparator
2024  * @extends Roo.bootstrap.Component
2025  * Bootstrap MenuSeparator class
2026  * 
2027  * @constructor
2028  * Create a new MenuItem
2029  * @param {Object} config The config object
2030  */
2031
2032
2033 Roo.bootstrap.MenuSeparator = function(config){
2034     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2035 };
2036
2037 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2038     
2039     getAutoCreate : function(){
2040         var cfg = {
2041             cls: 'divider',
2042             tag : 'li'
2043         };
2044         
2045         return cfg;
2046     }
2047    
2048 });
2049
2050  
2051
2052  
2053 /*
2054 <div class="modal fade">
2055   <div class="modal-dialog">
2056     <div class="modal-content">
2057       <div class="modal-header">
2058         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2059         <h4 class="modal-title">Modal title</h4>
2060       </div>
2061       <div class="modal-body">
2062         <p>One fine body&hellip;</p>
2063       </div>
2064       <div class="modal-footer">
2065         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2066         <button type="button" class="btn btn-primary">Save changes</button>
2067       </div>
2068     </div><!-- /.modal-content -->
2069   </div><!-- /.modal-dialog -->
2070 </div><!-- /.modal -->
2071 */
2072 /*
2073  * - LGPL
2074  *
2075  * page contgainer.
2076  * 
2077  */
2078
2079 /**
2080  * @class Roo.bootstrap.Modal
2081  * @extends Roo.bootstrap.Component
2082  * Bootstrap Modal class
2083  * @cfg {String} title Title of dialog
2084  * @cfg {Boolean} specificTitle default false
2085  * @cfg {Array} buttons Array of buttons or standard button set..
2086  * @cfg {String} buttonPosition (left|right|center) default right
2087  * @cfg {Boolean} animate default true
2088  * @cfg {Boolean} allow_close default true
2089  * 
2090  * @constructor
2091  * Create a new Modal Dialog
2092  * @param {Object} config The config object
2093  */
2094
2095 Roo.bootstrap.Modal = function(config){
2096     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2097     this.addEvents({
2098         // raw events
2099         /**
2100          * @event btnclick
2101          * The raw btnclick event for the button
2102          * @param {Roo.EventObject} e
2103          */
2104         "btnclick" : true
2105     });
2106     this.buttons = this.buttons || [];
2107 };
2108
2109 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2110     
2111     title : 'test dialog',
2112    
2113     buttons : false,
2114     
2115     // set on load...
2116     body:  false,
2117     
2118     specificTitle: false,
2119     
2120     buttonPosition: 'right',
2121     
2122     allow_close : true,
2123     
2124     animate : true,
2125     
2126     onRender : function(ct, position)
2127     {
2128         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2129      
2130         if(!this.el){
2131             var cfg = Roo.apply({},  this.getAutoCreate());
2132             cfg.id = Roo.id();
2133             //if(!cfg.name){
2134             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2135             //}
2136             //if (!cfg.name.length) {
2137             //    delete cfg.name;
2138            // }
2139             if (this.cls) {
2140                 cfg.cls += ' ' + this.cls;
2141             }
2142             if (this.style) {
2143                 cfg.style = this.style;
2144             }
2145             this.el = Roo.get(document.body).createChild(cfg, position);
2146         }
2147         //var type = this.el.dom.type;
2148         
2149         if(this.tabIndex !== undefined){
2150             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2151         }
2152         
2153         
2154         
2155         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2156         this.maskEl.enableDisplayMode("block");
2157         this.maskEl.hide();
2158         //this.el.addClass("x-dlg-modal");
2159     
2160         if (this.buttons.length) {
2161             Roo.each(this.buttons, function(bb) {
2162                 b = Roo.apply({}, bb);
2163                 b.xns = b.xns || Roo.bootstrap;
2164                 b.xtype = b.xtype || 'Button';
2165                 if (typeof(b.listeners) == 'undefined') {
2166                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2167                 }
2168                 
2169                 var btn = Roo.factory(b);
2170                 
2171                 btn.onRender(this.el.select('.modal-footer div').first());
2172                 
2173             },this);
2174         }
2175         // render the children.
2176         var nitems = [];
2177         
2178         if(typeof(this.items) != 'undefined'){
2179             var items = this.items;
2180             delete this.items;
2181
2182             for(var i =0;i < items.length;i++) {
2183                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2184             }
2185         }
2186         
2187         this.items = nitems;
2188         
2189         this.body = this.el.select('.modal-body',true).first();
2190         this.close = this.el.select('.modal-header .close', true).first();
2191         this.footer = this.el.select('.modal-footer',true).first();
2192         this.initEvents();
2193         //this.el.addClass([this.fieldClass, this.cls]);
2194         
2195     },
2196     getAutoCreate : function(){
2197         
2198         
2199         var bdy = {
2200                 cls : 'modal-body',
2201                 html : this.html || ''
2202         };
2203         
2204         var title = {
2205             tag: 'h4',
2206             cls : 'modal-title',
2207             html : this.title
2208         };
2209         
2210         if(this.specificTitle){
2211             title = this.title;
2212             
2213         };
2214         
2215         var header = [];
2216         if (this.allow_close) {
2217             header.push({
2218                 tag: 'button',
2219                 cls : 'close',
2220                 html : '&times'
2221             });
2222         }
2223         header.push(title);
2224         
2225         var modal = {
2226             cls: "modal",
2227             style : 'display: none',
2228             cn : [
2229                 {
2230                     cls: "modal-dialog",
2231                     cn : [
2232                         {
2233                             cls : "modal-content",
2234                             cn : [
2235                                 {
2236                                     cls : 'modal-header',
2237                                     cn : header
2238                                 },
2239                                 bdy,
2240                                 {
2241                                     cls : 'modal-footer',
2242                                     cn : [
2243                                         {
2244                                             tag: 'div',
2245                                             cls: 'btn-' + this.buttonPosition
2246                                         }
2247                                     ]
2248                                     
2249                                 }
2250                                 
2251                                 
2252                             ]
2253                             
2254                         }
2255                     ]
2256                         
2257                 }
2258             ]
2259         };
2260         
2261         if(this.animate){
2262             modal.cls += ' fade';
2263         }
2264         
2265         return modal;
2266           
2267     },
2268     getChildContainer : function() {
2269          
2270          return this.el.select('.modal-body',true).first();
2271         
2272     },
2273     getButtonContainer : function() {
2274          return this.el.select('.modal-footer div',true).first();
2275         
2276     },
2277     initEvents : function()
2278     {
2279         this.el.select('.modal-header .close').on('click', this.hide, this);
2280 //        
2281 //        this.addxtype(this);
2282     },
2283     show : function() {
2284         
2285         if (!this.rendered) {
2286             this.render();
2287         }
2288         
2289         this.el.setStyle('display', 'block');
2290         
2291         if(this.animate){
2292             var _this = this;
2293             (function(){ _this.el.addClass('in'); }).defer(50);
2294         }else{
2295             this.el.addClass('in');
2296         }
2297         
2298         Roo.get(document.body).addClass("x-body-masked");
2299         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2300         this.maskEl.show();
2301         this.el.setStyle('zIndex', '10001');
2302        
2303         this.fireEvent('show', this);
2304         
2305         
2306     },
2307     hide : function()
2308     {
2309         this.maskEl.hide();
2310         Roo.get(document.body).removeClass("x-body-masked");
2311         this.el.removeClass('in');
2312         
2313         if(this.animate){
2314             var _this = this;
2315             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2316         }else{
2317             this.el.setStyle('display', 'none');
2318         }
2319         
2320         this.fireEvent('hide', this);
2321     },
2322     
2323     addButton : function(str, cb)
2324     {
2325          
2326         
2327         var b = Roo.apply({}, { html : str } );
2328         b.xns = b.xns || Roo.bootstrap;
2329         b.xtype = b.xtype || 'Button';
2330         if (typeof(b.listeners) == 'undefined') {
2331             b.listeners = { click : cb.createDelegate(this)  };
2332         }
2333         
2334         var btn = Roo.factory(b);
2335            
2336         btn.onRender(this.el.select('.modal-footer div').first());
2337         
2338         return btn;   
2339        
2340     },
2341     
2342     setDefaultButton : function(btn)
2343     {
2344         //this.el.select('.modal-footer').()
2345     },
2346     resizeTo: function(w,h)
2347     {
2348         // skip..
2349     },
2350     setContentSize  : function(w, h)
2351     {
2352         
2353     },
2354     onButtonClick: function(btn,e)
2355     {
2356         //Roo.log([a,b,c]);
2357         this.fireEvent('btnclick', btn.name, e);
2358     },
2359     setTitle: function(str) {
2360         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2361         
2362     }
2363 });
2364
2365
2366 Roo.apply(Roo.bootstrap.Modal,  {
2367     /**
2368          * Button config that displays a single OK button
2369          * @type Object
2370          */
2371         OK :  [{
2372             name : 'ok',
2373             weight : 'primary',
2374             html : 'OK'
2375         }], 
2376         /**
2377          * Button config that displays Yes and No buttons
2378          * @type Object
2379          */
2380         YESNO : [
2381             {
2382                 name  : 'no',
2383                 html : 'No'
2384             },
2385             {
2386                 name  :'yes',
2387                 weight : 'primary',
2388                 html : 'Yes'
2389             }
2390         ],
2391         
2392         /**
2393          * Button config that displays OK and Cancel buttons
2394          * @type Object
2395          */
2396         OKCANCEL : [
2397             {
2398                name : 'cancel',
2399                 html : 'Cancel'
2400             },
2401             {
2402                 name : 'ok',
2403                 weight : 'primary',
2404                 html : 'OK'
2405             }
2406         ],
2407         /**
2408          * Button config that displays Yes, No and Cancel buttons
2409          * @type Object
2410          */
2411         YESNOCANCEL : [
2412             {
2413                 name : 'yes',
2414                 weight : 'primary',
2415                 html : 'Yes'
2416             },
2417             {
2418                 name : 'no',
2419                 html : 'No'
2420             },
2421             {
2422                 name : 'cancel',
2423                 html : 'Cancel'
2424             }
2425         ]
2426 });
2427  /*
2428  * - LGPL
2429  *
2430  * messagebox - can be used as a replace
2431  * 
2432  */
2433 /**
2434  * @class Roo.MessageBox
2435  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2436  * Example usage:
2437  *<pre><code>
2438 // Basic alert:
2439 Roo.Msg.alert('Status', 'Changes saved successfully.');
2440
2441 // Prompt for user data:
2442 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2443     if (btn == 'ok'){
2444         // process text value...
2445     }
2446 });
2447
2448 // Show a dialog using config options:
2449 Roo.Msg.show({
2450    title:'Save Changes?',
2451    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2452    buttons: Roo.Msg.YESNOCANCEL,
2453    fn: processResult,
2454    animEl: 'elId'
2455 });
2456 </code></pre>
2457  * @singleton
2458  */
2459 Roo.bootstrap.MessageBox = function(){
2460     var dlg, opt, mask, waitTimer;
2461     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2462     var buttons, activeTextEl, bwidth;
2463
2464     
2465     // private
2466     var handleButton = function(button){
2467         dlg.hide();
2468         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2469     };
2470
2471     // private
2472     var handleHide = function(){
2473         if(opt && opt.cls){
2474             dlg.el.removeClass(opt.cls);
2475         }
2476         //if(waitTimer){
2477         //    Roo.TaskMgr.stop(waitTimer);
2478         //    waitTimer = null;
2479         //}
2480     };
2481
2482     // private
2483     var updateButtons = function(b){
2484         var width = 0;
2485         if(!b){
2486             buttons["ok"].hide();
2487             buttons["cancel"].hide();
2488             buttons["yes"].hide();
2489             buttons["no"].hide();
2490             //dlg.footer.dom.style.display = 'none';
2491             return width;
2492         }
2493         dlg.footer.dom.style.display = '';
2494         for(var k in buttons){
2495             if(typeof buttons[k] != "function"){
2496                 if(b[k]){
2497                     buttons[k].show();
2498                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2499                     width += buttons[k].el.getWidth()+15;
2500                 }else{
2501                     buttons[k].hide();
2502                 }
2503             }
2504         }
2505         return width;
2506     };
2507
2508     // private
2509     var handleEsc = function(d, k, e){
2510         if(opt && opt.closable !== false){
2511             dlg.hide();
2512         }
2513         if(e){
2514             e.stopEvent();
2515         }
2516     };
2517
2518     return {
2519         /**
2520          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2521          * @return {Roo.BasicDialog} The BasicDialog element
2522          */
2523         getDialog : function(){
2524            if(!dlg){
2525                 dlg = new Roo.bootstrap.Modal( {
2526                     //draggable: true,
2527                     //resizable:false,
2528                     //constraintoviewport:false,
2529                     //fixedcenter:true,
2530                     //collapsible : false,
2531                     //shim:true,
2532                     //modal: true,
2533                   //  width:400,
2534                   //  height:100,
2535                     //buttonAlign:"center",
2536                     closeClick : function(){
2537                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2538                             handleButton("no");
2539                         }else{
2540                             handleButton("cancel");
2541                         }
2542                     }
2543                 });
2544                 dlg.render();
2545                 dlg.on("hide", handleHide);
2546                 mask = dlg.mask;
2547                 //dlg.addKeyListener(27, handleEsc);
2548                 buttons = {};
2549                 this.buttons = buttons;
2550                 var bt = this.buttonText;
2551                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2552                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2553                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2554                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2555                 Roo.log(buttons)
2556                 bodyEl = dlg.body.createChild({
2557
2558                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2559                         '<textarea class="roo-mb-textarea"></textarea>' +
2560                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2561                 });
2562                 msgEl = bodyEl.dom.firstChild;
2563                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2564                 textboxEl.enableDisplayMode();
2565                 textboxEl.addKeyListener([10,13], function(){
2566                     if(dlg.isVisible() && opt && opt.buttons){
2567                         if(opt.buttons.ok){
2568                             handleButton("ok");
2569                         }else if(opt.buttons.yes){
2570                             handleButton("yes");
2571                         }
2572                     }
2573                 });
2574                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2575                 textareaEl.enableDisplayMode();
2576                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2577                 progressEl.enableDisplayMode();
2578                 var pf = progressEl.dom.firstChild;
2579                 if (pf) {
2580                     pp = Roo.get(pf.firstChild);
2581                     pp.setHeight(pf.offsetHeight);
2582                 }
2583                 
2584             }
2585             return dlg;
2586         },
2587
2588         /**
2589          * Updates the message box body text
2590          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2591          * the XHTML-compliant non-breaking space character '&amp;#160;')
2592          * @return {Roo.MessageBox} This message box
2593          */
2594         updateText : function(text){
2595             if(!dlg.isVisible() && !opt.width){
2596                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2597             }
2598             msgEl.innerHTML = text || '&#160;';
2599       
2600             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2601             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2602             var w = Math.max(
2603                     Math.min(opt.width || cw , this.maxWidth), 
2604                     Math.max(opt.minWidth || this.minWidth, bwidth)
2605             );
2606             if(opt.prompt){
2607                 activeTextEl.setWidth(w);
2608             }
2609             if(dlg.isVisible()){
2610                 dlg.fixedcenter = false;
2611             }
2612             // to big, make it scroll. = But as usual stupid IE does not support
2613             // !important..
2614             
2615             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2616                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2617                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2618             } else {
2619                 bodyEl.dom.style.height = '';
2620                 bodyEl.dom.style.overflowY = '';
2621             }
2622             if (cw > w) {
2623                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2624             } else {
2625                 bodyEl.dom.style.overflowX = '';
2626             }
2627             
2628             dlg.setContentSize(w, bodyEl.getHeight());
2629             if(dlg.isVisible()){
2630                 dlg.fixedcenter = true;
2631             }
2632             return this;
2633         },
2634
2635         /**
2636          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2637          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2638          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2639          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2640          * @return {Roo.MessageBox} This message box
2641          */
2642         updateProgress : function(value, text){
2643             if(text){
2644                 this.updateText(text);
2645             }
2646             if (pp) { // weird bug on my firefox - for some reason this is not defined
2647                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2648             }
2649             return this;
2650         },        
2651
2652         /**
2653          * Returns true if the message box is currently displayed
2654          * @return {Boolean} True if the message box is visible, else false
2655          */
2656         isVisible : function(){
2657             return dlg && dlg.isVisible();  
2658         },
2659
2660         /**
2661          * Hides the message box if it is displayed
2662          */
2663         hide : function(){
2664             if(this.isVisible()){
2665                 dlg.hide();
2666             }  
2667         },
2668
2669         /**
2670          * Displays a new message box, or reinitializes an existing message box, based on the config options
2671          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2672          * The following config object properties are supported:
2673          * <pre>
2674 Property    Type             Description
2675 ----------  ---------------  ------------------------------------------------------------------------------------
2676 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2677                                    closes (defaults to undefined)
2678 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2679                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2680 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2681                                    progress and wait dialogs will ignore this property and always hide the
2682                                    close button as they can only be closed programmatically.
2683 cls               String           A custom CSS class to apply to the message box element
2684 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2685                                    displayed (defaults to 75)
2686 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2687                                    function will be btn (the name of the button that was clicked, if applicable,
2688                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2689                                    Progress and wait dialogs will ignore this option since they do not respond to
2690                                    user actions and can only be closed programmatically, so any required function
2691                                    should be called by the same code after it closes the dialog.
2692 icon              String           A CSS class that provides a background image to be used as an icon for
2693                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2694 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2695 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2696 modal             Boolean          False to allow user interaction with the page while the message box is
2697                                    displayed (defaults to true)
2698 msg               String           A string that will replace the existing message box body text (defaults
2699                                    to the XHTML-compliant non-breaking space character '&#160;')
2700 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2701 progress          Boolean          True to display a progress bar (defaults to false)
2702 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2703 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2704 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2705 title             String           The title text
2706 value             String           The string value to set into the active textbox element if displayed
2707 wait              Boolean          True to display a progress bar (defaults to false)
2708 width             Number           The width of the dialog in pixels
2709 </pre>
2710          *
2711          * Example usage:
2712          * <pre><code>
2713 Roo.Msg.show({
2714    title: 'Address',
2715    msg: 'Please enter your address:',
2716    width: 300,
2717    buttons: Roo.MessageBox.OKCANCEL,
2718    multiline: true,
2719    fn: saveAddress,
2720    animEl: 'addAddressBtn'
2721 });
2722 </code></pre>
2723          * @param {Object} config Configuration options
2724          * @return {Roo.MessageBox} This message box
2725          */
2726         show : function(options)
2727         {
2728             
2729             // this causes nightmares if you show one dialog after another
2730             // especially on callbacks..
2731              
2732             if(this.isVisible()){
2733                 
2734                 this.hide();
2735                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2736                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2737                 Roo.log("New Dialog Message:" +  options.msg )
2738                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2739                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2740                 
2741             }
2742             var d = this.getDialog();
2743             opt = options;
2744             d.setTitle(opt.title || "&#160;");
2745             d.close.setDisplayed(opt.closable !== false);
2746             activeTextEl = textboxEl;
2747             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2748             if(opt.prompt){
2749                 if(opt.multiline){
2750                     textboxEl.hide();
2751                     textareaEl.show();
2752                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2753                         opt.multiline : this.defaultTextHeight);
2754                     activeTextEl = textareaEl;
2755                 }else{
2756                     textboxEl.show();
2757                     textareaEl.hide();
2758                 }
2759             }else{
2760                 textboxEl.hide();
2761                 textareaEl.hide();
2762             }
2763             progressEl.setDisplayed(opt.progress === true);
2764             this.updateProgress(0);
2765             activeTextEl.dom.value = opt.value || "";
2766             if(opt.prompt){
2767                 dlg.setDefaultButton(activeTextEl);
2768             }else{
2769                 var bs = opt.buttons;
2770                 var db = null;
2771                 if(bs && bs.ok){
2772                     db = buttons["ok"];
2773                 }else if(bs && bs.yes){
2774                     db = buttons["yes"];
2775                 }
2776                 dlg.setDefaultButton(db);
2777             }
2778             bwidth = updateButtons(opt.buttons);
2779             this.updateText(opt.msg);
2780             if(opt.cls){
2781                 d.el.addClass(opt.cls);
2782             }
2783             d.proxyDrag = opt.proxyDrag === true;
2784             d.modal = opt.modal !== false;
2785             d.mask = opt.modal !== false ? mask : false;
2786             if(!d.isVisible()){
2787                 // force it to the end of the z-index stack so it gets a cursor in FF
2788                 document.body.appendChild(dlg.el.dom);
2789                 d.animateTarget = null;
2790                 d.show(options.animEl);
2791             }
2792             return this;
2793         },
2794
2795         /**
2796          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2797          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2798          * and closing the message box when the process is complete.
2799          * @param {String} title The title bar text
2800          * @param {String} msg The message box body text
2801          * @return {Roo.MessageBox} This message box
2802          */
2803         progress : function(title, msg){
2804             this.show({
2805                 title : title,
2806                 msg : msg,
2807                 buttons: false,
2808                 progress:true,
2809                 closable:false,
2810                 minWidth: this.minProgressWidth,
2811                 modal : true
2812             });
2813             return this;
2814         },
2815
2816         /**
2817          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2818          * If a callback function is passed it will be called after the user clicks the button, and the
2819          * id of the button that was clicked will be passed as the only parameter to the callback
2820          * (could also be the top-right close button).
2821          * @param {String} title The title bar text
2822          * @param {String} msg The message box body text
2823          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2824          * @param {Object} scope (optional) The scope of the callback function
2825          * @return {Roo.MessageBox} This message box
2826          */
2827         alert : function(title, msg, fn, scope){
2828             this.show({
2829                 title : title,
2830                 msg : msg,
2831                 buttons: this.OK,
2832                 fn: fn,
2833                 scope : scope,
2834                 modal : true
2835             });
2836             return this;
2837         },
2838
2839         /**
2840          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2841          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2842          * You are responsible for closing the message box when the process is complete.
2843          * @param {String} msg The message box body text
2844          * @param {String} title (optional) The title bar text
2845          * @return {Roo.MessageBox} This message box
2846          */
2847         wait : function(msg, title){
2848             this.show({
2849                 title : title,
2850                 msg : msg,
2851                 buttons: false,
2852                 closable:false,
2853                 progress:true,
2854                 modal:true,
2855                 width:300,
2856                 wait:true
2857             });
2858             waitTimer = Roo.TaskMgr.start({
2859                 run: function(i){
2860                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2861                 },
2862                 interval: 1000
2863             });
2864             return this;
2865         },
2866
2867         /**
2868          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2869          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2870          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2871          * @param {String} title The title bar text
2872          * @param {String} msg The message box body text
2873          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2874          * @param {Object} scope (optional) The scope of the callback function
2875          * @return {Roo.MessageBox} This message box
2876          */
2877         confirm : function(title, msg, fn, scope){
2878             this.show({
2879                 title : title,
2880                 msg : msg,
2881                 buttons: this.YESNO,
2882                 fn: fn,
2883                 scope : scope,
2884                 modal : true
2885             });
2886             return this;
2887         },
2888
2889         /**
2890          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2891          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2892          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2893          * (could also be the top-right close button) and the text that was entered will be passed as the two
2894          * parameters to the callback.
2895          * @param {String} title The title bar text
2896          * @param {String} msg The message box body text
2897          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2898          * @param {Object} scope (optional) The scope of the callback function
2899          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2900          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2901          * @return {Roo.MessageBox} This message box
2902          */
2903         prompt : function(title, msg, fn, scope, multiline){
2904             this.show({
2905                 title : title,
2906                 msg : msg,
2907                 buttons: this.OKCANCEL,
2908                 fn: fn,
2909                 minWidth:250,
2910                 scope : scope,
2911                 prompt:true,
2912                 multiline: multiline,
2913                 modal : true
2914             });
2915             return this;
2916         },
2917
2918         /**
2919          * Button config that displays a single OK button
2920          * @type Object
2921          */
2922         OK : {ok:true},
2923         /**
2924          * Button config that displays Yes and No buttons
2925          * @type Object
2926          */
2927         YESNO : {yes:true, no:true},
2928         /**
2929          * Button config that displays OK and Cancel buttons
2930          * @type Object
2931          */
2932         OKCANCEL : {ok:true, cancel:true},
2933         /**
2934          * Button config that displays Yes, No and Cancel buttons
2935          * @type Object
2936          */
2937         YESNOCANCEL : {yes:true, no:true, cancel:true},
2938
2939         /**
2940          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2941          * @type Number
2942          */
2943         defaultTextHeight : 75,
2944         /**
2945          * The maximum width in pixels of the message box (defaults to 600)
2946          * @type Number
2947          */
2948         maxWidth : 600,
2949         /**
2950          * The minimum width in pixels of the message box (defaults to 100)
2951          * @type Number
2952          */
2953         minWidth : 100,
2954         /**
2955          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2956          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2957          * @type Number
2958          */
2959         minProgressWidth : 250,
2960         /**
2961          * An object containing the default button text strings that can be overriden for localized language support.
2962          * Supported properties are: ok, cancel, yes and no.
2963          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2964          * @type Object
2965          */
2966         buttonText : {
2967             ok : "OK",
2968             cancel : "Cancel",
2969             yes : "Yes",
2970             no : "No"
2971         }
2972     };
2973 }();
2974
2975 /**
2976  * Shorthand for {@link Roo.MessageBox}
2977  */
2978 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
2979 Roo.Msg = Roo.Msg || Roo.MessageBox;
2980 /*
2981  * - LGPL
2982  *
2983  * navbar
2984  * 
2985  */
2986
2987 /**
2988  * @class Roo.bootstrap.Navbar
2989  * @extends Roo.bootstrap.Component
2990  * Bootstrap Navbar class
2991
2992  * @constructor
2993  * Create a new Navbar
2994  * @param {Object} config The config object
2995  */
2996
2997
2998 Roo.bootstrap.Navbar = function(config){
2999     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3000     
3001 };
3002
3003 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3004     
3005     
3006    
3007     // private
3008     navItems : false,
3009     loadMask : false,
3010     
3011     
3012     getAutoCreate : function(){
3013         
3014         
3015         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3016         
3017     },
3018     
3019     initEvents :function ()
3020     {
3021         //Roo.log(this.el.select('.navbar-toggle',true));
3022         this.el.select('.navbar-toggle',true).on('click', function() {
3023            // Roo.log('click');
3024             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3025         }, this);
3026         
3027         var mark = {
3028             tag: "div",
3029             cls:"x-dlg-mask"
3030         }
3031         
3032         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3033         
3034         var size = this.el.getSize();
3035         this.maskEl.setSize(size.width, size.height);
3036         this.maskEl.enableDisplayMode("block");
3037         this.maskEl.hide();
3038         
3039         if(this.loadMask){
3040             this.maskEl.show();
3041         }
3042     },
3043     
3044     
3045     getChildContainer : function()
3046     {
3047         if (this.el.select('.collapse').getCount()) {
3048             return this.el.select('.collapse',true).first();
3049         }
3050         
3051         return this.el;
3052     },
3053     
3054     mask : function()
3055     {
3056         this.maskEl.show();
3057     },
3058     
3059     unmask : function()
3060     {
3061         this.maskEl.hide();
3062     } 
3063     
3064     
3065     
3066     
3067 });
3068
3069
3070
3071  
3072
3073  /*
3074  * - LGPL
3075  *
3076  * navbar
3077  * 
3078  */
3079
3080 /**
3081  * @class Roo.bootstrap.NavSimplebar
3082  * @extends Roo.bootstrap.Navbar
3083  * Bootstrap Sidebar class
3084  *
3085  * @cfg {Boolean} inverse is inverted color
3086  * 
3087  * @cfg {String} type (nav | pills | tabs)
3088  * @cfg {Boolean} arrangement stacked | justified
3089  * @cfg {String} align (left | right) alignment
3090  * 
3091  * @cfg {Boolean} main (true|false) main nav bar? default false
3092  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3093  * 
3094  * @cfg {String} tag (header|footer|nav|div) default is nav 
3095
3096  * 
3097  * 
3098  * 
3099  * @constructor
3100  * Create a new Sidebar
3101  * @param {Object} config The config object
3102  */
3103
3104
3105 Roo.bootstrap.NavSimplebar = function(config){
3106     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3107 };
3108
3109 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3110     
3111     inverse: false,
3112     
3113     type: false,
3114     arrangement: '',
3115     align : false,
3116     
3117     
3118     
3119     main : false,
3120     
3121     
3122     tag : false,
3123     
3124     
3125     getAutoCreate : function(){
3126         
3127         
3128         var cfg = {
3129             tag : this.tag || 'div',
3130             cls : 'navbar'
3131         };
3132           
3133         
3134         cfg.cn = [
3135             {
3136                 cls: 'nav',
3137                 tag : 'ul'
3138             }
3139         ];
3140         
3141          
3142         this.type = this.type || 'nav';
3143         if (['tabs','pills'].indexOf(this.type)!==-1) {
3144             cfg.cn[0].cls += ' nav-' + this.type
3145         
3146         
3147         } else {
3148             if (this.type!=='nav') {
3149                 Roo.log('nav type must be nav/tabs/pills')
3150             }
3151             cfg.cn[0].cls += ' navbar-nav'
3152         }
3153         
3154         
3155         
3156         
3157         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3158             cfg.cn[0].cls += ' nav-' + this.arrangement;
3159         }
3160         
3161         
3162         if (this.align === 'right') {
3163             cfg.cn[0].cls += ' navbar-right';
3164         }
3165         
3166         if (this.inverse) {
3167             cfg.cls += ' navbar-inverse';
3168             
3169         }
3170         
3171         
3172         return cfg;
3173     
3174         
3175     }
3176     
3177     
3178     
3179 });
3180
3181
3182
3183  
3184
3185  
3186        /*
3187  * - LGPL
3188  *
3189  * navbar
3190  * 
3191  */
3192
3193 /**
3194  * @class Roo.bootstrap.NavHeaderbar
3195  * @extends Roo.bootstrap.NavSimplebar
3196  * Bootstrap Sidebar class
3197  *
3198  * @cfg {String} brand what is brand
3199  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3200  * @cfg {String} brand_href href of the brand
3201  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3202  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3203  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3204  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3205  * 
3206  * @constructor
3207  * Create a new Sidebar
3208  * @param {Object} config The config object
3209  */
3210
3211
3212 Roo.bootstrap.NavHeaderbar = function(config){
3213     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3214       
3215 };
3216
3217 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3218     
3219     position: '',
3220     brand: '',
3221     brand_href: false,
3222     srButton : true,
3223     autohide : false,
3224     desktopCenter : false,
3225    
3226     
3227     getAutoCreate : function(){
3228         
3229         var   cfg = {
3230             tag: this.nav || 'nav',
3231             cls: 'navbar',
3232             role: 'navigation',
3233             cn: []
3234         };
3235         
3236         var cn = cfg.cn;
3237         if (this.desktopCenter) {
3238             cn.push({cls : 'container', cn : []});
3239             cn = cn[0].cn;
3240         }
3241         
3242         if(this.srButton){
3243             cn.push({
3244                 tag: 'div',
3245                 cls: 'navbar-header',
3246                 cn: [
3247                     {
3248                         tag: 'button',
3249                         type: 'button',
3250                         cls: 'navbar-toggle',
3251                         'data-toggle': 'collapse',
3252                         cn: [
3253                             {
3254                                 tag: 'span',
3255                                 cls: 'sr-only',
3256                                 html: 'Toggle navigation'
3257                             },
3258                             {
3259                                 tag: 'span',
3260                                 cls: 'icon-bar'
3261                             },
3262                             {
3263                                 tag: 'span',
3264                                 cls: 'icon-bar'
3265                             },
3266                             {
3267                                 tag: 'span',
3268                                 cls: 'icon-bar'
3269                             }
3270                         ]
3271                     }
3272                 ]
3273             });
3274         }
3275         
3276         cn.push({
3277             tag: 'div',
3278             cls: 'collapse navbar-collapse',
3279             cn : []
3280         });
3281         
3282         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3283         
3284         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3285             cfg.cls += ' navbar-' + this.position;
3286             
3287             // tag can override this..
3288             
3289             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3290         }
3291         
3292         if (this.brand !== '') {
3293             cn[0].cn.push({
3294                 tag: 'a',
3295                 href: this.brand_href ? this.brand_href : '#',
3296                 cls: 'navbar-brand',
3297                 cn: [
3298                 this.brand
3299                 ]
3300             });
3301         }
3302         
3303         if(this.main){
3304             cfg.cls += ' main-nav';
3305         }
3306         
3307         
3308         return cfg;
3309
3310         
3311     },
3312     getHeaderChildContainer : function()
3313     {
3314         if (this.el.select('.navbar-header').getCount()) {
3315             return this.el.select('.navbar-header',true).first();
3316         }
3317         
3318         return this.getChildContainer();
3319     },
3320     
3321     
3322     initEvents : function()
3323     {
3324         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3325         
3326         if (this.autohide) {
3327             
3328             var prevScroll = 0;
3329             var ft = this.el;
3330             
3331             Roo.get(document).on('scroll',function(e) {
3332                 var ns = Roo.get(document).getScroll().top;
3333                 var os = prevScroll;
3334                 prevScroll = ns;
3335                 
3336                 if(ns > os){
3337                     ft.removeClass('slideDown');
3338                     ft.addClass('slideUp');
3339                     return;
3340                 }
3341                 ft.removeClass('slideUp');
3342                 ft.addClass('slideDown');
3343                  
3344               
3345           },this);
3346         }
3347     }    
3348           
3349       
3350     
3351     
3352 });
3353
3354
3355
3356  
3357
3358  /*
3359  * - LGPL
3360  *
3361  * navbar
3362  * 
3363  */
3364
3365 /**
3366  * @class Roo.bootstrap.NavSidebar
3367  * @extends Roo.bootstrap.Navbar
3368  * Bootstrap Sidebar class
3369  * 
3370  * @constructor
3371  * Create a new Sidebar
3372  * @param {Object} config The config object
3373  */
3374
3375
3376 Roo.bootstrap.NavSidebar = function(config){
3377     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3378 };
3379
3380 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3381     
3382     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3383     
3384     getAutoCreate : function(){
3385         
3386         
3387         return  {
3388             tag: 'div',
3389             cls: 'sidebar sidebar-nav'
3390         };
3391     
3392         
3393     }
3394     
3395     
3396     
3397 });
3398
3399
3400
3401  
3402
3403  /*
3404  * - LGPL
3405  *
3406  * nav group
3407  * 
3408  */
3409
3410 /**
3411  * @class Roo.bootstrap.NavGroup
3412  * @extends Roo.bootstrap.Component
3413  * Bootstrap NavGroup class
3414  * @cfg {String} align left | right
3415  * @cfg {Boolean} inverse false | true
3416  * @cfg {String} type (nav|pills|tab) default nav
3417  * @cfg {String} navId - reference Id for navbar.
3418
3419  * 
3420  * @constructor
3421  * Create a new nav group
3422  * @param {Object} config The config object
3423  */
3424
3425 Roo.bootstrap.NavGroup = function(config){
3426     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3427     this.navItems = [];
3428    
3429     Roo.bootstrap.NavGroup.register(this);
3430      this.addEvents({
3431         /**
3432              * @event changed
3433              * Fires when the active item changes
3434              * @param {Roo.bootstrap.NavGroup} this
3435              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3436              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3437          */
3438         'changed': true
3439      });
3440     
3441 };
3442
3443 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3444     
3445     align: '',
3446     inverse: false,
3447     form: false,
3448     type: 'nav',
3449     navId : '',
3450     // private
3451     
3452     navItems : false, 
3453     
3454     getAutoCreate : function()
3455     {
3456         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3457         
3458         cfg = {
3459             tag : 'ul',
3460             cls: 'nav' 
3461         }
3462         
3463         if (['tabs','pills'].indexOf(this.type)!==-1) {
3464             cfg.cls += ' nav-' + this.type
3465         } else {
3466             if (this.type!=='nav') {
3467                 Roo.log('nav type must be nav/tabs/pills')
3468             }
3469             cfg.cls += ' navbar-nav'
3470         }
3471         
3472         if (this.parent().sidebar) {
3473             cfg = {
3474                 tag: 'ul',
3475                 cls: 'dashboard-menu sidebar-menu'
3476             }
3477             
3478             return cfg;
3479         }
3480         
3481         if (this.form === true) {
3482             cfg = {
3483                 tag: 'form',
3484                 cls: 'navbar-form'
3485             }
3486             
3487             if (this.align === 'right') {
3488                 cfg.cls += ' navbar-right';
3489             } else {
3490                 cfg.cls += ' navbar-left';
3491             }
3492         }
3493         
3494         if (this.align === 'right') {
3495             cfg.cls += ' navbar-right';
3496         }
3497         
3498         if (this.inverse) {
3499             cfg.cls += ' navbar-inverse';
3500             
3501         }
3502         
3503         
3504         return cfg;
3505     },
3506     /**
3507     * sets the active Navigation item
3508     * @param {Roo.bootstrap.NavItem} the new current navitem
3509     */
3510     setActiveItem : function(item)
3511     {
3512         var prev = false;
3513         Roo.each(this.navItems, function(v){
3514             if (v == item) {
3515                 return ;
3516             }
3517             if (v.isActive()) {
3518                 v.setActive(false, true);
3519                 prev = v;
3520                 
3521             }
3522             
3523         });
3524
3525         item.setActive(true, true);
3526         this.fireEvent('changed', this, item, prev);
3527         
3528         
3529     },
3530     /**
3531     * gets the active Navigation item
3532     * @return {Roo.bootstrap.NavItem} the current navitem
3533     */
3534     getActive : function()
3535     {
3536         
3537         var prev = false;
3538         Roo.each(this.navItems, function(v){
3539             
3540             if (v.isActive()) {
3541                 prev = v;
3542                 
3543             }
3544             
3545         });
3546         return prev;
3547     },
3548     
3549     indexOfNav : function()
3550     {
3551         
3552         var prev = false;
3553         Roo.each(this.navItems, function(v,i){
3554             
3555             if (v.isActive()) {
3556                 prev = i;
3557                 
3558             }
3559             
3560         });
3561         return prev;
3562     },
3563     /**
3564     * adds a Navigation item
3565     * @param {Roo.bootstrap.NavItem} the navitem to add
3566     */
3567     addItem : function(cfg)
3568     {
3569         var cn = new Roo.bootstrap.NavItem(cfg);
3570         this.register(cn);
3571         cn.parentId = this.id;
3572         cn.onRender(this.el, null);
3573         return cn;
3574     },
3575     /**
3576     * register a Navigation item
3577     * @param {Roo.bootstrap.NavItem} the navitem to add
3578     */
3579     register : function(item)
3580     {
3581         this.navItems.push( item);
3582         item.navId = this.navId;
3583     
3584     },
3585     
3586     /**
3587     * clear all the Navigation item
3588     */
3589    
3590     clearAll : function()
3591     {
3592         this.navItems = [];
3593         this.el.dom.innerHTML = '';
3594     },
3595     
3596     getNavItem: function(tabId)
3597     {
3598         var ret = false;
3599         Roo.each(this.navItems, function(e) {
3600             if (e.tabId == tabId) {
3601                ret =  e;
3602                return false;
3603             }
3604             return true;
3605             
3606         });
3607         return ret;
3608     },
3609     
3610     setActiveNext : function()
3611     {
3612         var i = this.indexOfNav(this.getActive());
3613         if (i > this.navItems.length) {
3614             return;
3615         }
3616         this.setActiveItem(this.navItems[i+1]);
3617     },
3618     setActivePrev : function()
3619     {
3620         var i = this.indexOfNav(this.getActive());
3621         if (i  < 1) {
3622             return;
3623         }
3624         this.setActiveItem(this.navItems[i-1]);
3625     },
3626     clearWasActive : function(except) {
3627         Roo.each(this.navItems, function(e) {
3628             if (e.tabId != except.tabId && e.was_active) {
3629                e.was_active = false;
3630                return false;
3631             }
3632             return true;
3633             
3634         });
3635     },
3636     getWasActive : function ()
3637     {
3638         var r = false;
3639         Roo.each(this.navItems, function(e) {
3640             if (e.was_active) {
3641                r = e;
3642                return false;
3643             }
3644             return true;
3645             
3646         });
3647         return r;
3648     }
3649     
3650     
3651 });
3652
3653  
3654 Roo.apply(Roo.bootstrap.NavGroup, {
3655     
3656     groups: {},
3657      /**
3658     * register a Navigation Group
3659     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3660     */
3661     register : function(navgrp)
3662     {
3663         this.groups[navgrp.navId] = navgrp;
3664         
3665     },
3666     /**
3667     * fetch a Navigation Group based on the navigation ID
3668     * @param {string} the navgroup to add
3669     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3670     */
3671     get: function(navId) {
3672         if (typeof(this.groups[navId]) == 'undefined') {
3673             return false;
3674             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3675         }
3676         return this.groups[navId] ;
3677     }
3678     
3679     
3680     
3681 });
3682
3683  /*
3684  * - LGPL
3685  *
3686  * row
3687  * 
3688  */
3689
3690 /**
3691  * @class Roo.bootstrap.NavItem
3692  * @extends Roo.bootstrap.Component
3693  * Bootstrap Navbar.NavItem class
3694  * @cfg {String} href  link to
3695  * @cfg {String} html content of button
3696  * @cfg {String} badge text inside badge
3697  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3698  * @cfg {String} glyphicon name of glyphicon
3699  * @cfg {String} icon name of font awesome icon
3700  * @cfg {Boolean} active Is item active
3701  * @cfg {Boolean} disabled Is item disabled
3702  
3703  * @cfg {Boolean} preventDefault (true | false) default false
3704  * @cfg {String} tabId the tab that this item activates.
3705  * @cfg {String} tagtype (a|span) render as a href or span?
3706   
3707  * @constructor
3708  * Create a new Navbar Item
3709  * @param {Object} config The config object
3710  */
3711 Roo.bootstrap.NavItem = function(config){
3712     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3713     this.addEvents({
3714         // raw events
3715         /**
3716          * @event click
3717          * The raw click event for the entire grid.
3718          * @param {Roo.EventObject} e
3719          */
3720         "click" : true,
3721          /**
3722             * @event changed
3723             * Fires when the active item active state changes
3724             * @param {Roo.bootstrap.NavItem} this
3725             * @param {boolean} state the new state
3726              
3727          */
3728         'changed': true
3729     });
3730    
3731 };
3732
3733 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3734     
3735     href: false,
3736     html: '',
3737     badge: '',
3738     icon: false,
3739     glyphicon: false,
3740     active: false,
3741     preventDefault : false,
3742     tabId : false,
3743     tagtype : 'a',
3744     disabled : false,
3745     
3746     was_active : false,
3747     
3748     getAutoCreate : function(){
3749          
3750         var cfg = {
3751             tag: 'li',
3752             cls: 'nav-item'
3753             
3754         }
3755         if (this.active) {
3756             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3757         }
3758         if (this.disabled) {
3759             cfg.cls += ' disabled';
3760         }
3761         
3762         if (this.href || this.html || this.glyphicon || this.icon) {
3763             cfg.cn = [
3764                 {
3765                     tag: this.tagtype,
3766                     href : this.href || "#",
3767                     html: this.html || ''
3768                 }
3769             ];
3770             
3771             if (this.icon) {
3772                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3773             }
3774
3775             if(this.glyphicon) {
3776                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3777             }
3778             
3779             if (this.menu) {
3780                 
3781                 cfg.cn[0].html += " <span class='caret'></span>";
3782              
3783             }
3784             
3785             if (this.badge !== '') {
3786                  
3787                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3788             }
3789         }
3790         
3791         
3792         
3793         return cfg;
3794     },
3795     initEvents: function() 
3796     {
3797         if (typeof (this.menu) != 'undefined') {
3798             this.menu.parentType = this.xtype;
3799             this.menu.triggerEl = this.el;
3800             this.menu = this.addxtype(Roo.apply({}, this.menu));
3801         }
3802         
3803         this.el.select('a',true).on('click', this.onClick, this);
3804         
3805         if(this.tagtype == 'span'){
3806             this.el.select('span',true).on('click', this.onClick, this);
3807         }
3808        
3809         // at this point parent should be available..
3810         this.parent().register(this);
3811     },
3812     
3813     onClick : function(e)
3814     {
3815         if(this.preventDefault || this.href == '#'){
3816             e.preventDefault();
3817         }
3818         
3819         if (this.disabled) {
3820             return;
3821         }
3822         
3823         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3824         if (tg && tg.transition) {
3825             Roo.log("waiting for the transitionend");
3826             return;
3827         }
3828         
3829         Roo.log("fire event clicked");
3830         if(this.fireEvent('click', this, e) === false){
3831             return;
3832         };
3833         
3834         if(this.tagtype == 'span'){
3835             return;
3836         }
3837         
3838         var p = this.parent();
3839         if (['tabs','pills'].indexOf(p.type)!==-1) {
3840             if (typeof(p.setActiveItem) !== 'undefined') {
3841                 p.setActiveItem(this);
3842             }
3843         }
3844         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3845         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3846             // remove the collapsed menu expand...
3847             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3848         }
3849         
3850     },
3851     
3852     isActive: function () {
3853         return this.active
3854     },
3855     setActive : function(state, fire, is_was_active)
3856     {
3857         if (this.active && !state & this.navId) {
3858             this.was_active = true;
3859             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3860             if (nv) {
3861                 nv.clearWasActive(this);
3862             }
3863             
3864         }
3865         this.active = state;
3866         
3867         if (!state ) {
3868             this.el.removeClass('active');
3869         } else if (!this.el.hasClass('active')) {
3870             this.el.addClass('active');
3871         }
3872         if (fire) {
3873             this.fireEvent('changed', this, state);
3874         }
3875         
3876         // show a panel if it's registered and related..
3877         
3878         if (!this.navId || !this.tabId || !state || is_was_active) {
3879             return;
3880         }
3881         
3882         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3883         if (!tg) {
3884             return;
3885         }
3886         var pan = tg.getPanelByName(this.tabId);
3887         if (!pan) {
3888             return;
3889         }
3890         // if we can not flip to new panel - go back to old nav highlight..
3891         if (false == tg.showPanel(pan)) {
3892             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3893             if (nv) {
3894                 var onav = nv.getWasActive();
3895                 if (onav) {
3896                     onav.setActive(true, false, true);
3897                 }
3898             }
3899             
3900         }
3901         
3902         
3903         
3904     },
3905      // this should not be here...
3906     setDisabled : function(state)
3907     {
3908         this.disabled = state;
3909         if (!state ) {
3910             this.el.removeClass('disabled');
3911         } else if (!this.el.hasClass('disabled')) {
3912             this.el.addClass('disabled');
3913         }
3914         
3915     },
3916     
3917     /**
3918      * Fetch the element to display the tooltip on.
3919      * @return {Roo.Element} defaults to this.el
3920      */
3921     tooltipEl : function()
3922     {
3923         return this.el.select('' + this.tagtype + '', true).first();
3924     }
3925 });
3926  
3927
3928  /*
3929  * - LGPL
3930  *
3931  * sidebar item
3932  *
3933  *  li
3934  *    <span> icon </span>
3935  *    <span> text </span>
3936  *    <span>badge </span>
3937  */
3938
3939 /**
3940  * @class Roo.bootstrap.NavSidebarItem
3941  * @extends Roo.bootstrap.NavItem
3942  * Bootstrap Navbar.NavSidebarItem class
3943  * @constructor
3944  * Create a new Navbar Button
3945  * @param {Object} config The config object
3946  */
3947 Roo.bootstrap.NavSidebarItem = function(config){
3948     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3949     this.addEvents({
3950         // raw events
3951         /**
3952          * @event click
3953          * The raw click event for the entire grid.
3954          * @param {Roo.EventObject} e
3955          */
3956         "click" : true,
3957          /**
3958             * @event changed
3959             * Fires when the active item active state changes
3960             * @param {Roo.bootstrap.NavSidebarItem} this
3961             * @param {boolean} state the new state
3962              
3963          */
3964         'changed': true
3965     });
3966    
3967 };
3968
3969 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3970     
3971     
3972     getAutoCreate : function(){
3973         
3974         
3975         var a = {
3976                 tag: 'a',
3977                 href : this.href || '#',
3978                 cls: '',
3979                 html : '',
3980                 cn : []
3981         };
3982         var cfg = {
3983             tag: 'li',
3984             cls: '',
3985             cn: [ a ]
3986         }
3987         var span = {
3988             tag: 'span',
3989             html : this.html || ''
3990         }
3991         
3992         
3993         if (this.active) {
3994             cfg.cls += ' active';
3995         }
3996         
3997         // left icon..
3998         if (this.glyphicon || this.icon) {
3999             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4000             a.cn.push({ tag : 'i', cls : c }) ;
4001         }
4002         // html..
4003         a.cn.push(span);
4004         // then badge..
4005         if (this.badge !== '') {
4006             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4007         }
4008         // fi
4009         if (this.menu) {
4010             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4011             a.cls += 'dropdown-toggle treeview' ;
4012             
4013         }
4014         
4015         
4016         
4017         return cfg;
4018          
4019            
4020     }
4021    
4022      
4023  
4024 });
4025  
4026
4027  /*
4028  * - LGPL
4029  *
4030  * row
4031  * 
4032  */
4033
4034 /**
4035  * @class Roo.bootstrap.Row
4036  * @extends Roo.bootstrap.Component
4037  * Bootstrap Row class (contains columns...)
4038  * 
4039  * @constructor
4040  * Create a new Row
4041  * @param {Object} config The config object
4042  */
4043
4044 Roo.bootstrap.Row = function(config){
4045     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4046 };
4047
4048 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4049     
4050     getAutoCreate : function(){
4051        return {
4052             cls: 'row clearfix'
4053        };
4054     }
4055     
4056     
4057 });
4058
4059  
4060
4061  /*
4062  * - LGPL
4063  *
4064  * element
4065  * 
4066  */
4067
4068 /**
4069  * @class Roo.bootstrap.Element
4070  * @extends Roo.bootstrap.Component
4071  * Bootstrap Element class
4072  * @cfg {String} html contents of the element
4073  * @cfg {String} tag tag of the element
4074  * @cfg {String} cls class of the element
4075  * 
4076  * @constructor
4077  * Create a new Element
4078  * @param {Object} config The config object
4079  */
4080
4081 Roo.bootstrap.Element = function(config){
4082     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4083 };
4084
4085 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4086     
4087     tag: 'div',
4088     cls: '',
4089     html: '',
4090      
4091     
4092     getAutoCreate : function(){
4093         
4094         var cfg = {
4095             tag: this.tag,
4096             cls: this.cls,
4097             html: this.html
4098         }
4099         
4100         
4101         
4102         return cfg;
4103     }
4104    
4105 });
4106
4107  
4108
4109  /*
4110  * - LGPL
4111  *
4112  * pagination
4113  * 
4114  */
4115
4116 /**
4117  * @class Roo.bootstrap.Pagination
4118  * @extends Roo.bootstrap.Component
4119  * Bootstrap Pagination class
4120  * @cfg {String} size xs | sm | md | lg
4121  * @cfg {Boolean} inverse false | true
4122  * 
4123  * @constructor
4124  * Create a new Pagination
4125  * @param {Object} config The config object
4126  */
4127
4128 Roo.bootstrap.Pagination = function(config){
4129     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4130 };
4131
4132 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4133     
4134     cls: false,
4135     size: false,
4136     inverse: false,
4137     
4138     getAutoCreate : function(){
4139         var cfg = {
4140             tag: 'ul',
4141                 cls: 'pagination'
4142         };
4143         if (this.inverse) {
4144             cfg.cls += ' inverse';
4145         }
4146         if (this.html) {
4147             cfg.html=this.html;
4148         }
4149         if (this.cls) {
4150             cfg.cls += " " + this.cls;
4151         }
4152         return cfg;
4153     }
4154    
4155 });
4156
4157  
4158
4159  /*
4160  * - LGPL
4161  *
4162  * Pagination item
4163  * 
4164  */
4165
4166
4167 /**
4168  * @class Roo.bootstrap.PaginationItem
4169  * @extends Roo.bootstrap.Component
4170  * Bootstrap PaginationItem class
4171  * @cfg {String} html text
4172  * @cfg {String} href the link
4173  * @cfg {Boolean} preventDefault (true | false) default true
4174  * @cfg {Boolean} active (true | false) default false
4175  * @cfg {Boolean} disabled default false
4176  * 
4177  * 
4178  * @constructor
4179  * Create a new PaginationItem
4180  * @param {Object} config The config object
4181  */
4182
4183
4184 Roo.bootstrap.PaginationItem = function(config){
4185     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4186     this.addEvents({
4187         // raw events
4188         /**
4189          * @event click
4190          * The raw click event for the entire grid.
4191          * @param {Roo.EventObject} e
4192          */
4193         "click" : true
4194     });
4195 };
4196
4197 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4198     
4199     href : false,
4200     html : false,
4201     preventDefault: true,
4202     active : false,
4203     cls : false,
4204     disabled: false,
4205     
4206     getAutoCreate : function(){
4207         var cfg= {
4208             tag: 'li',
4209             cn: [
4210                 {
4211                     tag : 'a',
4212                     href : this.href ? this.href : '#',
4213                     html : this.html ? this.html : ''
4214                 }
4215             ]
4216         };
4217         
4218         if(this.cls){
4219             cfg.cls = this.cls;
4220         }
4221         
4222         if(this.disabled){
4223             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4224         }
4225         
4226         if(this.active){
4227             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4228         }
4229         
4230         return cfg;
4231     },
4232     
4233     initEvents: function() {
4234         
4235         this.el.on('click', this.onClick, this);
4236         
4237     },
4238     onClick : function(e)
4239     {
4240         Roo.log('PaginationItem on click ');
4241         if(this.preventDefault){
4242             e.preventDefault();
4243         }
4244         
4245         if(this.disabled){
4246             return;
4247         }
4248         
4249         this.fireEvent('click', this, e);
4250     }
4251    
4252 });
4253
4254  
4255
4256  /*
4257  * - LGPL
4258  *
4259  * slider
4260  * 
4261  */
4262
4263
4264 /**
4265  * @class Roo.bootstrap.Slider
4266  * @extends Roo.bootstrap.Component
4267  * Bootstrap Slider class
4268  *    
4269  * @constructor
4270  * Create a new Slider
4271  * @param {Object} config The config object
4272  */
4273
4274 Roo.bootstrap.Slider = function(config){
4275     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4276 };
4277
4278 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4279     
4280     getAutoCreate : function(){
4281         
4282         var cfg = {
4283             tag: 'div',
4284             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4285             cn: [
4286                 {
4287                     tag: 'a',
4288                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4289                 }
4290             ]
4291         }
4292         
4293         return cfg;
4294     }
4295    
4296 });
4297
4298  /*
4299  * Based on:
4300  * Ext JS Library 1.1.1
4301  * Copyright(c) 2006-2007, Ext JS, LLC.
4302  *
4303  * Originally Released Under LGPL - original licence link has changed is not relivant.
4304  *
4305  * Fork - LGPL
4306  * <script type="text/javascript">
4307  */
4308  
4309
4310 /**
4311  * @class Roo.grid.ColumnModel
4312  * @extends Roo.util.Observable
4313  * This is the default implementation of a ColumnModel used by the Grid. It defines
4314  * the columns in the grid.
4315  * <br>Usage:<br>
4316  <pre><code>
4317  var colModel = new Roo.grid.ColumnModel([
4318         {header: "Ticker", width: 60, sortable: true, locked: true},
4319         {header: "Company Name", width: 150, sortable: true},
4320         {header: "Market Cap.", width: 100, sortable: true},
4321         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4322         {header: "Employees", width: 100, sortable: true, resizable: false}
4323  ]);
4324  </code></pre>
4325  * <p>
4326  
4327  * The config options listed for this class are options which may appear in each
4328  * individual column definition.
4329  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4330  * @constructor
4331  * @param {Object} config An Array of column config objects. See this class's
4332  * config objects for details.
4333 */
4334 Roo.grid.ColumnModel = function(config){
4335         /**
4336      * The config passed into the constructor
4337      */
4338     this.config = config;
4339     this.lookup = {};
4340
4341     // if no id, create one
4342     // if the column does not have a dataIndex mapping,
4343     // map it to the order it is in the config
4344     for(var i = 0, len = config.length; i < len; i++){
4345         var c = config[i];
4346         if(typeof c.dataIndex == "undefined"){
4347             c.dataIndex = i;
4348         }
4349         if(typeof c.renderer == "string"){
4350             c.renderer = Roo.util.Format[c.renderer];
4351         }
4352         if(typeof c.id == "undefined"){
4353             c.id = Roo.id();
4354         }
4355         if(c.editor && c.editor.xtype){
4356             c.editor  = Roo.factory(c.editor, Roo.grid);
4357         }
4358         if(c.editor && c.editor.isFormField){
4359             c.editor = new Roo.grid.GridEditor(c.editor);
4360         }
4361         this.lookup[c.id] = c;
4362     }
4363
4364     /**
4365      * The width of columns which have no width specified (defaults to 100)
4366      * @type Number
4367      */
4368     this.defaultWidth = 100;
4369
4370     /**
4371      * Default sortable of columns which have no sortable specified (defaults to false)
4372      * @type Boolean
4373      */
4374     this.defaultSortable = false;
4375
4376     this.addEvents({
4377         /**
4378              * @event widthchange
4379              * Fires when the width of a column changes.
4380              * @param {ColumnModel} this
4381              * @param {Number} columnIndex The column index
4382              * @param {Number} newWidth The new width
4383              */
4384             "widthchange": true,
4385         /**
4386              * @event headerchange
4387              * Fires when the text of a header changes.
4388              * @param {ColumnModel} this
4389              * @param {Number} columnIndex The column index
4390              * @param {Number} newText The new header text
4391              */
4392             "headerchange": true,
4393         /**
4394              * @event hiddenchange
4395              * Fires when a column is hidden or "unhidden".
4396              * @param {ColumnModel} this
4397              * @param {Number} columnIndex The column index
4398              * @param {Boolean} hidden true if hidden, false otherwise
4399              */
4400             "hiddenchange": true,
4401             /**
4402          * @event columnmoved
4403          * Fires when a column is moved.
4404          * @param {ColumnModel} this
4405          * @param {Number} oldIndex
4406          * @param {Number} newIndex
4407          */
4408         "columnmoved" : true,
4409         /**
4410          * @event columlockchange
4411          * Fires when a column's locked state is changed
4412          * @param {ColumnModel} this
4413          * @param {Number} colIndex
4414          * @param {Boolean} locked true if locked
4415          */
4416         "columnlockchange" : true
4417     });
4418     Roo.grid.ColumnModel.superclass.constructor.call(this);
4419 };
4420 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4421     /**
4422      * @cfg {String} header The header text to display in the Grid view.
4423      */
4424     /**
4425      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4426      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4427      * specified, the column's index is used as an index into the Record's data Array.
4428      */
4429     /**
4430      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4431      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4432      */
4433     /**
4434      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4435      * Defaults to the value of the {@link #defaultSortable} property.
4436      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4437      */
4438     /**
4439      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4440      */
4441     /**
4442      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4443      */
4444     /**
4445      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4446      */
4447     /**
4448      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4449      */
4450     /**
4451      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4452      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4453      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4454      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4455      */
4456        /**
4457      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4458      */
4459     /**
4460      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4461      */
4462     /**
4463      * @cfg {String} cursor (Optional)
4464      */
4465     /**
4466      * @cfg {String} tooltip (Optional)
4467      */
4468     /**
4469      * Returns the id of the column at the specified index.
4470      * @param {Number} index The column index
4471      * @return {String} the id
4472      */
4473     getColumnId : function(index){
4474         return this.config[index].id;
4475     },
4476
4477     /**
4478      * Returns the column for a specified id.
4479      * @param {String} id The column id
4480      * @return {Object} the column
4481      */
4482     getColumnById : function(id){
4483         return this.lookup[id];
4484     },
4485
4486     
4487     /**
4488      * Returns the column for a specified dataIndex.
4489      * @param {String} dataIndex The column dataIndex
4490      * @return {Object|Boolean} the column or false if not found
4491      */
4492     getColumnByDataIndex: function(dataIndex){
4493         var index = this.findColumnIndex(dataIndex);
4494         return index > -1 ? this.config[index] : false;
4495     },
4496     
4497     /**
4498      * Returns the index for a specified column id.
4499      * @param {String} id The column id
4500      * @return {Number} the index, or -1 if not found
4501      */
4502     getIndexById : function(id){
4503         for(var i = 0, len = this.config.length; i < len; i++){
4504             if(this.config[i].id == id){
4505                 return i;
4506             }
4507         }
4508         return -1;
4509     },
4510     
4511     /**
4512      * Returns the index for a specified column dataIndex.
4513      * @param {String} dataIndex The column dataIndex
4514      * @return {Number} the index, or -1 if not found
4515      */
4516     
4517     findColumnIndex : function(dataIndex){
4518         for(var i = 0, len = this.config.length; i < len; i++){
4519             if(this.config[i].dataIndex == dataIndex){
4520                 return i;
4521             }
4522         }
4523         return -1;
4524     },
4525     
4526     
4527     moveColumn : function(oldIndex, newIndex){
4528         var c = this.config[oldIndex];
4529         this.config.splice(oldIndex, 1);
4530         this.config.splice(newIndex, 0, c);
4531         this.dataMap = null;
4532         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4533     },
4534
4535     isLocked : function(colIndex){
4536         return this.config[colIndex].locked === true;
4537     },
4538
4539     setLocked : function(colIndex, value, suppressEvent){
4540         if(this.isLocked(colIndex) == value){
4541             return;
4542         }
4543         this.config[colIndex].locked = value;
4544         if(!suppressEvent){
4545             this.fireEvent("columnlockchange", this, colIndex, value);
4546         }
4547     },
4548
4549     getTotalLockedWidth : function(){
4550         var totalWidth = 0;
4551         for(var i = 0; i < this.config.length; i++){
4552             if(this.isLocked(i) && !this.isHidden(i)){
4553                 this.totalWidth += this.getColumnWidth(i);
4554             }
4555         }
4556         return totalWidth;
4557     },
4558
4559     getLockedCount : function(){
4560         for(var i = 0, len = this.config.length; i < len; i++){
4561             if(!this.isLocked(i)){
4562                 return i;
4563             }
4564         }
4565     },
4566
4567     /**
4568      * Returns the number of columns.
4569      * @return {Number}
4570      */
4571     getColumnCount : function(visibleOnly){
4572         if(visibleOnly === true){
4573             var c = 0;
4574             for(var i = 0, len = this.config.length; i < len; i++){
4575                 if(!this.isHidden(i)){
4576                     c++;
4577                 }
4578             }
4579             return c;
4580         }
4581         return this.config.length;
4582     },
4583
4584     /**
4585      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4586      * @param {Function} fn
4587      * @param {Object} scope (optional)
4588      * @return {Array} result
4589      */
4590     getColumnsBy : function(fn, scope){
4591         var r = [];
4592         for(var i = 0, len = this.config.length; i < len; i++){
4593             var c = this.config[i];
4594             if(fn.call(scope||this, c, i) === true){
4595                 r[r.length] = c;
4596             }
4597         }
4598         return r;
4599     },
4600
4601     /**
4602      * Returns true if the specified column is sortable.
4603      * @param {Number} col The column index
4604      * @return {Boolean}
4605      */
4606     isSortable : function(col){
4607         if(typeof this.config[col].sortable == "undefined"){
4608             return this.defaultSortable;
4609         }
4610         return this.config[col].sortable;
4611     },
4612
4613     /**
4614      * Returns the rendering (formatting) function defined for the column.
4615      * @param {Number} col The column index.
4616      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4617      */
4618     getRenderer : function(col){
4619         if(!this.config[col].renderer){
4620             return Roo.grid.ColumnModel.defaultRenderer;
4621         }
4622         return this.config[col].renderer;
4623     },
4624
4625     /**
4626      * Sets the rendering (formatting) function for a column.
4627      * @param {Number} col The column index
4628      * @param {Function} fn The function to use to process the cell's raw data
4629      * to return HTML markup for the grid view. The render function is called with
4630      * the following parameters:<ul>
4631      * <li>Data value.</li>
4632      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4633      * <li>css A CSS style string to apply to the table cell.</li>
4634      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4635      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4636      * <li>Row index</li>
4637      * <li>Column index</li>
4638      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4639      */
4640     setRenderer : function(col, fn){
4641         this.config[col].renderer = fn;
4642     },
4643
4644     /**
4645      * Returns the width for the specified column.
4646      * @param {Number} col The column index
4647      * @return {Number}
4648      */
4649     getColumnWidth : function(col){
4650         return this.config[col].width * 1 || this.defaultWidth;
4651     },
4652
4653     /**
4654      * Sets the width for a column.
4655      * @param {Number} col The column index
4656      * @param {Number} width The new width
4657      */
4658     setColumnWidth : function(col, width, suppressEvent){
4659         this.config[col].width = width;
4660         this.totalWidth = null;
4661         if(!suppressEvent){
4662              this.fireEvent("widthchange", this, col, width);
4663         }
4664     },
4665
4666     /**
4667      * Returns the total width of all columns.
4668      * @param {Boolean} includeHidden True to include hidden column widths
4669      * @return {Number}
4670      */
4671     getTotalWidth : function(includeHidden){
4672         if(!this.totalWidth){
4673             this.totalWidth = 0;
4674             for(var i = 0, len = this.config.length; i < len; i++){
4675                 if(includeHidden || !this.isHidden(i)){
4676                     this.totalWidth += this.getColumnWidth(i);
4677                 }
4678             }
4679         }
4680         return this.totalWidth;
4681     },
4682
4683     /**
4684      * Returns the header for the specified column.
4685      * @param {Number} col The column index
4686      * @return {String}
4687      */
4688     getColumnHeader : function(col){
4689         return this.config[col].header;
4690     },
4691
4692     /**
4693      * Sets the header for a column.
4694      * @param {Number} col The column index
4695      * @param {String} header The new header
4696      */
4697     setColumnHeader : function(col, header){
4698         this.config[col].header = header;
4699         this.fireEvent("headerchange", this, col, header);
4700     },
4701
4702     /**
4703      * Returns the tooltip for the specified column.
4704      * @param {Number} col The column index
4705      * @return {String}
4706      */
4707     getColumnTooltip : function(col){
4708             return this.config[col].tooltip;
4709     },
4710     /**
4711      * Sets the tooltip for a column.
4712      * @param {Number} col The column index
4713      * @param {String} tooltip The new tooltip
4714      */
4715     setColumnTooltip : function(col, tooltip){
4716             this.config[col].tooltip = tooltip;
4717     },
4718
4719     /**
4720      * Returns the dataIndex for the specified column.
4721      * @param {Number} col The column index
4722      * @return {Number}
4723      */
4724     getDataIndex : function(col){
4725         return this.config[col].dataIndex;
4726     },
4727
4728     /**
4729      * Sets the dataIndex for a column.
4730      * @param {Number} col The column index
4731      * @param {Number} dataIndex The new dataIndex
4732      */
4733     setDataIndex : function(col, dataIndex){
4734         this.config[col].dataIndex = dataIndex;
4735     },
4736
4737     
4738     
4739     /**
4740      * Returns true if the cell is editable.
4741      * @param {Number} colIndex The column index
4742      * @param {Number} rowIndex The row index
4743      * @return {Boolean}
4744      */
4745     isCellEditable : function(colIndex, rowIndex){
4746         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4747     },
4748
4749     /**
4750      * Returns the editor defined for the cell/column.
4751      * return false or null to disable editing.
4752      * @param {Number} colIndex The column index
4753      * @param {Number} rowIndex The row index
4754      * @return {Object}
4755      */
4756     getCellEditor : function(colIndex, rowIndex){
4757         return this.config[colIndex].editor;
4758     },
4759
4760     /**
4761      * Sets if a column is editable.
4762      * @param {Number} col The column index
4763      * @param {Boolean} editable True if the column is editable
4764      */
4765     setEditable : function(col, editable){
4766         this.config[col].editable = editable;
4767     },
4768
4769
4770     /**
4771      * Returns true if the column is hidden.
4772      * @param {Number} colIndex The column index
4773      * @return {Boolean}
4774      */
4775     isHidden : function(colIndex){
4776         return this.config[colIndex].hidden;
4777     },
4778
4779
4780     /**
4781      * Returns true if the column width cannot be changed
4782      */
4783     isFixed : function(colIndex){
4784         return this.config[colIndex].fixed;
4785     },
4786
4787     /**
4788      * Returns true if the column can be resized
4789      * @return {Boolean}
4790      */
4791     isResizable : function(colIndex){
4792         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4793     },
4794     /**
4795      * Sets if a column is hidden.
4796      * @param {Number} colIndex The column index
4797      * @param {Boolean} hidden True if the column is hidden
4798      */
4799     setHidden : function(colIndex, hidden){
4800         this.config[colIndex].hidden = hidden;
4801         this.totalWidth = null;
4802         this.fireEvent("hiddenchange", this, colIndex, hidden);
4803     },
4804
4805     /**
4806      * Sets the editor for a column.
4807      * @param {Number} col The column index
4808      * @param {Object} editor The editor object
4809      */
4810     setEditor : function(col, editor){
4811         this.config[col].editor = editor;
4812     }
4813 });
4814
4815 Roo.grid.ColumnModel.defaultRenderer = function(value){
4816         if(typeof value == "string" && value.length < 1){
4817             return "&#160;";
4818         }
4819         return value;
4820 };
4821
4822 // Alias for backwards compatibility
4823 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4824 /*
4825  * Based on:
4826  * Ext JS Library 1.1.1
4827  * Copyright(c) 2006-2007, Ext JS, LLC.
4828  *
4829  * Originally Released Under LGPL - original licence link has changed is not relivant.
4830  *
4831  * Fork - LGPL
4832  * <script type="text/javascript">
4833  */
4834  
4835 /**
4836  * @class Roo.LoadMask
4837  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4838  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4839  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4840  * element's UpdateManager load indicator and will be destroyed after the initial load.
4841  * @constructor
4842  * Create a new LoadMask
4843  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4844  * @param {Object} config The config object
4845  */
4846 Roo.LoadMask = function(el, config){
4847     this.el = Roo.get(el);
4848     Roo.apply(this, config);
4849     if(this.store){
4850         this.store.on('beforeload', this.onBeforeLoad, this);
4851         this.store.on('load', this.onLoad, this);
4852         this.store.on('loadexception', this.onLoadException, this);
4853         this.removeMask = false;
4854     }else{
4855         var um = this.el.getUpdateManager();
4856         um.showLoadIndicator = false; // disable the default indicator
4857         um.on('beforeupdate', this.onBeforeLoad, this);
4858         um.on('update', this.onLoad, this);
4859         um.on('failure', this.onLoad, this);
4860         this.removeMask = true;
4861     }
4862 };
4863
4864 Roo.LoadMask.prototype = {
4865     /**
4866      * @cfg {Boolean} removeMask
4867      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4868      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4869      */
4870     /**
4871      * @cfg {String} msg
4872      * The text to display in a centered loading message box (defaults to 'Loading...')
4873      */
4874     msg : 'Loading...',
4875     /**
4876      * @cfg {String} msgCls
4877      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4878      */
4879     msgCls : 'x-mask-loading',
4880
4881     /**
4882      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4883      * @type Boolean
4884      */
4885     disabled: false,
4886
4887     /**
4888      * Disables the mask to prevent it from being displayed
4889      */
4890     disable : function(){
4891        this.disabled = true;
4892     },
4893
4894     /**
4895      * Enables the mask so that it can be displayed
4896      */
4897     enable : function(){
4898         this.disabled = false;
4899     },
4900     
4901     onLoadException : function()
4902     {
4903         Roo.log(arguments);
4904         
4905         if (typeof(arguments[3]) != 'undefined') {
4906             Roo.MessageBox.alert("Error loading",arguments[3]);
4907         } 
4908         /*
4909         try {
4910             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4911                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4912             }   
4913         } catch(e) {
4914             
4915         }
4916         */
4917     
4918         
4919         
4920         this.el.unmask(this.removeMask);
4921     },
4922     // private
4923     onLoad : function()
4924     {
4925         this.el.unmask(this.removeMask);
4926     },
4927
4928     // private
4929     onBeforeLoad : function(){
4930         if(!this.disabled){
4931             this.el.mask(this.msg, this.msgCls);
4932         }
4933     },
4934
4935     // private
4936     destroy : function(){
4937         if(this.store){
4938             this.store.un('beforeload', this.onBeforeLoad, this);
4939             this.store.un('load', this.onLoad, this);
4940             this.store.un('loadexception', this.onLoadException, this);
4941         }else{
4942             var um = this.el.getUpdateManager();
4943             um.un('beforeupdate', this.onBeforeLoad, this);
4944             um.un('update', this.onLoad, this);
4945             um.un('failure', this.onLoad, this);
4946         }
4947     }
4948 };/*
4949  * - LGPL
4950  *
4951  * table
4952  * 
4953  */
4954
4955 /**
4956  * @class Roo.bootstrap.Table
4957  * @extends Roo.bootstrap.Component
4958  * Bootstrap Table class
4959  * @cfg {String} cls table class
4960  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4961  * @cfg {String} bgcolor Specifies the background color for a table
4962  * @cfg {Number} border Specifies whether the table cells should have borders or not
4963  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4964  * @cfg {Number} cellspacing Specifies the space between cells
4965  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4966  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4967  * @cfg {String} sortable Specifies that the table should be sortable
4968  * @cfg {String} summary Specifies a summary of the content of a table
4969  * @cfg {Number} width Specifies the width of a table
4970  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4971  * 
4972  * @cfg {boolean} striped Should the rows be alternative striped
4973  * @cfg {boolean} bordered Add borders to the table
4974  * @cfg {boolean} hover Add hover highlighting
4975  * @cfg {boolean} condensed Format condensed
4976  * @cfg {boolean} responsive Format condensed
4977  * @cfg {Boolean} loadMask (true|false) default false
4978  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4979  * @cfg {Boolean} thead (true|false) generate thead, default true
4980  * @cfg {Boolean} RowSelection (true|false) default false
4981  * @cfg {Boolean} CellSelection (true|false) default false
4982  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4983  
4984  * 
4985  * @constructor
4986  * Create a new Table
4987  * @param {Object} config The config object
4988  */
4989
4990 Roo.bootstrap.Table = function(config){
4991     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4992     
4993     if (this.sm) {
4994         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4995         this.sm = this.selModel;
4996         this.sm.xmodule = this.xmodule || false;
4997     }
4998     if (this.cm && typeof(this.cm.config) == 'undefined') {
4999         this.colModel = new Roo.grid.ColumnModel(this.cm);
5000         this.cm = this.colModel;
5001         this.cm.xmodule = this.xmodule || false;
5002     }
5003     if (this.store) {
5004         this.store= Roo.factory(this.store, Roo.data);
5005         this.ds = this.store;
5006         this.ds.xmodule = this.xmodule || false;
5007          
5008     }
5009     if (this.footer && this.store) {
5010         this.footer.dataSource = this.ds;
5011         this.footer = Roo.factory(this.footer);
5012     }
5013     
5014     /** @private */
5015     this.addEvents({
5016         /**
5017          * @event cellclick
5018          * Fires when a cell is clicked
5019          * @param {Roo.bootstrap.Table} this
5020          * @param {Roo.Element} el
5021          * @param {Number} rowIndex
5022          * @param {Number} columnIndex
5023          * @param {Roo.EventObject} e
5024          */
5025         "cellclick" : true,
5026         /**
5027          * @event celldblclick
5028          * Fires when a cell is double clicked
5029          * @param {Roo.bootstrap.Table} this
5030          * @param {Roo.Element} el
5031          * @param {Number} rowIndex
5032          * @param {Number} columnIndex
5033          * @param {Roo.EventObject} e
5034          */
5035         "celldblclick" : true,
5036         /**
5037          * @event rowclick
5038          * Fires when a row is clicked
5039          * @param {Roo.bootstrap.Table} this
5040          * @param {Roo.Element} el
5041          * @param {Number} rowIndex
5042          * @param {Roo.EventObject} e
5043          */
5044         "rowclick" : true,
5045         /**
5046          * @event rowdblclick
5047          * Fires when a row is double clicked
5048          * @param {Roo.bootstrap.Table} this
5049          * @param {Roo.Element} el
5050          * @param {Number} rowIndex
5051          * @param {Roo.EventObject} e
5052          */
5053         "rowdblclick" : true,
5054         /**
5055          * @event mouseover
5056          * Fires when a mouseover occur
5057          * @param {Roo.bootstrap.Table} this
5058          * @param {Roo.Element} el
5059          * @param {Number} rowIndex
5060          * @param {Number} columnIndex
5061          * @param {Roo.EventObject} e
5062          */
5063         "mouseover" : true,
5064         /**
5065          * @event mouseout
5066          * Fires when a mouseout occur
5067          * @param {Roo.bootstrap.Table} this
5068          * @param {Roo.Element} el
5069          * @param {Number} rowIndex
5070          * @param {Number} columnIndex
5071          * @param {Roo.EventObject} e
5072          */
5073         "mouseout" : true,
5074         /**
5075          * @event rowclass
5076          * Fires when a row is rendered, so you can change add a style to it.
5077          * @param {Roo.bootstrap.Table} this
5078          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5079          */
5080         'rowclass' : true,
5081           /**
5082          * @event rowsrendered
5083          * Fires when all the  rows have been rendered
5084          * @param {Roo.bootstrap.Table} this
5085          */
5086         'rowsrendered' : true
5087         
5088     });
5089 };
5090
5091 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5092     
5093     cls: false,
5094     align: false,
5095     bgcolor: false,
5096     border: false,
5097     cellpadding: false,
5098     cellspacing: false,
5099     frame: false,
5100     rules: false,
5101     sortable: false,
5102     summary: false,
5103     width: false,
5104     striped : false,
5105     bordered: false,
5106     hover:  false,
5107     condensed : false,
5108     responsive : false,
5109     sm : false,
5110     cm : false,
5111     store : false,
5112     loadMask : false,
5113     tfoot : true,
5114     thead : true,
5115     RowSelection : false,
5116     CellSelection : false,
5117     layout : false,
5118     
5119     // Roo.Element - the tbody
5120     mainBody: false, 
5121     
5122     getAutoCreate : function(){
5123         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5124         
5125         cfg = {
5126             tag: 'table',
5127             cls : 'table',
5128             cn : []
5129         }
5130             
5131         if (this.striped) {
5132             cfg.cls += ' table-striped';
5133         }
5134         
5135         if (this.hover) {
5136             cfg.cls += ' table-hover';
5137         }
5138         if (this.bordered) {
5139             cfg.cls += ' table-bordered';
5140         }
5141         if (this.condensed) {
5142             cfg.cls += ' table-condensed';
5143         }
5144         if (this.responsive) {
5145             cfg.cls += ' table-responsive';
5146         }
5147         
5148         if (this.cls) {
5149             cfg.cls+=  ' ' +this.cls;
5150         }
5151         
5152         // this lot should be simplifed...
5153         
5154         if (this.align) {
5155             cfg.align=this.align;
5156         }
5157         if (this.bgcolor) {
5158             cfg.bgcolor=this.bgcolor;
5159         }
5160         if (this.border) {
5161             cfg.border=this.border;
5162         }
5163         if (this.cellpadding) {
5164             cfg.cellpadding=this.cellpadding;
5165         }
5166         if (this.cellspacing) {
5167             cfg.cellspacing=this.cellspacing;
5168         }
5169         if (this.frame) {
5170             cfg.frame=this.frame;
5171         }
5172         if (this.rules) {
5173             cfg.rules=this.rules;
5174         }
5175         if (this.sortable) {
5176             cfg.sortable=this.sortable;
5177         }
5178         if (this.summary) {
5179             cfg.summary=this.summary;
5180         }
5181         if (this.width) {
5182             cfg.width=this.width;
5183         }
5184         if (this.layout) {
5185             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5186         }
5187         
5188         if(this.store || this.cm){
5189             if(this.thead){
5190                 cfg.cn.push(this.renderHeader());
5191             }
5192             
5193             cfg.cn.push(this.renderBody());
5194             
5195             if(this.tfoot){
5196                 cfg.cn.push(this.renderFooter());
5197             }
5198             
5199             cfg.cls+=  ' TableGrid';
5200         }
5201         
5202         return { cn : [ cfg ] };
5203     },
5204     
5205     initEvents : function()
5206     {   
5207         if(!this.store || !this.cm){
5208             return;
5209         }
5210         
5211         //Roo.log('initEvents with ds!!!!');
5212         
5213         this.mainBody = this.el.select('tbody', true).first();
5214         
5215         
5216         var _this = this;
5217         
5218         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5219             e.on('click', _this.sort, _this);
5220         });
5221         
5222         this.el.on("click", this.onClick, this);
5223         this.el.on("dblclick", this.onDblClick, this);
5224         
5225         // why is this done????? = it breaks dialogs??
5226         //this.parent().el.setStyle('position', 'relative');
5227         
5228         
5229         if (this.footer) {
5230             this.footer.parentId = this.id;
5231             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5232         }
5233         
5234         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5235         
5236         this.store.on('load', this.onLoad, this);
5237         this.store.on('beforeload', this.onBeforeLoad, this);
5238         this.store.on('update', this.onUpdate, this);
5239         this.store.on('add', this.onAdd, this);
5240         
5241     },
5242     
5243     onMouseover : function(e, el)
5244     {
5245         var cell = Roo.get(el);
5246         
5247         if(!cell){
5248             return;
5249         }
5250         
5251         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5252             cell = cell.findParent('td', false, true);
5253         }
5254         
5255         var row = cell.findParent('tr', false, true);
5256         var cellIndex = cell.dom.cellIndex;
5257         var rowIndex = row.dom.rowIndex - 1; // start from 0
5258         
5259         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5260         
5261     },
5262     
5263     onMouseout : function(e, el)
5264     {
5265         var cell = Roo.get(el);
5266         
5267         if(!cell){
5268             return;
5269         }
5270         
5271         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5272             cell = cell.findParent('td', false, true);
5273         }
5274         
5275         var row = cell.findParent('tr', false, true);
5276         var cellIndex = cell.dom.cellIndex;
5277         var rowIndex = row.dom.rowIndex - 1; // start from 0
5278         
5279         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5280         
5281     },
5282     
5283     onClick : function(e, el)
5284     {
5285         var cell = Roo.get(el);
5286         
5287         if(!cell || (!this.CellSelection && !this.RowSelection)){
5288             return;
5289         }
5290         
5291         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5292             cell = cell.findParent('td', false, true);
5293         }
5294         
5295         if(!cell || typeof(cell) == 'undefined'){
5296             return;
5297         }
5298         
5299         var row = cell.findParent('tr', false, true);
5300         
5301         if(!row || typeof(row) == 'undefined'){
5302             return;
5303         }
5304         
5305         var cellIndex = cell.dom.cellIndex;
5306         var rowIndex = this.getRowIndex(row);
5307         
5308         if(this.CellSelection){
5309             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5310         }
5311         
5312         if(this.RowSelection){
5313             this.fireEvent('rowclick', this, row, rowIndex, e);
5314         }
5315         
5316         
5317     },
5318     
5319     onDblClick : function(e,el)
5320     {
5321         var cell = Roo.get(el);
5322         
5323         if(!cell || (!this.CellSelection && !this.RowSelection)){
5324             return;
5325         }
5326         
5327         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5328             cell = cell.findParent('td', false, true);
5329         }
5330         
5331         if(!cell || typeof(cell) == 'undefined'){
5332             return;
5333         }
5334         
5335         var row = cell.findParent('tr', false, true);
5336         
5337         if(!row || typeof(row) == 'undefined'){
5338             return;
5339         }
5340         
5341         var cellIndex = cell.dom.cellIndex;
5342         var rowIndex = this.getRowIndex(row);
5343         
5344         if(this.CellSelection){
5345             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5346         }
5347         
5348         if(this.RowSelection){
5349             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5350         }
5351     },
5352     
5353     sort : function(e,el)
5354     {
5355         var col = Roo.get(el);
5356         
5357         if(!col.hasClass('sortable')){
5358             return;
5359         }
5360         
5361         var sort = col.attr('sort');
5362         var dir = 'ASC';
5363         
5364         if(col.hasClass('glyphicon-arrow-up')){
5365             dir = 'DESC';
5366         }
5367         
5368         this.store.sortInfo = {field : sort, direction : dir};
5369         
5370         if (this.footer) {
5371             Roo.log("calling footer first");
5372             this.footer.onClick('first');
5373         } else {
5374         
5375             this.store.load({ params : { start : 0 } });
5376         }
5377     },
5378     
5379     renderHeader : function()
5380     {
5381         var header = {
5382             tag: 'thead',
5383             cn : []
5384         };
5385         
5386         var cm = this.cm;
5387         
5388         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5389             
5390             var config = cm.config[i];
5391                     
5392             var c = {
5393                 tag: 'th',
5394                 style : '',
5395                 html: cm.getColumnHeader(i)
5396             };
5397             
5398             if(typeof(config.tooltip) != 'undefined'){
5399                 c.tooltip = config.tooltip;
5400             }
5401             
5402             if(typeof(config.hidden) != 'undefined' && config.hidden){
5403                 c.style += ' display:none;';
5404             }
5405             
5406             if(typeof(config.dataIndex) != 'undefined'){
5407                 c.sort = config.dataIndex;
5408             }
5409             
5410             if(typeof(config.sortable) != 'undefined' && config.sortable){
5411                 c.cls = 'sortable';
5412             }
5413             
5414             if(typeof(config.align) != 'undefined' && config.align.length){
5415                 c.style += ' text-align:' + config.align + ';';
5416             }
5417             
5418             if(typeof(config.width) != 'undefined'){
5419                 c.style += ' width:' + config.width + 'px;';
5420             }
5421             
5422             header.cn.push(c)
5423         }
5424         
5425         return header;
5426     },
5427     
5428     renderBody : function()
5429     {
5430         var body = {
5431             tag: 'tbody',
5432             cn : [
5433                 {
5434                     tag: 'tr',
5435                     cn : [
5436                         {
5437                             tag : 'td',
5438                             colspan :  this.cm.getColumnCount()
5439                         }
5440                     ]
5441                 }
5442             ]
5443         };
5444         
5445         return body;
5446     },
5447     
5448     renderFooter : function()
5449     {
5450         var footer = {
5451             tag: 'tfoot',
5452             cn : [
5453                 {
5454                     tag: 'tr',
5455                     cn : [
5456                         {
5457                             tag : 'td',
5458                             colspan :  this.cm.getColumnCount()
5459                         }
5460                     ]
5461                 }
5462             ]
5463         };
5464         
5465         return footer;
5466     },
5467     
5468     
5469     
5470     onLoad : function()
5471     {
5472         Roo.log('ds onload');
5473         this.clear();
5474         
5475         var _this = this;
5476         var cm = this.cm;
5477         var ds = this.store;
5478         
5479         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5480             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5481             
5482             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5483                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5484             }
5485             
5486             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5487                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5488             }
5489         });
5490         
5491         var tbody =  this.mainBody;
5492               
5493         if(ds.getCount() > 0){
5494             ds.data.each(function(d,rowIndex){
5495                 var row =  this.renderRow(cm, ds, rowIndex);
5496                 
5497                 tbody.createChild(row);
5498                 
5499                 var _this = this;
5500                 
5501                 if(row.cellObjects.length){
5502                     Roo.each(row.cellObjects, function(r){
5503                         _this.renderCellObject(r);
5504                     })
5505                 }
5506                 
5507             }, this);
5508         }
5509         
5510         Roo.each(this.el.select('tbody td', true).elements, function(e){
5511             e.on('mouseover', _this.onMouseover, _this);
5512         });
5513         
5514         Roo.each(this.el.select('tbody td', true).elements, function(e){
5515             e.on('mouseout', _this.onMouseout, _this);
5516         });
5517         this.fireEvent('rowsrendered', this);
5518         //if(this.loadMask){
5519         //    this.maskEl.hide();
5520         //}
5521     },
5522     
5523     
5524     onUpdate : function(ds,record)
5525     {
5526         this.refreshRow(record);
5527     },
5528     
5529     onRemove : function(ds, record, index, isUpdate){
5530         if(isUpdate !== true){
5531             this.fireEvent("beforerowremoved", this, index, record);
5532         }
5533         var bt = this.mainBody.dom;
5534         
5535         var rows = this.el.select('tbody > tr', true).elements;
5536         
5537         if(typeof(rows[index]) != 'undefined'){
5538             bt.removeChild(rows[index].dom);
5539         }
5540         
5541 //        if(bt.rows[index]){
5542 //            bt.removeChild(bt.rows[index]);
5543 //        }
5544         
5545         if(isUpdate !== true){
5546             //this.stripeRows(index);
5547             //this.syncRowHeights(index, index);
5548             //this.layout();
5549             this.fireEvent("rowremoved", this, index, record);
5550         }
5551     },
5552     
5553     onAdd : function(ds, records, rowIndex)
5554     {
5555         //Roo.log('on Add called');
5556         // - note this does not handle multiple adding very well..
5557         var bt = this.mainBody.dom;
5558         for (var i =0 ; i < records.length;i++) {
5559             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5560             //Roo.log(records[i]);
5561             //Roo.log(this.store.getAt(rowIndex+i));
5562             this.insertRow(this.store, rowIndex + i, false);
5563             return;
5564         }
5565         
5566     },
5567     
5568     
5569     refreshRow : function(record){
5570         var ds = this.store, index;
5571         if(typeof record == 'number'){
5572             index = record;
5573             record = ds.getAt(index);
5574         }else{
5575             index = ds.indexOf(record);
5576         }
5577         this.insertRow(ds, index, true);
5578         this.onRemove(ds, record, index+1, true);
5579         //this.syncRowHeights(index, index);
5580         //this.layout();
5581         this.fireEvent("rowupdated", this, index, record);
5582     },
5583     
5584     insertRow : function(dm, rowIndex, isUpdate){
5585         
5586         if(!isUpdate){
5587             this.fireEvent("beforerowsinserted", this, rowIndex);
5588         }
5589             //var s = this.getScrollState();
5590         var row = this.renderRow(this.cm, this.store, rowIndex);
5591         // insert before rowIndex..
5592         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5593         
5594         var _this = this;
5595                 
5596         if(row.cellObjects.length){
5597             Roo.each(row.cellObjects, function(r){
5598                 _this.renderCellObject(r);
5599             })
5600         }
5601             
5602         if(!isUpdate){
5603             this.fireEvent("rowsinserted", this, rowIndex);
5604             //this.syncRowHeights(firstRow, lastRow);
5605             //this.stripeRows(firstRow);
5606             //this.layout();
5607         }
5608         
5609     },
5610     
5611     
5612     getRowDom : function(rowIndex)
5613     {
5614         var rows = this.el.select('tbody > tr', true).elements;
5615         
5616         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5617         
5618     },
5619     // returns the object tree for a tr..
5620   
5621     
5622     renderRow : function(cm, ds, rowIndex) 
5623     {
5624         
5625         var d = ds.getAt(rowIndex);
5626         
5627         var row = {
5628             tag : 'tr',
5629             cn : []
5630         };
5631             
5632         var cellObjects = [];
5633         
5634         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5635             var config = cm.config[i];
5636             
5637             var renderer = cm.getRenderer(i);
5638             var value = '';
5639             var id = false;
5640             
5641             if(typeof(renderer) !== 'undefined'){
5642                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5643             }
5644             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5645             // and are rendered into the cells after the row is rendered - using the id for the element.
5646             
5647             if(typeof(value) === 'object'){
5648                 id = Roo.id();
5649                 cellObjects.push({
5650                     container : id,
5651                     cfg : value 
5652                 })
5653             }
5654             
5655             var rowcfg = {
5656                 record: d,
5657                 rowIndex : rowIndex,
5658                 colIndex : i,
5659                 rowClass : ''
5660             }
5661
5662             this.fireEvent('rowclass', this, rowcfg);
5663             
5664             var td = {
5665                 tag: 'td',
5666                 cls : rowcfg.rowClass,
5667                 style: '',
5668                 html: (typeof(value) === 'object') ? '' : value
5669             };
5670             
5671             if (id) {
5672                 td.id = id;
5673             }
5674             
5675             if(typeof(config.hidden) != 'undefined' && config.hidden){
5676                 td.style += ' display:none;';
5677             }
5678             
5679             if(typeof(config.align) != 'undefined' && config.align.length){
5680                 td.style += ' text-align:' + config.align + ';';
5681             }
5682             
5683             if(typeof(config.width) != 'undefined'){
5684                 td.style += ' width:' +  config.width + 'px;';
5685             }
5686             
5687             if(typeof(config.cursor) != 'undefined'){
5688                 td.style += ' cursor:' +  config.cursor + ';';
5689             }
5690              
5691             row.cn.push(td);
5692            
5693         }
5694         
5695         row.cellObjects = cellObjects;
5696         
5697         return row;
5698           
5699     },
5700     
5701     
5702     
5703     onBeforeLoad : function()
5704     {
5705         //Roo.log('ds onBeforeLoad');
5706         
5707         //this.clear();
5708         
5709         //if(this.loadMask){
5710         //    this.maskEl.show();
5711         //}
5712     },
5713      /**
5714      * Remove all rows
5715      */
5716     clear : function()
5717     {
5718         this.el.select('tbody', true).first().dom.innerHTML = '';
5719     },
5720     /**
5721      * Show or hide a row.
5722      * @param {Number} rowIndex to show or hide
5723      * @param {Boolean} state hide
5724      */
5725     setRowVisibility : function(rowIndex, state)
5726     {
5727         var bt = this.mainBody.dom;
5728         
5729         var rows = this.el.select('tbody > tr', true).elements;
5730         
5731         if(typeof(rows[rowIndex]) == 'undefined'){
5732             return;
5733         }
5734         rows[rowIndex].dom.style.display = state ? '' : 'none';
5735     },
5736     
5737     
5738     getSelectionModel : function(){
5739         if(!this.selModel){
5740             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5741         }
5742         return this.selModel;
5743     },
5744     /*
5745      * Render the Roo.bootstrap object from renderder
5746      */
5747     renderCellObject : function(r)
5748     {
5749         var _this = this;
5750         
5751         var t = r.cfg.render(r.container);
5752         
5753         if(r.cfg.cn){
5754             Roo.each(r.cfg.cn, function(c){
5755                 var child = {
5756                     container: t.getChildContainer(),
5757                     cfg: c
5758                 }
5759                 _this.renderCellObject(child);
5760             })
5761         }
5762     },
5763     
5764     getRowIndex : function(row)
5765     {
5766         var rowIndex = -1;
5767         
5768         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
5769             if(el != row){
5770                 return;
5771             }
5772             
5773             rowIndex = index;
5774         });
5775         
5776         return rowIndex;
5777     }
5778    
5779 });
5780
5781  
5782
5783  /*
5784  * - LGPL
5785  *
5786  * table cell
5787  * 
5788  */
5789
5790 /**
5791  * @class Roo.bootstrap.TableCell
5792  * @extends Roo.bootstrap.Component
5793  * Bootstrap TableCell class
5794  * @cfg {String} html cell contain text
5795  * @cfg {String} cls cell class
5796  * @cfg {String} tag cell tag (td|th) default td
5797  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5798  * @cfg {String} align Aligns the content in a cell
5799  * @cfg {String} axis Categorizes cells
5800  * @cfg {String} bgcolor Specifies the background color of a cell
5801  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5802  * @cfg {Number} colspan Specifies the number of columns a cell should span
5803  * @cfg {String} headers Specifies one or more header cells a cell is related to
5804  * @cfg {Number} height Sets the height of a cell
5805  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5806  * @cfg {Number} rowspan Sets the number of rows a cell should span
5807  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5808  * @cfg {String} valign Vertical aligns the content in a cell
5809  * @cfg {Number} width Specifies the width of a cell
5810  * 
5811  * @constructor
5812  * Create a new TableCell
5813  * @param {Object} config The config object
5814  */
5815
5816 Roo.bootstrap.TableCell = function(config){
5817     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5818 };
5819
5820 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5821     
5822     html: false,
5823     cls: false,
5824     tag: false,
5825     abbr: false,
5826     align: false,
5827     axis: false,
5828     bgcolor: false,
5829     charoff: false,
5830     colspan: false,
5831     headers: false,
5832     height: false,
5833     nowrap: false,
5834     rowspan: false,
5835     scope: false,
5836     valign: false,
5837     width: false,
5838     
5839     
5840     getAutoCreate : function(){
5841         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5842         
5843         cfg = {
5844             tag: 'td'
5845         }
5846         
5847         if(this.tag){
5848             cfg.tag = this.tag;
5849         }
5850         
5851         if (this.html) {
5852             cfg.html=this.html
5853         }
5854         if (this.cls) {
5855             cfg.cls=this.cls
5856         }
5857         if (this.abbr) {
5858             cfg.abbr=this.abbr
5859         }
5860         if (this.align) {
5861             cfg.align=this.align
5862         }
5863         if (this.axis) {
5864             cfg.axis=this.axis
5865         }
5866         if (this.bgcolor) {
5867             cfg.bgcolor=this.bgcolor
5868         }
5869         if (this.charoff) {
5870             cfg.charoff=this.charoff
5871         }
5872         if (this.colspan) {
5873             cfg.colspan=this.colspan
5874         }
5875         if (this.headers) {
5876             cfg.headers=this.headers
5877         }
5878         if (this.height) {
5879             cfg.height=this.height
5880         }
5881         if (this.nowrap) {
5882             cfg.nowrap=this.nowrap
5883         }
5884         if (this.rowspan) {
5885             cfg.rowspan=this.rowspan
5886         }
5887         if (this.scope) {
5888             cfg.scope=this.scope
5889         }
5890         if (this.valign) {
5891             cfg.valign=this.valign
5892         }
5893         if (this.width) {
5894             cfg.width=this.width
5895         }
5896         
5897         
5898         return cfg;
5899     }
5900    
5901 });
5902
5903  
5904
5905  /*
5906  * - LGPL
5907  *
5908  * table row
5909  * 
5910  */
5911
5912 /**
5913  * @class Roo.bootstrap.TableRow
5914  * @extends Roo.bootstrap.Component
5915  * Bootstrap TableRow class
5916  * @cfg {String} cls row class
5917  * @cfg {String} align Aligns the content in a table row
5918  * @cfg {String} bgcolor Specifies a background color for a table row
5919  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5920  * @cfg {String} valign Vertical aligns the content in a table row
5921  * 
5922  * @constructor
5923  * Create a new TableRow
5924  * @param {Object} config The config object
5925  */
5926
5927 Roo.bootstrap.TableRow = function(config){
5928     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5929 };
5930
5931 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5932     
5933     cls: false,
5934     align: false,
5935     bgcolor: false,
5936     charoff: false,
5937     valign: false,
5938     
5939     getAutoCreate : function(){
5940         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5941         
5942         cfg = {
5943             tag: 'tr'
5944         }
5945             
5946         if(this.cls){
5947             cfg.cls = this.cls;
5948         }
5949         if(this.align){
5950             cfg.align = this.align;
5951         }
5952         if(this.bgcolor){
5953             cfg.bgcolor = this.bgcolor;
5954         }
5955         if(this.charoff){
5956             cfg.charoff = this.charoff;
5957         }
5958         if(this.valign){
5959             cfg.valign = this.valign;
5960         }
5961         
5962         return cfg;
5963     }
5964    
5965 });
5966
5967  
5968
5969  /*
5970  * - LGPL
5971  *
5972  * table body
5973  * 
5974  */
5975
5976 /**
5977  * @class Roo.bootstrap.TableBody
5978  * @extends Roo.bootstrap.Component
5979  * Bootstrap TableBody class
5980  * @cfg {String} cls element class
5981  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5982  * @cfg {String} align Aligns the content inside the element
5983  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5984  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5985  * 
5986  * @constructor
5987  * Create a new TableBody
5988  * @param {Object} config The config object
5989  */
5990
5991 Roo.bootstrap.TableBody = function(config){
5992     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5993 };
5994
5995 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5996     
5997     cls: false,
5998     tag: false,
5999     align: false,
6000     charoff: false,
6001     valign: false,
6002     
6003     getAutoCreate : function(){
6004         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6005         
6006         cfg = {
6007             tag: 'tbody'
6008         }
6009             
6010         if (this.cls) {
6011             cfg.cls=this.cls
6012         }
6013         if(this.tag){
6014             cfg.tag = this.tag;
6015         }
6016         
6017         if(this.align){
6018             cfg.align = this.align;
6019         }
6020         if(this.charoff){
6021             cfg.charoff = this.charoff;
6022         }
6023         if(this.valign){
6024             cfg.valign = this.valign;
6025         }
6026         
6027         return cfg;
6028     }
6029     
6030     
6031 //    initEvents : function()
6032 //    {
6033 //        
6034 //        if(!this.store){
6035 //            return;
6036 //        }
6037 //        
6038 //        this.store = Roo.factory(this.store, Roo.data);
6039 //        this.store.on('load', this.onLoad, this);
6040 //        
6041 //        this.store.load();
6042 //        
6043 //    },
6044 //    
6045 //    onLoad: function () 
6046 //    {   
6047 //        this.fireEvent('load', this);
6048 //    }
6049 //    
6050 //   
6051 });
6052
6053  
6054
6055  /*
6056  * Based on:
6057  * Ext JS Library 1.1.1
6058  * Copyright(c) 2006-2007, Ext JS, LLC.
6059  *
6060  * Originally Released Under LGPL - original licence link has changed is not relivant.
6061  *
6062  * Fork - LGPL
6063  * <script type="text/javascript">
6064  */
6065
6066 // as we use this in bootstrap.
6067 Roo.namespace('Roo.form');
6068  /**
6069  * @class Roo.form.Action
6070  * Internal Class used to handle form actions
6071  * @constructor
6072  * @param {Roo.form.BasicForm} el The form element or its id
6073  * @param {Object} config Configuration options
6074  */
6075
6076  
6077  
6078 // define the action interface
6079 Roo.form.Action = function(form, options){
6080     this.form = form;
6081     this.options = options || {};
6082 };
6083 /**
6084  * Client Validation Failed
6085  * @const 
6086  */
6087 Roo.form.Action.CLIENT_INVALID = 'client';
6088 /**
6089  * Server Validation Failed
6090  * @const 
6091  */
6092 Roo.form.Action.SERVER_INVALID = 'server';
6093  /**
6094  * Connect to Server Failed
6095  * @const 
6096  */
6097 Roo.form.Action.CONNECT_FAILURE = 'connect';
6098 /**
6099  * Reading Data from Server Failed
6100  * @const 
6101  */
6102 Roo.form.Action.LOAD_FAILURE = 'load';
6103
6104 Roo.form.Action.prototype = {
6105     type : 'default',
6106     failureType : undefined,
6107     response : undefined,
6108     result : undefined,
6109
6110     // interface method
6111     run : function(options){
6112
6113     },
6114
6115     // interface method
6116     success : function(response){
6117
6118     },
6119
6120     // interface method
6121     handleResponse : function(response){
6122
6123     },
6124
6125     // default connection failure
6126     failure : function(response){
6127         
6128         this.response = response;
6129         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6130         this.form.afterAction(this, false);
6131     },
6132
6133     processResponse : function(response){
6134         this.response = response;
6135         if(!response.responseText){
6136             return true;
6137         }
6138         this.result = this.handleResponse(response);
6139         return this.result;
6140     },
6141
6142     // utility functions used internally
6143     getUrl : function(appendParams){
6144         var url = this.options.url || this.form.url || this.form.el.dom.action;
6145         if(appendParams){
6146             var p = this.getParams();
6147             if(p){
6148                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6149             }
6150         }
6151         return url;
6152     },
6153
6154     getMethod : function(){
6155         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6156     },
6157
6158     getParams : function(){
6159         var bp = this.form.baseParams;
6160         var p = this.options.params;
6161         if(p){
6162             if(typeof p == "object"){
6163                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6164             }else if(typeof p == 'string' && bp){
6165                 p += '&' + Roo.urlEncode(bp);
6166             }
6167         }else if(bp){
6168             p = Roo.urlEncode(bp);
6169         }
6170         return p;
6171     },
6172
6173     createCallback : function(){
6174         return {
6175             success: this.success,
6176             failure: this.failure,
6177             scope: this,
6178             timeout: (this.form.timeout*1000),
6179             upload: this.form.fileUpload ? this.success : undefined
6180         };
6181     }
6182 };
6183
6184 Roo.form.Action.Submit = function(form, options){
6185     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6186 };
6187
6188 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6189     type : 'submit',
6190
6191     haveProgress : false,
6192     uploadComplete : false,
6193     
6194     // uploadProgress indicator.
6195     uploadProgress : function()
6196     {
6197         if (!this.form.progressUrl) {
6198             return;
6199         }
6200         
6201         if (!this.haveProgress) {
6202             Roo.MessageBox.progress("Uploading", "Uploading");
6203         }
6204         if (this.uploadComplete) {
6205            Roo.MessageBox.hide();
6206            return;
6207         }
6208         
6209         this.haveProgress = true;
6210    
6211         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6212         
6213         var c = new Roo.data.Connection();
6214         c.request({
6215             url : this.form.progressUrl,
6216             params: {
6217                 id : uid
6218             },
6219             method: 'GET',
6220             success : function(req){
6221                //console.log(data);
6222                 var rdata = false;
6223                 var edata;
6224                 try  {
6225                    rdata = Roo.decode(req.responseText)
6226                 } catch (e) {
6227                     Roo.log("Invalid data from server..");
6228                     Roo.log(edata);
6229                     return;
6230                 }
6231                 if (!rdata || !rdata.success) {
6232                     Roo.log(rdata);
6233                     Roo.MessageBox.alert(Roo.encode(rdata));
6234                     return;
6235                 }
6236                 var data = rdata.data;
6237                 
6238                 if (this.uploadComplete) {
6239                    Roo.MessageBox.hide();
6240                    return;
6241                 }
6242                    
6243                 if (data){
6244                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6245                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6246                     );
6247                 }
6248                 this.uploadProgress.defer(2000,this);
6249             },
6250        
6251             failure: function(data) {
6252                 Roo.log('progress url failed ');
6253                 Roo.log(data);
6254             },
6255             scope : this
6256         });
6257            
6258     },
6259     
6260     
6261     run : function()
6262     {
6263         // run get Values on the form, so it syncs any secondary forms.
6264         this.form.getValues();
6265         
6266         var o = this.options;
6267         var method = this.getMethod();
6268         var isPost = method == 'POST';
6269         if(o.clientValidation === false || this.form.isValid()){
6270             
6271             if (this.form.progressUrl) {
6272                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6273                     (new Date() * 1) + '' + Math.random());
6274                     
6275             } 
6276             
6277             
6278             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6279                 form:this.form.el.dom,
6280                 url:this.getUrl(!isPost),
6281                 method: method,
6282                 params:isPost ? this.getParams() : null,
6283                 isUpload: this.form.fileUpload
6284             }));
6285             
6286             this.uploadProgress();
6287
6288         }else if (o.clientValidation !== false){ // client validation failed
6289             this.failureType = Roo.form.Action.CLIENT_INVALID;
6290             this.form.afterAction(this, false);
6291         }
6292     },
6293
6294     success : function(response)
6295     {
6296         this.uploadComplete= true;
6297         if (this.haveProgress) {
6298             Roo.MessageBox.hide();
6299         }
6300         
6301         
6302         var result = this.processResponse(response);
6303         if(result === true || result.success){
6304             this.form.afterAction(this, true);
6305             return;
6306         }
6307         if(result.errors){
6308             this.form.markInvalid(result.errors);
6309             this.failureType = Roo.form.Action.SERVER_INVALID;
6310         }
6311         this.form.afterAction(this, false);
6312     },
6313     failure : function(response)
6314     {
6315         this.uploadComplete= true;
6316         if (this.haveProgress) {
6317             Roo.MessageBox.hide();
6318         }
6319         
6320         this.response = response;
6321         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6322         this.form.afterAction(this, false);
6323     },
6324     
6325     handleResponse : function(response){
6326         if(this.form.errorReader){
6327             var rs = this.form.errorReader.read(response);
6328             var errors = [];
6329             if(rs.records){
6330                 for(var i = 0, len = rs.records.length; i < len; i++) {
6331                     var r = rs.records[i];
6332                     errors[i] = r.data;
6333                 }
6334             }
6335             if(errors.length < 1){
6336                 errors = null;
6337             }
6338             return {
6339                 success : rs.success,
6340                 errors : errors
6341             };
6342         }
6343         var ret = false;
6344         try {
6345             ret = Roo.decode(response.responseText);
6346         } catch (e) {
6347             ret = {
6348                 success: false,
6349                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6350                 errors : []
6351             };
6352         }
6353         return ret;
6354         
6355     }
6356 });
6357
6358
6359 Roo.form.Action.Load = function(form, options){
6360     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6361     this.reader = this.form.reader;
6362 };
6363
6364 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6365     type : 'load',
6366
6367     run : function(){
6368         
6369         Roo.Ajax.request(Roo.apply(
6370                 this.createCallback(), {
6371                     method:this.getMethod(),
6372                     url:this.getUrl(false),
6373                     params:this.getParams()
6374         }));
6375     },
6376
6377     success : function(response){
6378         
6379         var result = this.processResponse(response);
6380         if(result === true || !result.success || !result.data){
6381             this.failureType = Roo.form.Action.LOAD_FAILURE;
6382             this.form.afterAction(this, false);
6383             return;
6384         }
6385         this.form.clearInvalid();
6386         this.form.setValues(result.data);
6387         this.form.afterAction(this, true);
6388     },
6389
6390     handleResponse : function(response){
6391         if(this.form.reader){
6392             var rs = this.form.reader.read(response);
6393             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6394             return {
6395                 success : rs.success,
6396                 data : data
6397             };
6398         }
6399         return Roo.decode(response.responseText);
6400     }
6401 });
6402
6403 Roo.form.Action.ACTION_TYPES = {
6404     'load' : Roo.form.Action.Load,
6405     'submit' : Roo.form.Action.Submit
6406 };/*
6407  * - LGPL
6408  *
6409  * form
6410  * 
6411  */
6412
6413 /**
6414  * @class Roo.bootstrap.Form
6415  * @extends Roo.bootstrap.Component
6416  * Bootstrap Form class
6417  * @cfg {String} method  GET | POST (default POST)
6418  * @cfg {String} labelAlign top | left (default top)
6419  * @cfg {String} align left  | right - for navbars
6420  * @cfg {Boolean} loadMask load mask when submit (default true)
6421
6422  * 
6423  * @constructor
6424  * Create a new Form
6425  * @param {Object} config The config object
6426  */
6427
6428
6429 Roo.bootstrap.Form = function(config){
6430     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6431     this.addEvents({
6432         /**
6433          * @event clientvalidation
6434          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6435          * @param {Form} this
6436          * @param {Boolean} valid true if the form has passed client-side validation
6437          */
6438         clientvalidation: true,
6439         /**
6440          * @event beforeaction
6441          * Fires before any action is performed. Return false to cancel the action.
6442          * @param {Form} this
6443          * @param {Action} action The action to be performed
6444          */
6445         beforeaction: true,
6446         /**
6447          * @event actionfailed
6448          * Fires when an action fails.
6449          * @param {Form} this
6450          * @param {Action} action The action that failed
6451          */
6452         actionfailed : true,
6453         /**
6454          * @event actioncomplete
6455          * Fires when an action is completed.
6456          * @param {Form} this
6457          * @param {Action} action The action that completed
6458          */
6459         actioncomplete : true
6460     });
6461     
6462 };
6463
6464 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6465       
6466      /**
6467      * @cfg {String} method
6468      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6469      */
6470     method : 'POST',
6471     /**
6472      * @cfg {String} url
6473      * The URL to use for form actions if one isn't supplied in the action options.
6474      */
6475     /**
6476      * @cfg {Boolean} fileUpload
6477      * Set to true if this form is a file upload.
6478      */
6479      
6480     /**
6481      * @cfg {Object} baseParams
6482      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6483      */
6484       
6485     /**
6486      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6487      */
6488     timeout: 30,
6489     /**
6490      * @cfg {Sting} align (left|right) for navbar forms
6491      */
6492     align : 'left',
6493
6494     // private
6495     activeAction : null,
6496  
6497     /**
6498      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6499      * element by passing it or its id or mask the form itself by passing in true.
6500      * @type Mixed
6501      */
6502     waitMsgTarget : false,
6503     
6504     loadMask : true,
6505     
6506     getAutoCreate : function(){
6507         
6508         var cfg = {
6509             tag: 'form',
6510             method : this.method || 'POST',
6511             id : this.id || Roo.id(),
6512             cls : ''
6513         }
6514         if (this.parent().xtype.match(/^Nav/)) {
6515             cfg.cls = 'navbar-form navbar-' + this.align;
6516             
6517         }
6518         
6519         if (this.labelAlign == 'left' ) {
6520             cfg.cls += ' form-horizontal';
6521         }
6522         
6523         
6524         return cfg;
6525     },
6526     initEvents : function()
6527     {
6528         this.el.on('submit', this.onSubmit, this);
6529         // this was added as random key presses on the form where triggering form submit.
6530         this.el.on('keypress', function(e) {
6531             if (e.getCharCode() != 13) {
6532                 return true;
6533             }
6534             // we might need to allow it for textareas.. and some other items.
6535             // check e.getTarget().
6536             
6537             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6538                 return true;
6539             }
6540         
6541             Roo.log("keypress blocked");
6542             
6543             e.preventDefault();
6544             return false;
6545         });
6546         
6547     },
6548     // private
6549     onSubmit : function(e){
6550         e.stopEvent();
6551     },
6552     
6553      /**
6554      * Returns true if client-side validation on the form is successful.
6555      * @return Boolean
6556      */
6557     isValid : function(){
6558         var items = this.getItems();
6559         var valid = true;
6560         items.each(function(f){
6561            if(!f.validate()){
6562                valid = false;
6563                
6564            }
6565         });
6566         return valid;
6567     },
6568     /**
6569      * Returns true if any fields in this form have changed since their original load.
6570      * @return Boolean
6571      */
6572     isDirty : function(){
6573         var dirty = false;
6574         var items = this.getItems();
6575         items.each(function(f){
6576            if(f.isDirty()){
6577                dirty = true;
6578                return false;
6579            }
6580            return true;
6581         });
6582         return dirty;
6583     },
6584      /**
6585      * Performs a predefined action (submit or load) or custom actions you define on this form.
6586      * @param {String} actionName The name of the action type
6587      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6588      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6589      * accept other config options):
6590      * <pre>
6591 Property          Type             Description
6592 ----------------  ---------------  ----------------------------------------------------------------------------------
6593 url               String           The url for the action (defaults to the form's url)
6594 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6595 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6596 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6597                                    validate the form on the client (defaults to false)
6598      * </pre>
6599      * @return {BasicForm} this
6600      */
6601     doAction : function(action, options){
6602         if(typeof action == 'string'){
6603             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6604         }
6605         if(this.fireEvent('beforeaction', this, action) !== false){
6606             this.beforeAction(action);
6607             action.run.defer(100, action);
6608         }
6609         return this;
6610     },
6611     
6612     // private
6613     beforeAction : function(action){
6614         var o = action.options;
6615         
6616         if(this.loadMask){
6617             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6618         }
6619         // not really supported yet.. ??
6620         
6621         //if(this.waitMsgTarget === true){
6622         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6623         //}else if(this.waitMsgTarget){
6624         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6625         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6626         //}else {
6627         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6628        // }
6629          
6630     },
6631
6632     // private
6633     afterAction : function(action, success){
6634         this.activeAction = null;
6635         var o = action.options;
6636         
6637         //if(this.waitMsgTarget === true){
6638             this.el.unmask();
6639         //}else if(this.waitMsgTarget){
6640         //    this.waitMsgTarget.unmask();
6641         //}else{
6642         //    Roo.MessageBox.updateProgress(1);
6643         //    Roo.MessageBox.hide();
6644        // }
6645         // 
6646         if(success){
6647             if(o.reset){
6648                 this.reset();
6649             }
6650             Roo.callback(o.success, o.scope, [this, action]);
6651             this.fireEvent('actioncomplete', this, action);
6652             
6653         }else{
6654             
6655             // failure condition..
6656             // we have a scenario where updates need confirming.
6657             // eg. if a locking scenario exists..
6658             // we look for { errors : { needs_confirm : true }} in the response.
6659             if (
6660                 (typeof(action.result) != 'undefined')  &&
6661                 (typeof(action.result.errors) != 'undefined')  &&
6662                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6663            ){
6664                 var _t = this;
6665                 Roo.log("not supported yet");
6666                  /*
6667                 
6668                 Roo.MessageBox.confirm(
6669                     "Change requires confirmation",
6670                     action.result.errorMsg,
6671                     function(r) {
6672                         if (r != 'yes') {
6673                             return;
6674                         }
6675                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6676                     }
6677                     
6678                 );
6679                 */
6680                 
6681                 
6682                 return;
6683             }
6684             
6685             Roo.callback(o.failure, o.scope, [this, action]);
6686             // show an error message if no failed handler is set..
6687             if (!this.hasListener('actionfailed')) {
6688                 Roo.log("need to add dialog support");
6689                 /*
6690                 Roo.MessageBox.alert("Error",
6691                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6692                         action.result.errorMsg :
6693                         "Saving Failed, please check your entries or try again"
6694                 );
6695                 */
6696             }
6697             
6698             this.fireEvent('actionfailed', this, action);
6699         }
6700         
6701     },
6702     /**
6703      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6704      * @param {String} id The value to search for
6705      * @return Field
6706      */
6707     findField : function(id){
6708         var items = this.getItems();
6709         var field = items.get(id);
6710         if(!field){
6711              items.each(function(f){
6712                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6713                     field = f;
6714                     return false;
6715                 }
6716                 return true;
6717             });
6718         }
6719         return field || null;
6720     },
6721      /**
6722      * Mark fields in this form invalid in bulk.
6723      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6724      * @return {BasicForm} this
6725      */
6726     markInvalid : function(errors){
6727         if(errors instanceof Array){
6728             for(var i = 0, len = errors.length; i < len; i++){
6729                 var fieldError = errors[i];
6730                 var f = this.findField(fieldError.id);
6731                 if(f){
6732                     f.markInvalid(fieldError.msg);
6733                 }
6734             }
6735         }else{
6736             var field, id;
6737             for(id in errors){
6738                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6739                     field.markInvalid(errors[id]);
6740                 }
6741             }
6742         }
6743         //Roo.each(this.childForms || [], function (f) {
6744         //    f.markInvalid(errors);
6745         //});
6746         
6747         return this;
6748     },
6749
6750     /**
6751      * Set values for fields in this form in bulk.
6752      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6753      * @return {BasicForm} this
6754      */
6755     setValues : function(values){
6756         if(values instanceof Array){ // array of objects
6757             for(var i = 0, len = values.length; i < len; i++){
6758                 var v = values[i];
6759                 var f = this.findField(v.id);
6760                 if(f){
6761                     f.setValue(v.value);
6762                     if(this.trackResetOnLoad){
6763                         f.originalValue = f.getValue();
6764                     }
6765                 }
6766             }
6767         }else{ // object hash
6768             var field, id;
6769             for(id in values){
6770                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6771                     
6772                     if (field.setFromData && 
6773                         field.valueField && 
6774                         field.displayField &&
6775                         // combos' with local stores can 
6776                         // be queried via setValue()
6777                         // to set their value..
6778                         (field.store && !field.store.isLocal)
6779                         ) {
6780                         // it's a combo
6781                         var sd = { };
6782                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6783                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6784                         field.setFromData(sd);
6785                         
6786                     } else {
6787                         field.setValue(values[id]);
6788                     }
6789                     
6790                     
6791                     if(this.trackResetOnLoad){
6792                         field.originalValue = field.getValue();
6793                     }
6794                 }
6795             }
6796         }
6797          
6798         //Roo.each(this.childForms || [], function (f) {
6799         //    f.setValues(values);
6800         //});
6801                 
6802         return this;
6803     },
6804
6805     /**
6806      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6807      * they are returned as an array.
6808      * @param {Boolean} asString
6809      * @return {Object}
6810      */
6811     getValues : function(asString){
6812         //if (this.childForms) {
6813             // copy values from the child forms
6814         //    Roo.each(this.childForms, function (f) {
6815         //        this.setValues(f.getValues());
6816         //    }, this);
6817         //}
6818         
6819         
6820         
6821         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6822         if(asString === true){
6823             return fs;
6824         }
6825         return Roo.urlDecode(fs);
6826     },
6827     
6828     /**
6829      * Returns the fields in this form as an object with key/value pairs. 
6830      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6831      * @return {Object}
6832      */
6833     getFieldValues : function(with_hidden)
6834     {
6835         var items = this.getItems();
6836         var ret = {};
6837         items.each(function(f){
6838             if (!f.getName()) {
6839                 return;
6840             }
6841             var v = f.getValue();
6842             if (f.inputType =='radio') {
6843                 if (typeof(ret[f.getName()]) == 'undefined') {
6844                     ret[f.getName()] = ''; // empty..
6845                 }
6846                 
6847                 if (!f.el.dom.checked) {
6848                     return;
6849                     
6850                 }
6851                 v = f.el.dom.value;
6852                 
6853             }
6854             
6855             // not sure if this supported any more..
6856             if ((typeof(v) == 'object') && f.getRawValue) {
6857                 v = f.getRawValue() ; // dates..
6858             }
6859             // combo boxes where name != hiddenName...
6860             if (f.name != f.getName()) {
6861                 ret[f.name] = f.getRawValue();
6862             }
6863             ret[f.getName()] = v;
6864         });
6865         
6866         return ret;
6867     },
6868
6869     /**
6870      * Clears all invalid messages in this form.
6871      * @return {BasicForm} this
6872      */
6873     clearInvalid : function(){
6874         var items = this.getItems();
6875         
6876         items.each(function(f){
6877            f.clearInvalid();
6878         });
6879         
6880         
6881         
6882         return this;
6883     },
6884
6885     /**
6886      * Resets this form.
6887      * @return {BasicForm} this
6888      */
6889     reset : function(){
6890         var items = this.getItems();
6891         items.each(function(f){
6892             f.reset();
6893         });
6894         
6895         Roo.each(this.childForms || [], function (f) {
6896             f.reset();
6897         });
6898        
6899         
6900         return this;
6901     },
6902     getItems : function()
6903     {
6904         var r=new Roo.util.MixedCollection(false, function(o){
6905             return o.id || (o.id = Roo.id());
6906         });
6907         var iter = function(el) {
6908             if (el.inputEl) {
6909                 r.add(el);
6910             }
6911             if (!el.items) {
6912                 return;
6913             }
6914             Roo.each(el.items,function(e) {
6915                 iter(e);
6916             });
6917             
6918             
6919         };
6920         
6921         iter(this);
6922         return r;
6923         
6924         
6925         
6926         
6927     }
6928     
6929 });
6930
6931  
6932 /*
6933  * Based on:
6934  * Ext JS Library 1.1.1
6935  * Copyright(c) 2006-2007, Ext JS, LLC.
6936  *
6937  * Originally Released Under LGPL - original licence link has changed is not relivant.
6938  *
6939  * Fork - LGPL
6940  * <script type="text/javascript">
6941  */
6942 /**
6943  * @class Roo.form.VTypes
6944  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6945  * @singleton
6946  */
6947 Roo.form.VTypes = function(){
6948     // closure these in so they are only created once.
6949     var alpha = /^[a-zA-Z_]+$/;
6950     var alphanum = /^[a-zA-Z0-9_]+$/;
6951     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6952     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6953
6954     // All these messages and functions are configurable
6955     return {
6956         /**
6957          * The function used to validate email addresses
6958          * @param {String} value The email address
6959          */
6960         'email' : function(v){
6961             return email.test(v);
6962         },
6963         /**
6964          * The error text to display when the email validation function returns false
6965          * @type String
6966          */
6967         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6968         /**
6969          * The keystroke filter mask to be applied on email input
6970          * @type RegExp
6971          */
6972         'emailMask' : /[a-z0-9_\.\-@]/i,
6973
6974         /**
6975          * The function used to validate URLs
6976          * @param {String} value The URL
6977          */
6978         'url' : function(v){
6979             return url.test(v);
6980         },
6981         /**
6982          * The error text to display when the url validation function returns false
6983          * @type String
6984          */
6985         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6986         
6987         /**
6988          * The function used to validate alpha values
6989          * @param {String} value The value
6990          */
6991         'alpha' : function(v){
6992             return alpha.test(v);
6993         },
6994         /**
6995          * The error text to display when the alpha validation function returns false
6996          * @type String
6997          */
6998         'alphaText' : 'This field should only contain letters and _',
6999         /**
7000          * The keystroke filter mask to be applied on alpha input
7001          * @type RegExp
7002          */
7003         'alphaMask' : /[a-z_]/i,
7004
7005         /**
7006          * The function used to validate alphanumeric values
7007          * @param {String} value The value
7008          */
7009         'alphanum' : function(v){
7010             return alphanum.test(v);
7011         },
7012         /**
7013          * The error text to display when the alphanumeric validation function returns false
7014          * @type String
7015          */
7016         'alphanumText' : 'This field should only contain letters, numbers and _',
7017         /**
7018          * The keystroke filter mask to be applied on alphanumeric input
7019          * @type RegExp
7020          */
7021         'alphanumMask' : /[a-z0-9_]/i
7022     };
7023 }();/*
7024  * - LGPL
7025  *
7026  * Input
7027  * 
7028  */
7029
7030 /**
7031  * @class Roo.bootstrap.Input
7032  * @extends Roo.bootstrap.Component
7033  * Bootstrap Input class
7034  * @cfg {Boolean} disabled is it disabled
7035  * @cfg {String} fieldLabel - the label associated
7036  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7037  * @cfg {String} name name of the input
7038  * @cfg {string} fieldLabel - the label associated
7039  * @cfg {string}  inputType - input / file submit ...
7040  * @cfg {string} placeholder - placeholder to put in text.
7041  * @cfg {string}  before - input group add on before
7042  * @cfg {string} after - input group add on after
7043  * @cfg {string} size - (lg|sm) or leave empty..
7044  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7045  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7046  * @cfg {Number} md colspan out of 12 for computer-sized screens
7047  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7048  * @cfg {string} value default value of the input
7049  * @cfg {Number} labelWidth set the width of label (0-12)
7050  * @cfg {String} labelAlign (top|left)
7051  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7052  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7053
7054  * @cfg {String} align (left|center|right) Default left
7055  * 
7056  * 
7057  * 
7058  * @constructor
7059  * Create a new Input
7060  * @param {Object} config The config object
7061  */
7062
7063 Roo.bootstrap.Input = function(config){
7064     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7065    
7066         this.addEvents({
7067             /**
7068              * @event focus
7069              * Fires when this field receives input focus.
7070              * @param {Roo.form.Field} this
7071              */
7072             focus : true,
7073             /**
7074              * @event blur
7075              * Fires when this field loses input focus.
7076              * @param {Roo.form.Field} this
7077              */
7078             blur : true,
7079             /**
7080              * @event specialkey
7081              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7082              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7083              * @param {Roo.form.Field} this
7084              * @param {Roo.EventObject} e The event object
7085              */
7086             specialkey : true,
7087             /**
7088              * @event change
7089              * Fires just before the field blurs if the field value has changed.
7090              * @param {Roo.form.Field} this
7091              * @param {Mixed} newValue The new value
7092              * @param {Mixed} oldValue The original value
7093              */
7094             change : true,
7095             /**
7096              * @event invalid
7097              * Fires after the field has been marked as invalid.
7098              * @param {Roo.form.Field} this
7099              * @param {String} msg The validation message
7100              */
7101             invalid : true,
7102             /**
7103              * @event valid
7104              * Fires after the field has been validated with no errors.
7105              * @param {Roo.form.Field} this
7106              */
7107             valid : true,
7108              /**
7109              * @event keyup
7110              * Fires after the key up
7111              * @param {Roo.form.Field} this
7112              * @param {Roo.EventObject}  e The event Object
7113              */
7114             keyup : true
7115         });
7116 };
7117
7118 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7119      /**
7120      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7121       automatic validation (defaults to "keyup").
7122      */
7123     validationEvent : "keyup",
7124      /**
7125      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7126      */
7127     validateOnBlur : true,
7128     /**
7129      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7130      */
7131     validationDelay : 250,
7132      /**
7133      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7134      */
7135     focusClass : "x-form-focus",  // not needed???
7136     
7137        
7138     /**
7139      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7140      */
7141     invalidClass : "has-warning",
7142     
7143     /**
7144      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7145      */
7146     validClass : "has-success",
7147     
7148     /**
7149      * @cfg {Boolean} hasFeedback (true|false) default true
7150      */
7151     hasFeedback : true,
7152     
7153     /**
7154      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7155      */
7156     invalidFeedbackClass : "glyphicon-warning-sign",
7157     
7158     /**
7159      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7160      */
7161     validFeedbackClass : "glyphicon-ok",
7162     
7163     /**
7164      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7165      */
7166     selectOnFocus : false,
7167     
7168      /**
7169      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7170      */
7171     maskRe : null,
7172        /**
7173      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7174      */
7175     vtype : null,
7176     
7177       /**
7178      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7179      */
7180     disableKeyFilter : false,
7181     
7182        /**
7183      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7184      */
7185     disabled : false,
7186      /**
7187      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7188      */
7189     allowBlank : true,
7190     /**
7191      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7192      */
7193     blankText : "This field is required",
7194     
7195      /**
7196      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7197      */
7198     minLength : 0,
7199     /**
7200      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7201      */
7202     maxLength : Number.MAX_VALUE,
7203     /**
7204      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7205      */
7206     minLengthText : "The minimum length for this field is {0}",
7207     /**
7208      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7209      */
7210     maxLengthText : "The maximum length for this field is {0}",
7211   
7212     
7213     /**
7214      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7215      * If available, this function will be called only after the basic validators all return true, and will be passed the
7216      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7217      */
7218     validator : null,
7219     /**
7220      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7221      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7222      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7223      */
7224     regex : null,
7225     /**
7226      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7227      */
7228     regexText : "",
7229     
7230     autocomplete: false,
7231     
7232     
7233     fieldLabel : '',
7234     inputType : 'text',
7235     
7236     name : false,
7237     placeholder: false,
7238     before : false,
7239     after : false,
7240     size : false,
7241     hasFocus : false,
7242     preventMark: false,
7243     isFormField : true,
7244     value : '',
7245     labelWidth : 2,
7246     labelAlign : false,
7247     readOnly : false,
7248     align : false,
7249     formatedValue : false,
7250     
7251     parentLabelAlign : function()
7252     {
7253         var parent = this;
7254         while (parent.parent()) {
7255             parent = parent.parent();
7256             if (typeof(parent.labelAlign) !='undefined') {
7257                 return parent.labelAlign;
7258             }
7259         }
7260         return 'left';
7261         
7262     },
7263     
7264     getAutoCreate : function(){
7265         
7266         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7267         
7268         var id = Roo.id();
7269         
7270         var cfg = {};
7271         
7272         if(this.inputType != 'hidden'){
7273             cfg.cls = 'form-group' //input-group
7274         }
7275         
7276         var input =  {
7277             tag: 'input',
7278             id : id,
7279             type : this.inputType,
7280             value : this.value,
7281             cls : 'form-control',
7282             placeholder : this.placeholder || '',
7283             autocomplete : this.autocomplete || 'new-password'
7284         };
7285         
7286         
7287         if(this.align){
7288             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7289         }
7290         
7291         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7292             input.maxLength = this.maxLength;
7293         }
7294         
7295         if (this.disabled) {
7296             input.disabled=true;
7297         }
7298         
7299         if (this.readOnly) {
7300             input.readonly=true;
7301         }
7302         
7303         if (this.name) {
7304             input.name = this.name;
7305         }
7306         if (this.size) {
7307             input.cls += ' input-' + this.size;
7308         }
7309         var settings=this;
7310         ['xs','sm','md','lg'].map(function(size){
7311             if (settings[size]) {
7312                 cfg.cls += ' col-' + size + '-' + settings[size];
7313             }
7314         });
7315         
7316         var inputblock = input;
7317         
7318         if(this.hasFeedback && this.inputType != 'hidden'){
7319             
7320             var feedback = {
7321                 tag: 'span',
7322                 cls: 'glyphicon form-control-feedback'
7323             };
7324
7325             inputblock = {
7326                 cls : 'has-feedback',
7327                 cn :  [
7328                     input,
7329                     feedback
7330                 ] 
7331             };  
7332         }
7333          
7334 //        var inputblock = input;
7335         
7336         if (this.before || this.after) {
7337             
7338             inputblock = {
7339                 cls : 'input-group',
7340                 cn :  [] 
7341             };
7342             
7343             if (this.before && typeof(this.before) == 'string') {
7344                 
7345                 inputblock.cn.push({
7346                     tag :'span',
7347                     cls : 'roo-input-before input-group-addon',
7348                     html : this.before
7349                 });
7350             }
7351             if (this.before && typeof(this.before) == 'object') {
7352                 this.before = Roo.factory(this.before);
7353                 Roo.log(this.before);
7354                 inputblock.cn.push({
7355                     tag :'span',
7356                     cls : 'roo-input-before input-group-' +
7357                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7358                 });
7359             }
7360             
7361             inputblock.cn.push(input);
7362             
7363             if(this.hasFeedback && this.inputType != 'hidden'){
7364                 inputblock.cls += ' has-feedback';
7365                 inputblock.cn.push(feedback);
7366             }
7367             
7368             if (this.after && typeof(this.after) == 'string') {
7369                 inputblock.cn.push({
7370                     tag :'span',
7371                     cls : 'roo-input-after input-group-addon',
7372                     html : this.after
7373                 });
7374             }
7375             if (this.after && typeof(this.after) == 'object') {
7376                 this.after = Roo.factory(this.after);
7377                 Roo.log(this.after);
7378                 inputblock.cn.push({
7379                     tag :'span',
7380                     cls : 'roo-input-after input-group-' +
7381                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7382                 });
7383             }
7384         };
7385         
7386         if (align ==='left' && this.fieldLabel.length) {
7387                 Roo.log("left and has label");
7388                 cfg.cn = [
7389                     
7390                     {
7391                         tag: 'label',
7392                         'for' :  id,
7393                         cls : 'control-label col-sm-' + this.labelWidth,
7394                         html : this.fieldLabel
7395                         
7396                     },
7397                     {
7398                         cls : "col-sm-" + (12 - this.labelWidth), 
7399                         cn: [
7400                             inputblock
7401                         ]
7402                     }
7403                     
7404                 ];
7405         } else if ( this.fieldLabel.length) {
7406                 Roo.log(" label");
7407                  cfg.cn = [
7408                    
7409                     {
7410                         tag: 'label',
7411                         //cls : 'input-group-addon',
7412                         html : this.fieldLabel
7413                         
7414                     },
7415                     
7416                     inputblock
7417                     
7418                 ];
7419
7420         } else {
7421             
7422                 Roo.log(" no label && no align");
7423                 cfg.cn = [
7424                     
7425                         inputblock
7426                     
7427                 ];
7428                 
7429                 
7430         };
7431         Roo.log('input-parentType: ' + this.parentType);
7432         
7433         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7434            cfg.cls += ' navbar-form';
7435            Roo.log(cfg);
7436         }
7437         
7438         return cfg;
7439         
7440     },
7441     /**
7442      * return the real input element.
7443      */
7444     inputEl: function ()
7445     {
7446         return this.el.select('input.form-control',true).first();
7447     },
7448     
7449     tooltipEl : function()
7450     {
7451         return this.inputEl();
7452     },
7453     
7454     setDisabled : function(v)
7455     {
7456         var i  = this.inputEl().dom;
7457         if (!v) {
7458             i.removeAttribute('disabled');
7459             return;
7460             
7461         }
7462         i.setAttribute('disabled','true');
7463     },
7464     initEvents : function()
7465     {
7466           
7467         this.inputEl().on("keydown" , this.fireKey,  this);
7468         this.inputEl().on("focus", this.onFocus,  this);
7469         this.inputEl().on("blur", this.onBlur,  this);
7470         
7471         this.inputEl().relayEvent('keyup', this);
7472
7473         // reference to original value for reset
7474         this.originalValue = this.getValue();
7475         //Roo.form.TextField.superclass.initEvents.call(this);
7476         if(this.validationEvent == 'keyup'){
7477             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7478             this.inputEl().on('keyup', this.filterValidation, this);
7479         }
7480         else if(this.validationEvent !== false){
7481             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7482         }
7483         
7484         if(this.selectOnFocus){
7485             this.on("focus", this.preFocus, this);
7486             
7487         }
7488         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7489             this.inputEl().on("keypress", this.filterKeys, this);
7490         }
7491        /* if(this.grow){
7492             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7493             this.el.on("click", this.autoSize,  this);
7494         }
7495         */
7496         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7497             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7498         }
7499         
7500         if (typeof(this.before) == 'object') {
7501             this.before.render(this.el.select('.roo-input-before',true).first());
7502         }
7503         if (typeof(this.after) == 'object') {
7504             this.after.render(this.el.select('.roo-input-after',true).first());
7505         }
7506         
7507         
7508     },
7509     filterValidation : function(e){
7510         if(!e.isNavKeyPress()){
7511             this.validationTask.delay(this.validationDelay);
7512         }
7513     },
7514      /**
7515      * Validates the field value
7516      * @return {Boolean} True if the value is valid, else false
7517      */
7518     validate : function(){
7519         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7520         if(this.disabled || this.validateValue(this.getRawValue())){
7521             this.markValid();
7522             return true;
7523         }
7524         
7525         this.markInvalid();
7526         return false;
7527     },
7528     
7529     
7530     /**
7531      * Validates a value according to the field's validation rules and marks the field as invalid
7532      * if the validation fails
7533      * @param {Mixed} value The value to validate
7534      * @return {Boolean} True if the value is valid, else false
7535      */
7536     validateValue : function(value){
7537         if(value.length < 1)  { // if it's blank
7538             if(this.allowBlank){
7539                 return true;
7540             }
7541             return false;
7542         }
7543         
7544         if(value.length < this.minLength){
7545             return false;
7546         }
7547         if(value.length > this.maxLength){
7548             return false;
7549         }
7550         if(this.vtype){
7551             var vt = Roo.form.VTypes;
7552             if(!vt[this.vtype](value, this)){
7553                 return false;
7554             }
7555         }
7556         if(typeof this.validator == "function"){
7557             var msg = this.validator(value);
7558             if(msg !== true){
7559                 return false;
7560             }
7561         }
7562         
7563         if(this.regex && !this.regex.test(value)){
7564             return false;
7565         }
7566         
7567         return true;
7568     },
7569
7570     
7571     
7572      // private
7573     fireKey : function(e){
7574         //Roo.log('field ' + e.getKey());
7575         if(e.isNavKeyPress()){
7576             this.fireEvent("specialkey", this, e);
7577         }
7578     },
7579     focus : function (selectText){
7580         if(this.rendered){
7581             this.inputEl().focus();
7582             if(selectText === true){
7583                 this.inputEl().dom.select();
7584             }
7585         }
7586         return this;
7587     } ,
7588     
7589     onFocus : function(){
7590         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7591            // this.el.addClass(this.focusClass);
7592         }
7593         if(!this.hasFocus){
7594             this.hasFocus = true;
7595             this.startValue = this.getValue();
7596             this.fireEvent("focus", this);
7597         }
7598     },
7599     
7600     beforeBlur : Roo.emptyFn,
7601
7602     
7603     // private
7604     onBlur : function(){
7605         this.beforeBlur();
7606         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7607             //this.el.removeClass(this.focusClass);
7608         }
7609         this.hasFocus = false;
7610         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7611             this.validate();
7612         }
7613         var v = this.getValue();
7614         if(String(v) !== String(this.startValue)){
7615             this.fireEvent('change', this, v, this.startValue);
7616         }
7617         this.fireEvent("blur", this);
7618     },
7619     
7620     /**
7621      * Resets the current field value to the originally loaded value and clears any validation messages
7622      */
7623     reset : function(){
7624         this.setValue(this.originalValue);
7625         this.validate();
7626     },
7627      /**
7628      * Returns the name of the field
7629      * @return {Mixed} name The name field
7630      */
7631     getName: function(){
7632         return this.name;
7633     },
7634      /**
7635      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7636      * @return {Mixed} value The field value
7637      */
7638     getValue : function(){
7639         
7640         var v = this.inputEl().getValue();
7641         
7642         return v;
7643     },
7644     /**
7645      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7646      * @return {Mixed} value The field value
7647      */
7648     getRawValue : function(){
7649         var v = this.inputEl().getValue();
7650         
7651         return v;
7652     },
7653     
7654     /**
7655      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7656      * @param {Mixed} value The value to set
7657      */
7658     setRawValue : function(v){
7659         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7660     },
7661     
7662     selectText : function(start, end){
7663         var v = this.getRawValue();
7664         if(v.length > 0){
7665             start = start === undefined ? 0 : start;
7666             end = end === undefined ? v.length : end;
7667             var d = this.inputEl().dom;
7668             if(d.setSelectionRange){
7669                 d.setSelectionRange(start, end);
7670             }else if(d.createTextRange){
7671                 var range = d.createTextRange();
7672                 range.moveStart("character", start);
7673                 range.moveEnd("character", v.length-end);
7674                 range.select();
7675             }
7676         }
7677     },
7678     
7679     /**
7680      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7681      * @param {Mixed} value The value to set
7682      */
7683     setValue : function(v){
7684         this.value = v;
7685         if(this.rendered){
7686             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7687             this.validate();
7688         }
7689     },
7690     
7691     /*
7692     processValue : function(value){
7693         if(this.stripCharsRe){
7694             var newValue = value.replace(this.stripCharsRe, '');
7695             if(newValue !== value){
7696                 this.setRawValue(newValue);
7697                 return newValue;
7698             }
7699         }
7700         return value;
7701     },
7702   */
7703     preFocus : function(){
7704         
7705         if(this.selectOnFocus){
7706             this.inputEl().dom.select();
7707         }
7708     },
7709     filterKeys : function(e){
7710         var k = e.getKey();
7711         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7712             return;
7713         }
7714         var c = e.getCharCode(), cc = String.fromCharCode(c);
7715         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7716             return;
7717         }
7718         if(!this.maskRe.test(cc)){
7719             e.stopEvent();
7720         }
7721     },
7722      /**
7723      * Clear any invalid styles/messages for this field
7724      */
7725     clearInvalid : function(){
7726         
7727         if(!this.el || this.preventMark){ // not rendered
7728             return;
7729         }
7730         this.el.removeClass(this.invalidClass);
7731         
7732         this.fireEvent('valid', this);
7733     },
7734     
7735      /**
7736      * Mark this field as valid
7737      */
7738     markValid : function(){
7739         if(!this.el  || this.preventMark){ // not rendered
7740             return;
7741         }
7742         
7743         this.el.removeClass([this.invalidClass, this.validClass]);
7744         
7745         this.el.addClass(this.validClass);
7746         
7747         if(this.hasFeedback && this.inputType != 'hidden'){
7748             var feedback = this.el.select('.form-control-feedback', true).first();
7749             
7750             if(feedback){
7751                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7752                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
7753             }
7754             
7755         }
7756         
7757         this.fireEvent('valid', this);
7758     },
7759     
7760      /**
7761      * Mark this field as invalid
7762      * @param {String} msg The validation message
7763      */
7764     markInvalid : function(msg){
7765         if(!this.el  || this.preventMark){ // not rendered
7766             return;
7767         }
7768         
7769         this.el.removeClass([this.invalidClass, this.validClass]);
7770         
7771         this.el.addClass(this.invalidClass);
7772         
7773         if(this.hasFeedback && this.inputType != 'hidden'){
7774             
7775             var feedback = this.el.select('.form-control-feedback', true).first();
7776             
7777             if(feedback){
7778                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7779                 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
7780             }
7781             
7782         }
7783         
7784         this.fireEvent('invalid', this, msg);
7785     },
7786     // private
7787     SafariOnKeyDown : function(event)
7788     {
7789         // this is a workaround for a password hang bug on chrome/ webkit.
7790         
7791         var isSelectAll = false;
7792         
7793         if(this.inputEl().dom.selectionEnd > 0){
7794             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7795         }
7796         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7797             event.preventDefault();
7798             this.setValue('');
7799             return;
7800         }
7801         
7802         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
7803             
7804             event.preventDefault();
7805             // this is very hacky as keydown always get's upper case.
7806             //
7807             var cc = String.fromCharCode(event.getCharCode());
7808             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7809             
7810         }
7811     },
7812     adjustWidth : function(tag, w){
7813         tag = tag.toLowerCase();
7814         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7815             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7816                 if(tag == 'input'){
7817                     return w + 2;
7818                 }
7819                 if(tag == 'textarea'){
7820                     return w-2;
7821                 }
7822             }else if(Roo.isOpera){
7823                 if(tag == 'input'){
7824                     return w + 2;
7825                 }
7826                 if(tag == 'textarea'){
7827                     return w-2;
7828                 }
7829             }
7830         }
7831         return w;
7832     }
7833     
7834 });
7835
7836  
7837 /*
7838  * - LGPL
7839  *
7840  * Input
7841  * 
7842  */
7843
7844 /**
7845  * @class Roo.bootstrap.TextArea
7846  * @extends Roo.bootstrap.Input
7847  * Bootstrap TextArea class
7848  * @cfg {Number} cols Specifies the visible width of a text area
7849  * @cfg {Number} rows Specifies the visible number of lines in a text area
7850  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7851  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7852  * @cfg {string} html text
7853  * 
7854  * @constructor
7855  * Create a new TextArea
7856  * @param {Object} config The config object
7857  */
7858
7859 Roo.bootstrap.TextArea = function(config){
7860     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7861    
7862 };
7863
7864 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7865      
7866     cols : false,
7867     rows : 5,
7868     readOnly : false,
7869     warp : 'soft',
7870     resize : false,
7871     value: false,
7872     html: false,
7873     
7874     getAutoCreate : function(){
7875         
7876         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7877         
7878         var id = Roo.id();
7879         
7880         var cfg = {};
7881         
7882         var input =  {
7883             tag: 'textarea',
7884             id : id,
7885             warp : this.warp,
7886             rows : this.rows,
7887             value : this.value || '',
7888             html: this.html || '',
7889             cls : 'form-control',
7890             placeholder : this.placeholder || '' 
7891             
7892         };
7893         
7894         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7895             input.maxLength = this.maxLength;
7896         }
7897         
7898         if(this.resize){
7899             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7900         }
7901         
7902         if(this.cols){
7903             input.cols = this.cols;
7904         }
7905         
7906         if (this.readOnly) {
7907             input.readonly = true;
7908         }
7909         
7910         if (this.name) {
7911             input.name = this.name;
7912         }
7913         
7914         if (this.size) {
7915             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7916         }
7917         
7918         var settings=this;
7919         ['xs','sm','md','lg'].map(function(size){
7920             if (settings[size]) {
7921                 cfg.cls += ' col-' + size + '-' + settings[size];
7922             }
7923         });
7924         
7925         var inputblock = input;
7926         
7927         if(this.hasFeedback){
7928             
7929             var feedback = {
7930                 tag: 'span',
7931                 cls: 'glyphicon form-control-feedback'
7932             };
7933
7934             inputblock = {
7935                 cls : 'has-feedback',
7936                 cn :  [
7937                     input,
7938                     feedback
7939                 ] 
7940             };  
7941         }
7942         
7943         
7944         if (this.before || this.after) {
7945             
7946             inputblock = {
7947                 cls : 'input-group',
7948                 cn :  [] 
7949             };
7950             if (this.before) {
7951                 inputblock.cn.push({
7952                     tag :'span',
7953                     cls : 'input-group-addon',
7954                     html : this.before
7955                 });
7956             }
7957             
7958             inputblock.cn.push(input);
7959             
7960             if(this.hasFeedback){
7961                 inputblock.cls += ' has-feedback';
7962                 inputblock.cn.push(feedback);
7963             }
7964             
7965             if (this.after) {
7966                 inputblock.cn.push({
7967                     tag :'span',
7968                     cls : 'input-group-addon',
7969                     html : this.after
7970                 });
7971             }
7972             
7973         }
7974         
7975         if (align ==='left' && this.fieldLabel.length) {
7976                 Roo.log("left and has label");
7977                 cfg.cn = [
7978                     
7979                     {
7980                         tag: 'label',
7981                         'for' :  id,
7982                         cls : 'control-label col-sm-' + this.labelWidth,
7983                         html : this.fieldLabel
7984                         
7985                     },
7986                     {
7987                         cls : "col-sm-" + (12 - this.labelWidth), 
7988                         cn: [
7989                             inputblock
7990                         ]
7991                     }
7992                     
7993                 ];
7994         } else if ( this.fieldLabel.length) {
7995                 Roo.log(" label");
7996                  cfg.cn = [
7997                    
7998                     {
7999                         tag: 'label',
8000                         //cls : 'input-group-addon',
8001                         html : this.fieldLabel
8002                         
8003                     },
8004                     
8005                     inputblock
8006                     
8007                 ];
8008
8009         } else {
8010             
8011                    Roo.log(" no label && no align");
8012                 cfg.cn = [
8013                     
8014                         inputblock
8015                     
8016                 ];
8017                 
8018                 
8019         }
8020         
8021         if (this.disabled) {
8022             input.disabled=true;
8023         }
8024         
8025         return cfg;
8026         
8027     },
8028     /**
8029      * return the real textarea element.
8030      */
8031     inputEl: function ()
8032     {
8033         return this.el.select('textarea.form-control',true).first();
8034     }
8035 });
8036
8037  
8038 /*
8039  * - LGPL
8040  *
8041  * trigger field - base class for combo..
8042  * 
8043  */
8044  
8045 /**
8046  * @class Roo.bootstrap.TriggerField
8047  * @extends Roo.bootstrap.Input
8048  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8049  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8050  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8051  * for which you can provide a custom implementation.  For example:
8052  * <pre><code>
8053 var trigger = new Roo.bootstrap.TriggerField();
8054 trigger.onTriggerClick = myTriggerFn;
8055 trigger.applyTo('my-field');
8056 </code></pre>
8057  *
8058  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8059  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8060  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8061  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8062  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8063
8064  * @constructor
8065  * Create a new TriggerField.
8066  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8067  * to the base TextField)
8068  */
8069 Roo.bootstrap.TriggerField = function(config){
8070     this.mimicing = false;
8071     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8072 };
8073
8074 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8075     /**
8076      * @cfg {String} triggerClass A CSS class to apply to the trigger
8077      */
8078      /**
8079      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8080      */
8081     hideTrigger:false,
8082
8083     /** @cfg {Boolean} grow @hide */
8084     /** @cfg {Number} growMin @hide */
8085     /** @cfg {Number} growMax @hide */
8086
8087     /**
8088      * @hide 
8089      * @method
8090      */
8091     autoSize: Roo.emptyFn,
8092     // private
8093     monitorTab : true,
8094     // private
8095     deferHeight : true,
8096
8097     
8098     actionMode : 'wrap',
8099     
8100     caret : false,
8101     
8102     
8103     getAutoCreate : function(){
8104        
8105         var align = this.labelAlign || this.parentLabelAlign();
8106         
8107         var id = Roo.id();
8108         
8109         var cfg = {
8110             cls: 'form-group' //input-group
8111         };
8112         
8113         
8114         var input =  {
8115             tag: 'input',
8116             id : id,
8117             type : this.inputType,
8118             cls : 'form-control',
8119             autocomplete: 'new-password',
8120             placeholder : this.placeholder || '' 
8121             
8122         };
8123         if (this.name) {
8124             input.name = this.name;
8125         }
8126         if (this.size) {
8127             input.cls += ' input-' + this.size;
8128         }
8129         
8130         if (this.disabled) {
8131             input.disabled=true;
8132         }
8133         
8134         var inputblock = input;
8135         
8136         if (this.before || this.after) {
8137             
8138             inputblock = {
8139                 cls : 'input-group',
8140                 cn :  [] 
8141             };
8142             if (this.before) {
8143                 inputblock.cn.push({
8144                     tag :'span',
8145                     cls : 'input-group-addon',
8146                     html : this.before
8147                 });
8148             }
8149             inputblock.cn.push(input);
8150             if (this.after) {
8151                 inputblock.cn.push({
8152                     tag :'span',
8153                     cls : 'input-group-addon',
8154                     html : this.after
8155                 });
8156             }
8157             
8158         };
8159         
8160         var box = {
8161             tag: 'div',
8162             cn: [
8163                 {
8164                     tag: 'input',
8165                     type : 'hidden',
8166                     cls: 'form-hidden-field'
8167                 },
8168                 inputblock
8169             ]
8170             
8171         };
8172         
8173         if(this.multiple){
8174             Roo.log('multiple');
8175             
8176             box = {
8177                 tag: 'div',
8178                 cn: [
8179                     {
8180                         tag: 'input',
8181                         type : 'hidden',
8182                         cls: 'form-hidden-field'
8183                     },
8184                     {
8185                         tag: 'ul',
8186                         cls: 'select2-choices',
8187                         cn:[
8188                             {
8189                                 tag: 'li',
8190                                 cls: 'select2-search-field',
8191                                 cn: [
8192
8193                                     inputblock
8194                                 ]
8195                             }
8196                         ]
8197                     }
8198                 ]
8199             }
8200         };
8201         
8202         var combobox = {
8203             cls: 'select2-container input-group',
8204             cn: [
8205                 box
8206 //                {
8207 //                    tag: 'ul',
8208 //                    cls: 'typeahead typeahead-long dropdown-menu',
8209 //                    style: 'display:none'
8210 //                }
8211             ]
8212         };
8213         
8214         if(!this.multiple && this.showToggleBtn){
8215             
8216             var caret = {
8217                         tag: 'span',
8218                         cls: 'caret'
8219              };
8220             if (this.caret != false) {
8221                 caret = {
8222                      tag: 'i',
8223                      cls: 'fa fa-' + this.caret
8224                 };
8225                 
8226             }
8227             
8228             combobox.cn.push({
8229                 tag :'span',
8230                 cls : 'input-group-addon btn dropdown-toggle',
8231                 cn : [
8232                     caret,
8233                     {
8234                         tag: 'span',
8235                         cls: 'combobox-clear',
8236                         cn  : [
8237                             {
8238                                 tag : 'i',
8239                                 cls: 'icon-remove'
8240                             }
8241                         ]
8242                     }
8243                 ]
8244
8245             })
8246         }
8247         
8248         if(this.multiple){
8249             combobox.cls += ' select2-container-multi';
8250         }
8251         
8252         if (align ==='left' && this.fieldLabel.length) {
8253             
8254                 Roo.log("left and has label");
8255                 cfg.cn = [
8256                     
8257                     {
8258                         tag: 'label',
8259                         'for' :  id,
8260                         cls : 'control-label col-sm-' + this.labelWidth,
8261                         html : this.fieldLabel
8262                         
8263                     },
8264                     {
8265                         cls : "col-sm-" + (12 - this.labelWidth), 
8266                         cn: [
8267                             combobox
8268                         ]
8269                     }
8270                     
8271                 ];
8272         } else if ( this.fieldLabel.length) {
8273                 Roo.log(" label");
8274                  cfg.cn = [
8275                    
8276                     {
8277                         tag: 'label',
8278                         //cls : 'input-group-addon',
8279                         html : this.fieldLabel
8280                         
8281                     },
8282                     
8283                     combobox
8284                     
8285                 ];
8286
8287         } else {
8288             
8289                 Roo.log(" no label && no align");
8290                 cfg = combobox
8291                      
8292                 
8293         }
8294          
8295         var settings=this;
8296         ['xs','sm','md','lg'].map(function(size){
8297             if (settings[size]) {
8298                 cfg.cls += ' col-' + size + '-' + settings[size];
8299             }
8300         });
8301         
8302         return cfg;
8303         
8304     },
8305     
8306     
8307     
8308     // private
8309     onResize : function(w, h){
8310 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8311 //        if(typeof w == 'number'){
8312 //            var x = w - this.trigger.getWidth();
8313 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8314 //            this.trigger.setStyle('left', x+'px');
8315 //        }
8316     },
8317
8318     // private
8319     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8320
8321     // private
8322     getResizeEl : function(){
8323         return this.inputEl();
8324     },
8325
8326     // private
8327     getPositionEl : function(){
8328         return this.inputEl();
8329     },
8330
8331     // private
8332     alignErrorIcon : function(){
8333         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8334     },
8335
8336     // private
8337     initEvents : function(){
8338         
8339         this.createList();
8340         
8341         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8342         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8343         if(!this.multiple && this.showToggleBtn){
8344             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8345             if(this.hideTrigger){
8346                 this.trigger.setDisplayed(false);
8347             }
8348             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8349         }
8350         
8351         if(this.multiple){
8352             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8353         }
8354         
8355         //this.trigger.addClassOnOver('x-form-trigger-over');
8356         //this.trigger.addClassOnClick('x-form-trigger-click');
8357         
8358         //if(!this.width){
8359         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8360         //}
8361     },
8362     
8363     createList : function()
8364     {
8365         this.list = Roo.get(document.body).createChild({
8366             tag: 'ul',
8367             cls: 'typeahead typeahead-long dropdown-menu',
8368             style: 'display:none'
8369         });
8370         
8371         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8372         
8373     },
8374
8375     // private
8376     initTrigger : function(){
8377        
8378     },
8379
8380     // private
8381     onDestroy : function(){
8382         if(this.trigger){
8383             this.trigger.removeAllListeners();
8384           //  this.trigger.remove();
8385         }
8386         //if(this.wrap){
8387         //    this.wrap.remove();
8388         //}
8389         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8390     },
8391
8392     // private
8393     onFocus : function(){
8394         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8395         /*
8396         if(!this.mimicing){
8397             this.wrap.addClass('x-trigger-wrap-focus');
8398             this.mimicing = true;
8399             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8400             if(this.monitorTab){
8401                 this.el.on("keydown", this.checkTab, this);
8402             }
8403         }
8404         */
8405     },
8406
8407     // private
8408     checkTab : function(e){
8409         if(e.getKey() == e.TAB){
8410             this.triggerBlur();
8411         }
8412     },
8413
8414     // private
8415     onBlur : function(){
8416         // do nothing
8417     },
8418
8419     // private
8420     mimicBlur : function(e, t){
8421         /*
8422         if(!this.wrap.contains(t) && this.validateBlur()){
8423             this.triggerBlur();
8424         }
8425         */
8426     },
8427
8428     // private
8429     triggerBlur : function(){
8430         this.mimicing = false;
8431         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8432         if(this.monitorTab){
8433             this.el.un("keydown", this.checkTab, this);
8434         }
8435         //this.wrap.removeClass('x-trigger-wrap-focus');
8436         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8437     },
8438
8439     // private
8440     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8441     validateBlur : function(e, t){
8442         return true;
8443     },
8444
8445     // private
8446     onDisable : function(){
8447         this.inputEl().dom.disabled = true;
8448         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8449         //if(this.wrap){
8450         //    this.wrap.addClass('x-item-disabled');
8451         //}
8452     },
8453
8454     // private
8455     onEnable : function(){
8456         this.inputEl().dom.disabled = false;
8457         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8458         //if(this.wrap){
8459         //    this.el.removeClass('x-item-disabled');
8460         //}
8461     },
8462
8463     // private
8464     onShow : function(){
8465         var ae = this.getActionEl();
8466         
8467         if(ae){
8468             ae.dom.style.display = '';
8469             ae.dom.style.visibility = 'visible';
8470         }
8471     },
8472
8473     // private
8474     
8475     onHide : function(){
8476         var ae = this.getActionEl();
8477         ae.dom.style.display = 'none';
8478     },
8479
8480     /**
8481      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8482      * by an implementing function.
8483      * @method
8484      * @param {EventObject} e
8485      */
8486     onTriggerClick : Roo.emptyFn
8487 });
8488  /*
8489  * Based on:
8490  * Ext JS Library 1.1.1
8491  * Copyright(c) 2006-2007, Ext JS, LLC.
8492  *
8493  * Originally Released Under LGPL - original licence link has changed is not relivant.
8494  *
8495  * Fork - LGPL
8496  * <script type="text/javascript">
8497  */
8498
8499
8500 /**
8501  * @class Roo.data.SortTypes
8502  * @singleton
8503  * Defines the default sorting (casting?) comparison functions used when sorting data.
8504  */
8505 Roo.data.SortTypes = {
8506     /**
8507      * Default sort that does nothing
8508      * @param {Mixed} s The value being converted
8509      * @return {Mixed} The comparison value
8510      */
8511     none : function(s){
8512         return s;
8513     },
8514     
8515     /**
8516      * The regular expression used to strip tags
8517      * @type {RegExp}
8518      * @property
8519      */
8520     stripTagsRE : /<\/?[^>]+>/gi,
8521     
8522     /**
8523      * Strips all HTML tags to sort on text only
8524      * @param {Mixed} s The value being converted
8525      * @return {String} The comparison value
8526      */
8527     asText : function(s){
8528         return String(s).replace(this.stripTagsRE, "");
8529     },
8530     
8531     /**
8532      * Strips all HTML tags to sort on text only - Case insensitive
8533      * @param {Mixed} s The value being converted
8534      * @return {String} The comparison value
8535      */
8536     asUCText : function(s){
8537         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8538     },
8539     
8540     /**
8541      * Case insensitive string
8542      * @param {Mixed} s The value being converted
8543      * @return {String} The comparison value
8544      */
8545     asUCString : function(s) {
8546         return String(s).toUpperCase();
8547     },
8548     
8549     /**
8550      * Date sorting
8551      * @param {Mixed} s The value being converted
8552      * @return {Number} The comparison value
8553      */
8554     asDate : function(s) {
8555         if(!s){
8556             return 0;
8557         }
8558         if(s instanceof Date){
8559             return s.getTime();
8560         }
8561         return Date.parse(String(s));
8562     },
8563     
8564     /**
8565      * Float sorting
8566      * @param {Mixed} s The value being converted
8567      * @return {Float} The comparison value
8568      */
8569     asFloat : function(s) {
8570         var val = parseFloat(String(s).replace(/,/g, ""));
8571         if(isNaN(val)) val = 0;
8572         return val;
8573     },
8574     
8575     /**
8576      * Integer sorting
8577      * @param {Mixed} s The value being converted
8578      * @return {Number} The comparison value
8579      */
8580     asInt : function(s) {
8581         var val = parseInt(String(s).replace(/,/g, ""));
8582         if(isNaN(val)) val = 0;
8583         return val;
8584     }
8585 };/*
8586  * Based on:
8587  * Ext JS Library 1.1.1
8588  * Copyright(c) 2006-2007, Ext JS, LLC.
8589  *
8590  * Originally Released Under LGPL - original licence link has changed is not relivant.
8591  *
8592  * Fork - LGPL
8593  * <script type="text/javascript">
8594  */
8595
8596 /**
8597 * @class Roo.data.Record
8598  * Instances of this class encapsulate both record <em>definition</em> information, and record
8599  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8600  * to access Records cached in an {@link Roo.data.Store} object.<br>
8601  * <p>
8602  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8603  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8604  * objects.<br>
8605  * <p>
8606  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8607  * @constructor
8608  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8609  * {@link #create}. The parameters are the same.
8610  * @param {Array} data An associative Array of data values keyed by the field name.
8611  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8612  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8613  * not specified an integer id is generated.
8614  */
8615 Roo.data.Record = function(data, id){
8616     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8617     this.data = data;
8618 };
8619
8620 /**
8621  * Generate a constructor for a specific record layout.
8622  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8623  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8624  * Each field definition object may contain the following properties: <ul>
8625  * <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,
8626  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8627  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8628  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8629  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8630  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8631  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8632  * this may be omitted.</p></li>
8633  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8634  * <ul><li>auto (Default, implies no conversion)</li>
8635  * <li>string</li>
8636  * <li>int</li>
8637  * <li>float</li>
8638  * <li>boolean</li>
8639  * <li>date</li></ul></p></li>
8640  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8641  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8642  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8643  * by the Reader into an object that will be stored in the Record. It is passed the
8644  * following parameters:<ul>
8645  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8646  * </ul></p></li>
8647  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8648  * </ul>
8649  * <br>usage:<br><pre><code>
8650 var TopicRecord = Roo.data.Record.create(
8651     {name: 'title', mapping: 'topic_title'},
8652     {name: 'author', mapping: 'username'},
8653     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8654     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8655     {name: 'lastPoster', mapping: 'user2'},
8656     {name: 'excerpt', mapping: 'post_text'}
8657 );
8658
8659 var myNewRecord = new TopicRecord({
8660     title: 'Do my job please',
8661     author: 'noobie',
8662     totalPosts: 1,
8663     lastPost: new Date(),
8664     lastPoster: 'Animal',
8665     excerpt: 'No way dude!'
8666 });
8667 myStore.add(myNewRecord);
8668 </code></pre>
8669  * @method create
8670  * @static
8671  */
8672 Roo.data.Record.create = function(o){
8673     var f = function(){
8674         f.superclass.constructor.apply(this, arguments);
8675     };
8676     Roo.extend(f, Roo.data.Record);
8677     var p = f.prototype;
8678     p.fields = new Roo.util.MixedCollection(false, function(field){
8679         return field.name;
8680     });
8681     for(var i = 0, len = o.length; i < len; i++){
8682         p.fields.add(new Roo.data.Field(o[i]));
8683     }
8684     f.getField = function(name){
8685         return p.fields.get(name);  
8686     };
8687     return f;
8688 };
8689
8690 Roo.data.Record.AUTO_ID = 1000;
8691 Roo.data.Record.EDIT = 'edit';
8692 Roo.data.Record.REJECT = 'reject';
8693 Roo.data.Record.COMMIT = 'commit';
8694
8695 Roo.data.Record.prototype = {
8696     /**
8697      * Readonly flag - true if this record has been modified.
8698      * @type Boolean
8699      */
8700     dirty : false,
8701     editing : false,
8702     error: null,
8703     modified: null,
8704
8705     // private
8706     join : function(store){
8707         this.store = store;
8708     },
8709
8710     /**
8711      * Set the named field to the specified value.
8712      * @param {String} name The name of the field to set.
8713      * @param {Object} value The value to set the field to.
8714      */
8715     set : function(name, value){
8716         if(this.data[name] == value){
8717             return;
8718         }
8719         this.dirty = true;
8720         if(!this.modified){
8721             this.modified = {};
8722         }
8723         if(typeof this.modified[name] == 'undefined'){
8724             this.modified[name] = this.data[name];
8725         }
8726         this.data[name] = value;
8727         if(!this.editing && this.store){
8728             this.store.afterEdit(this);
8729         }       
8730     },
8731
8732     /**
8733      * Get the value of the named field.
8734      * @param {String} name The name of the field to get the value of.
8735      * @return {Object} The value of the field.
8736      */
8737     get : function(name){
8738         return this.data[name]; 
8739     },
8740
8741     // private
8742     beginEdit : function(){
8743         this.editing = true;
8744         this.modified = {}; 
8745     },
8746
8747     // private
8748     cancelEdit : function(){
8749         this.editing = false;
8750         delete this.modified;
8751     },
8752
8753     // private
8754     endEdit : function(){
8755         this.editing = false;
8756         if(this.dirty && this.store){
8757             this.store.afterEdit(this);
8758         }
8759     },
8760
8761     /**
8762      * Usually called by the {@link Roo.data.Store} which owns the Record.
8763      * Rejects all changes made to the Record since either creation, or the last commit operation.
8764      * Modified fields are reverted to their original values.
8765      * <p>
8766      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8767      * of reject operations.
8768      */
8769     reject : function(){
8770         var m = this.modified;
8771         for(var n in m){
8772             if(typeof m[n] != "function"){
8773                 this.data[n] = m[n];
8774             }
8775         }
8776         this.dirty = false;
8777         delete this.modified;
8778         this.editing = false;
8779         if(this.store){
8780             this.store.afterReject(this);
8781         }
8782     },
8783
8784     /**
8785      * Usually called by the {@link Roo.data.Store} which owns the Record.
8786      * Commits all changes made to the Record since either creation, or the last commit operation.
8787      * <p>
8788      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8789      * of commit operations.
8790      */
8791     commit : function(){
8792         this.dirty = false;
8793         delete this.modified;
8794         this.editing = false;
8795         if(this.store){
8796             this.store.afterCommit(this);
8797         }
8798     },
8799
8800     // private
8801     hasError : function(){
8802         return this.error != null;
8803     },
8804
8805     // private
8806     clearError : function(){
8807         this.error = null;
8808     },
8809
8810     /**
8811      * Creates a copy of this record.
8812      * @param {String} id (optional) A new record id if you don't want to use this record's id
8813      * @return {Record}
8814      */
8815     copy : function(newId) {
8816         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8817     }
8818 };/*
8819  * Based on:
8820  * Ext JS Library 1.1.1
8821  * Copyright(c) 2006-2007, Ext JS, LLC.
8822  *
8823  * Originally Released Under LGPL - original licence link has changed is not relivant.
8824  *
8825  * Fork - LGPL
8826  * <script type="text/javascript">
8827  */
8828
8829
8830
8831 /**
8832  * @class Roo.data.Store
8833  * @extends Roo.util.Observable
8834  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8835  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8836  * <p>
8837  * 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
8838  * has no knowledge of the format of the data returned by the Proxy.<br>
8839  * <p>
8840  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8841  * instances from the data object. These records are cached and made available through accessor functions.
8842  * @constructor
8843  * Creates a new Store.
8844  * @param {Object} config A config object containing the objects needed for the Store to access data,
8845  * and read the data into Records.
8846  */
8847 Roo.data.Store = function(config){
8848     this.data = new Roo.util.MixedCollection(false);
8849     this.data.getKey = function(o){
8850         return o.id;
8851     };
8852     this.baseParams = {};
8853     // private
8854     this.paramNames = {
8855         "start" : "start",
8856         "limit" : "limit",
8857         "sort" : "sort",
8858         "dir" : "dir",
8859         "multisort" : "_multisort"
8860     };
8861
8862     if(config && config.data){
8863         this.inlineData = config.data;
8864         delete config.data;
8865     }
8866
8867     Roo.apply(this, config);
8868     
8869     if(this.reader){ // reader passed
8870         this.reader = Roo.factory(this.reader, Roo.data);
8871         this.reader.xmodule = this.xmodule || false;
8872         if(!this.recordType){
8873             this.recordType = this.reader.recordType;
8874         }
8875         if(this.reader.onMetaChange){
8876             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8877         }
8878     }
8879
8880     if(this.recordType){
8881         this.fields = this.recordType.prototype.fields;
8882     }
8883     this.modified = [];
8884
8885     this.addEvents({
8886         /**
8887          * @event datachanged
8888          * Fires when the data cache has changed, and a widget which is using this Store
8889          * as a Record cache should refresh its view.
8890          * @param {Store} this
8891          */
8892         datachanged : true,
8893         /**
8894          * @event metachange
8895          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8896          * @param {Store} this
8897          * @param {Object} meta The JSON metadata
8898          */
8899         metachange : true,
8900         /**
8901          * @event add
8902          * Fires when Records have been added to the Store
8903          * @param {Store} this
8904          * @param {Roo.data.Record[]} records The array of Records added
8905          * @param {Number} index The index at which the record(s) were added
8906          */
8907         add : true,
8908         /**
8909          * @event remove
8910          * Fires when a Record has been removed from the Store
8911          * @param {Store} this
8912          * @param {Roo.data.Record} record The Record that was removed
8913          * @param {Number} index The index at which the record was removed
8914          */
8915         remove : true,
8916         /**
8917          * @event update
8918          * Fires when a Record has been updated
8919          * @param {Store} this
8920          * @param {Roo.data.Record} record The Record that was updated
8921          * @param {String} operation The update operation being performed.  Value may be one of:
8922          * <pre><code>
8923  Roo.data.Record.EDIT
8924  Roo.data.Record.REJECT
8925  Roo.data.Record.COMMIT
8926          * </code></pre>
8927          */
8928         update : true,
8929         /**
8930          * @event clear
8931          * Fires when the data cache has been cleared.
8932          * @param {Store} this
8933          */
8934         clear : true,
8935         /**
8936          * @event beforeload
8937          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8938          * the load action will be canceled.
8939          * @param {Store} this
8940          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8941          */
8942         beforeload : true,
8943         /**
8944          * @event beforeloadadd
8945          * Fires after a new set of Records has been loaded.
8946          * @param {Store} this
8947          * @param {Roo.data.Record[]} records The Records that were loaded
8948          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8949          */
8950         beforeloadadd : true,
8951         /**
8952          * @event load
8953          * Fires after a new set of Records has been loaded, before they are added to the store.
8954          * @param {Store} this
8955          * @param {Roo.data.Record[]} records The Records that were loaded
8956          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8957          * @params {Object} return from reader
8958          */
8959         load : true,
8960         /**
8961          * @event loadexception
8962          * Fires if an exception occurs in the Proxy during loading.
8963          * Called with the signature of the Proxy's "loadexception" event.
8964          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8965          * 
8966          * @param {Proxy} 
8967          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8968          * @param {Object} load options 
8969          * @param {Object} jsonData from your request (normally this contains the Exception)
8970          */
8971         loadexception : true
8972     });
8973     
8974     if(this.proxy){
8975         this.proxy = Roo.factory(this.proxy, Roo.data);
8976         this.proxy.xmodule = this.xmodule || false;
8977         this.relayEvents(this.proxy,  ["loadexception"]);
8978     }
8979     this.sortToggle = {};
8980     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8981
8982     Roo.data.Store.superclass.constructor.call(this);
8983
8984     if(this.inlineData){
8985         this.loadData(this.inlineData);
8986         delete this.inlineData;
8987     }
8988 };
8989
8990 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8991      /**
8992     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8993     * without a remote query - used by combo/forms at present.
8994     */
8995     
8996     /**
8997     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8998     */
8999     /**
9000     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9001     */
9002     /**
9003     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9004     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9005     */
9006     /**
9007     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9008     * on any HTTP request
9009     */
9010     /**
9011     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9012     */
9013     /**
9014     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9015     */
9016     multiSort: false,
9017     /**
9018     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9019     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9020     */
9021     remoteSort : false,
9022
9023     /**
9024     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9025      * loaded or when a record is removed. (defaults to false).
9026     */
9027     pruneModifiedRecords : false,
9028
9029     // private
9030     lastOptions : null,
9031
9032     /**
9033      * Add Records to the Store and fires the add event.
9034      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9035      */
9036     add : function(records){
9037         records = [].concat(records);
9038         for(var i = 0, len = records.length; i < len; i++){
9039             records[i].join(this);
9040         }
9041         var index = this.data.length;
9042         this.data.addAll(records);
9043         this.fireEvent("add", this, records, index);
9044     },
9045
9046     /**
9047      * Remove a Record from the Store and fires the remove event.
9048      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9049      */
9050     remove : function(record){
9051         var index = this.data.indexOf(record);
9052         this.data.removeAt(index);
9053         if(this.pruneModifiedRecords){
9054             this.modified.remove(record);
9055         }
9056         this.fireEvent("remove", this, record, index);
9057     },
9058
9059     /**
9060      * Remove all Records from the Store and fires the clear event.
9061      */
9062     removeAll : function(){
9063         this.data.clear();
9064         if(this.pruneModifiedRecords){
9065             this.modified = [];
9066         }
9067         this.fireEvent("clear", this);
9068     },
9069
9070     /**
9071      * Inserts Records to the Store at the given index and fires the add event.
9072      * @param {Number} index The start index at which to insert the passed Records.
9073      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9074      */
9075     insert : function(index, records){
9076         records = [].concat(records);
9077         for(var i = 0, len = records.length; i < len; i++){
9078             this.data.insert(index, records[i]);
9079             records[i].join(this);
9080         }
9081         this.fireEvent("add", this, records, index);
9082     },
9083
9084     /**
9085      * Get the index within the cache of the passed Record.
9086      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9087      * @return {Number} The index of the passed Record. Returns -1 if not found.
9088      */
9089     indexOf : function(record){
9090         return this.data.indexOf(record);
9091     },
9092
9093     /**
9094      * Get the index within the cache of the Record with the passed id.
9095      * @param {String} id The id of the Record to find.
9096      * @return {Number} The index of the Record. Returns -1 if not found.
9097      */
9098     indexOfId : function(id){
9099         return this.data.indexOfKey(id);
9100     },
9101
9102     /**
9103      * Get the Record with the specified id.
9104      * @param {String} id The id of the Record to find.
9105      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9106      */
9107     getById : function(id){
9108         return this.data.key(id);
9109     },
9110
9111     /**
9112      * Get the Record at the specified index.
9113      * @param {Number} index The index of the Record to find.
9114      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9115      */
9116     getAt : function(index){
9117         return this.data.itemAt(index);
9118     },
9119
9120     /**
9121      * Returns a range of Records between specified indices.
9122      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9123      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9124      * @return {Roo.data.Record[]} An array of Records
9125      */
9126     getRange : function(start, end){
9127         return this.data.getRange(start, end);
9128     },
9129
9130     // private
9131     storeOptions : function(o){
9132         o = Roo.apply({}, o);
9133         delete o.callback;
9134         delete o.scope;
9135         this.lastOptions = o;
9136     },
9137
9138     /**
9139      * Loads the Record cache from the configured Proxy using the configured Reader.
9140      * <p>
9141      * If using remote paging, then the first load call must specify the <em>start</em>
9142      * and <em>limit</em> properties in the options.params property to establish the initial
9143      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9144      * <p>
9145      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9146      * and this call will return before the new data has been loaded. Perform any post-processing
9147      * in a callback function, or in a "load" event handler.</strong>
9148      * <p>
9149      * @param {Object} options An object containing properties which control loading options:<ul>
9150      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9151      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9152      * passed the following arguments:<ul>
9153      * <li>r : Roo.data.Record[]</li>
9154      * <li>options: Options object from the load call</li>
9155      * <li>success: Boolean success indicator</li></ul></li>
9156      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9157      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9158      * </ul>
9159      */
9160     load : function(options){
9161         options = options || {};
9162         if(this.fireEvent("beforeload", this, options) !== false){
9163             this.storeOptions(options);
9164             var p = Roo.apply(options.params || {}, this.baseParams);
9165             // if meta was not loaded from remote source.. try requesting it.
9166             if (!this.reader.metaFromRemote) {
9167                 p._requestMeta = 1;
9168             }
9169             if(this.sortInfo && this.remoteSort){
9170                 var pn = this.paramNames;
9171                 p[pn["sort"]] = this.sortInfo.field;
9172                 p[pn["dir"]] = this.sortInfo.direction;
9173             }
9174             if (this.multiSort) {
9175                 var pn = this.paramNames;
9176                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9177             }
9178             
9179             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9180         }
9181     },
9182
9183     /**
9184      * Reloads the Record cache from the configured Proxy using the configured Reader and
9185      * the options from the last load operation performed.
9186      * @param {Object} options (optional) An object containing properties which may override the options
9187      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9188      * the most recently used options are reused).
9189      */
9190     reload : function(options){
9191         this.load(Roo.applyIf(options||{}, this.lastOptions));
9192     },
9193
9194     // private
9195     // Called as a callback by the Reader during a load operation.
9196     loadRecords : function(o, options, success){
9197         if(!o || success === false){
9198             if(success !== false){
9199                 this.fireEvent("load", this, [], options, o);
9200             }
9201             if(options.callback){
9202                 options.callback.call(options.scope || this, [], options, false);
9203             }
9204             return;
9205         }
9206         // if data returned failure - throw an exception.
9207         if (o.success === false) {
9208             // show a message if no listener is registered.
9209             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9210                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9211             }
9212             // loadmask wil be hooked into this..
9213             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9214             return;
9215         }
9216         var r = o.records, t = o.totalRecords || r.length;
9217         
9218         this.fireEvent("beforeloadadd", this, r, options, o);
9219         
9220         if(!options || options.add !== true){
9221             if(this.pruneModifiedRecords){
9222                 this.modified = [];
9223             }
9224             for(var i = 0, len = r.length; i < len; i++){
9225                 r[i].join(this);
9226             }
9227             if(this.snapshot){
9228                 this.data = this.snapshot;
9229                 delete this.snapshot;
9230             }
9231             this.data.clear();
9232             this.data.addAll(r);
9233             this.totalLength = t;
9234             this.applySort();
9235             this.fireEvent("datachanged", this);
9236         }else{
9237             this.totalLength = Math.max(t, this.data.length+r.length);
9238             this.add(r);
9239         }
9240         this.fireEvent("load", this, r, options, o);
9241         if(options.callback){
9242             options.callback.call(options.scope || this, r, options, true);
9243         }
9244     },
9245
9246
9247     /**
9248      * Loads data from a passed data block. A Reader which understands the format of the data
9249      * must have been configured in the constructor.
9250      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9251      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9252      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9253      */
9254     loadData : function(o, append){
9255         var r = this.reader.readRecords(o);
9256         this.loadRecords(r, {add: append}, true);
9257     },
9258
9259     /**
9260      * Gets the number of cached records.
9261      * <p>
9262      * <em>If using paging, this may not be the total size of the dataset. If the data object
9263      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9264      * the data set size</em>
9265      */
9266     getCount : function(){
9267         return this.data.length || 0;
9268     },
9269
9270     /**
9271      * Gets the total number of records in the dataset as returned by the server.
9272      * <p>
9273      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9274      * the dataset size</em>
9275      */
9276     getTotalCount : function(){
9277         return this.totalLength || 0;
9278     },
9279
9280     /**
9281      * Returns the sort state of the Store as an object with two properties:
9282      * <pre><code>
9283  field {String} The name of the field by which the Records are sorted
9284  direction {String} The sort order, "ASC" or "DESC"
9285      * </code></pre>
9286      */
9287     getSortState : function(){
9288         return this.sortInfo;
9289     },
9290
9291     // private
9292     applySort : function(){
9293         if(this.sortInfo && !this.remoteSort){
9294             var s = this.sortInfo, f = s.field;
9295             var st = this.fields.get(f).sortType;
9296             var fn = function(r1, r2){
9297                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9298                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9299             };
9300             this.data.sort(s.direction, fn);
9301             if(this.snapshot && this.snapshot != this.data){
9302                 this.snapshot.sort(s.direction, fn);
9303             }
9304         }
9305     },
9306
9307     /**
9308      * Sets the default sort column and order to be used by the next load operation.
9309      * @param {String} fieldName The name of the field to sort by.
9310      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9311      */
9312     setDefaultSort : function(field, dir){
9313         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9314     },
9315
9316     /**
9317      * Sort the Records.
9318      * If remote sorting is used, the sort is performed on the server, and the cache is
9319      * reloaded. If local sorting is used, the cache is sorted internally.
9320      * @param {String} fieldName The name of the field to sort by.
9321      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9322      */
9323     sort : function(fieldName, dir){
9324         var f = this.fields.get(fieldName);
9325         if(!dir){
9326             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9327             
9328             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9329                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9330             }else{
9331                 dir = f.sortDir;
9332             }
9333         }
9334         this.sortToggle[f.name] = dir;
9335         this.sortInfo = {field: f.name, direction: dir};
9336         if(!this.remoteSort){
9337             this.applySort();
9338             this.fireEvent("datachanged", this);
9339         }else{
9340             this.load(this.lastOptions);
9341         }
9342     },
9343
9344     /**
9345      * Calls the specified function for each of the Records in the cache.
9346      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9347      * Returning <em>false</em> aborts and exits the iteration.
9348      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9349      */
9350     each : function(fn, scope){
9351         this.data.each(fn, scope);
9352     },
9353
9354     /**
9355      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9356      * (e.g., during paging).
9357      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9358      */
9359     getModifiedRecords : function(){
9360         return this.modified;
9361     },
9362
9363     // private
9364     createFilterFn : function(property, value, anyMatch){
9365         if(!value.exec){ // not a regex
9366             value = String(value);
9367             if(value.length == 0){
9368                 return false;
9369             }
9370             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9371         }
9372         return function(r){
9373             return value.test(r.data[property]);
9374         };
9375     },
9376
9377     /**
9378      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9379      * @param {String} property A field on your records
9380      * @param {Number} start The record index to start at (defaults to 0)
9381      * @param {Number} end The last record index to include (defaults to length - 1)
9382      * @return {Number} The sum
9383      */
9384     sum : function(property, start, end){
9385         var rs = this.data.items, v = 0;
9386         start = start || 0;
9387         end = (end || end === 0) ? end : rs.length-1;
9388
9389         for(var i = start; i <= end; i++){
9390             v += (rs[i].data[property] || 0);
9391         }
9392         return v;
9393     },
9394
9395     /**
9396      * Filter the records by a specified property.
9397      * @param {String} field A field on your records
9398      * @param {String/RegExp} value Either a string that the field
9399      * should start with or a RegExp to test against the field
9400      * @param {Boolean} anyMatch True to match any part not just the beginning
9401      */
9402     filter : function(property, value, anyMatch){
9403         var fn = this.createFilterFn(property, value, anyMatch);
9404         return fn ? this.filterBy(fn) : this.clearFilter();
9405     },
9406
9407     /**
9408      * Filter by a function. The specified function will be called with each
9409      * record in this data source. If the function returns true the record is included,
9410      * otherwise it is filtered.
9411      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9412      * @param {Object} scope (optional) The scope of the function (defaults to this)
9413      */
9414     filterBy : function(fn, scope){
9415         this.snapshot = this.snapshot || this.data;
9416         this.data = this.queryBy(fn, scope||this);
9417         this.fireEvent("datachanged", this);
9418     },
9419
9420     /**
9421      * Query the records by a specified property.
9422      * @param {String} field A field on your records
9423      * @param {String/RegExp} value Either a string that the field
9424      * should start with or a RegExp to test against the field
9425      * @param {Boolean} anyMatch True to match any part not just the beginning
9426      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9427      */
9428     query : function(property, value, anyMatch){
9429         var fn = this.createFilterFn(property, value, anyMatch);
9430         return fn ? this.queryBy(fn) : this.data.clone();
9431     },
9432
9433     /**
9434      * Query by a function. The specified function will be called with each
9435      * record in this data source. If the function returns true the record is included
9436      * in the results.
9437      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9438      * @param {Object} scope (optional) The scope of the function (defaults to this)
9439       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9440      **/
9441     queryBy : function(fn, scope){
9442         var data = this.snapshot || this.data;
9443         return data.filterBy(fn, scope||this);
9444     },
9445
9446     /**
9447      * Collects unique values for a particular dataIndex from this store.
9448      * @param {String} dataIndex The property to collect
9449      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9450      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9451      * @return {Array} An array of the unique values
9452      **/
9453     collect : function(dataIndex, allowNull, bypassFilter){
9454         var d = (bypassFilter === true && this.snapshot) ?
9455                 this.snapshot.items : this.data.items;
9456         var v, sv, r = [], l = {};
9457         for(var i = 0, len = d.length; i < len; i++){
9458             v = d[i].data[dataIndex];
9459             sv = String(v);
9460             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9461                 l[sv] = true;
9462                 r[r.length] = v;
9463             }
9464         }
9465         return r;
9466     },
9467
9468     /**
9469      * Revert to a view of the Record cache with no filtering applied.
9470      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9471      */
9472     clearFilter : function(suppressEvent){
9473         if(this.snapshot && this.snapshot != this.data){
9474             this.data = this.snapshot;
9475             delete this.snapshot;
9476             if(suppressEvent !== true){
9477                 this.fireEvent("datachanged", this);
9478             }
9479         }
9480     },
9481
9482     // private
9483     afterEdit : function(record){
9484         if(this.modified.indexOf(record) == -1){
9485             this.modified.push(record);
9486         }
9487         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9488     },
9489     
9490     // private
9491     afterReject : function(record){
9492         this.modified.remove(record);
9493         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9494     },
9495
9496     // private
9497     afterCommit : function(record){
9498         this.modified.remove(record);
9499         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9500     },
9501
9502     /**
9503      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9504      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9505      */
9506     commitChanges : function(){
9507         var m = this.modified.slice(0);
9508         this.modified = [];
9509         for(var i = 0, len = m.length; i < len; i++){
9510             m[i].commit();
9511         }
9512     },
9513
9514     /**
9515      * Cancel outstanding changes on all changed records.
9516      */
9517     rejectChanges : function(){
9518         var m = this.modified.slice(0);
9519         this.modified = [];
9520         for(var i = 0, len = m.length; i < len; i++){
9521             m[i].reject();
9522         }
9523     },
9524
9525     onMetaChange : function(meta, rtype, o){
9526         this.recordType = rtype;
9527         this.fields = rtype.prototype.fields;
9528         delete this.snapshot;
9529         this.sortInfo = meta.sortInfo || this.sortInfo;
9530         this.modified = [];
9531         this.fireEvent('metachange', this, this.reader.meta);
9532     },
9533     
9534     moveIndex : function(data, type)
9535     {
9536         var index = this.indexOf(data);
9537         
9538         var newIndex = index + type;
9539         
9540         this.remove(data);
9541         
9542         this.insert(newIndex, data);
9543         
9544     }
9545 });/*
9546  * Based on:
9547  * Ext JS Library 1.1.1
9548  * Copyright(c) 2006-2007, Ext JS, LLC.
9549  *
9550  * Originally Released Under LGPL - original licence link has changed is not relivant.
9551  *
9552  * Fork - LGPL
9553  * <script type="text/javascript">
9554  */
9555
9556 /**
9557  * @class Roo.data.SimpleStore
9558  * @extends Roo.data.Store
9559  * Small helper class to make creating Stores from Array data easier.
9560  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9561  * @cfg {Array} fields An array of field definition objects, or field name strings.
9562  * @cfg {Array} data The multi-dimensional array of data
9563  * @constructor
9564  * @param {Object} config
9565  */
9566 Roo.data.SimpleStore = function(config){
9567     Roo.data.SimpleStore.superclass.constructor.call(this, {
9568         isLocal : true,
9569         reader: new Roo.data.ArrayReader({
9570                 id: config.id
9571             },
9572             Roo.data.Record.create(config.fields)
9573         ),
9574         proxy : new Roo.data.MemoryProxy(config.data)
9575     });
9576     this.load();
9577 };
9578 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9579  * Based on:
9580  * Ext JS Library 1.1.1
9581  * Copyright(c) 2006-2007, Ext JS, LLC.
9582  *
9583  * Originally Released Under LGPL - original licence link has changed is not relivant.
9584  *
9585  * Fork - LGPL
9586  * <script type="text/javascript">
9587  */
9588
9589 /**
9590 /**
9591  * @extends Roo.data.Store
9592  * @class Roo.data.JsonStore
9593  * Small helper class to make creating Stores for JSON data easier. <br/>
9594 <pre><code>
9595 var store = new Roo.data.JsonStore({
9596     url: 'get-images.php',
9597     root: 'images',
9598     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9599 });
9600 </code></pre>
9601  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9602  * JsonReader and HttpProxy (unless inline data is provided).</b>
9603  * @cfg {Array} fields An array of field definition objects, or field name strings.
9604  * @constructor
9605  * @param {Object} config
9606  */
9607 Roo.data.JsonStore = function(c){
9608     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9609         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9610         reader: new Roo.data.JsonReader(c, c.fields)
9611     }));
9612 };
9613 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9614  * Based on:
9615  * Ext JS Library 1.1.1
9616  * Copyright(c) 2006-2007, Ext JS, LLC.
9617  *
9618  * Originally Released Under LGPL - original licence link has changed is not relivant.
9619  *
9620  * Fork - LGPL
9621  * <script type="text/javascript">
9622  */
9623
9624  
9625 Roo.data.Field = function(config){
9626     if(typeof config == "string"){
9627         config = {name: config};
9628     }
9629     Roo.apply(this, config);
9630     
9631     if(!this.type){
9632         this.type = "auto";
9633     }
9634     
9635     var st = Roo.data.SortTypes;
9636     // named sortTypes are supported, here we look them up
9637     if(typeof this.sortType == "string"){
9638         this.sortType = st[this.sortType];
9639     }
9640     
9641     // set default sortType for strings and dates
9642     if(!this.sortType){
9643         switch(this.type){
9644             case "string":
9645                 this.sortType = st.asUCString;
9646                 break;
9647             case "date":
9648                 this.sortType = st.asDate;
9649                 break;
9650             default:
9651                 this.sortType = st.none;
9652         }
9653     }
9654
9655     // define once
9656     var stripRe = /[\$,%]/g;
9657
9658     // prebuilt conversion function for this field, instead of
9659     // switching every time we're reading a value
9660     if(!this.convert){
9661         var cv, dateFormat = this.dateFormat;
9662         switch(this.type){
9663             case "":
9664             case "auto":
9665             case undefined:
9666                 cv = function(v){ return v; };
9667                 break;
9668             case "string":
9669                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9670                 break;
9671             case "int":
9672                 cv = function(v){
9673                     return v !== undefined && v !== null && v !== '' ?
9674                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9675                     };
9676                 break;
9677             case "float":
9678                 cv = function(v){
9679                     return v !== undefined && v !== null && v !== '' ?
9680                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9681                     };
9682                 break;
9683             case "bool":
9684             case "boolean":
9685                 cv = function(v){ return v === true || v === "true" || v == 1; };
9686                 break;
9687             case "date":
9688                 cv = function(v){
9689                     if(!v){
9690                         return '';
9691                     }
9692                     if(v instanceof Date){
9693                         return v;
9694                     }
9695                     if(dateFormat){
9696                         if(dateFormat == "timestamp"){
9697                             return new Date(v*1000);
9698                         }
9699                         return Date.parseDate(v, dateFormat);
9700                     }
9701                     var parsed = Date.parse(v);
9702                     return parsed ? new Date(parsed) : null;
9703                 };
9704              break;
9705             
9706         }
9707         this.convert = cv;
9708     }
9709 };
9710
9711 Roo.data.Field.prototype = {
9712     dateFormat: null,
9713     defaultValue: "",
9714     mapping: null,
9715     sortType : null,
9716     sortDir : "ASC"
9717 };/*
9718  * Based on:
9719  * Ext JS Library 1.1.1
9720  * Copyright(c) 2006-2007, Ext JS, LLC.
9721  *
9722  * Originally Released Under LGPL - original licence link has changed is not relivant.
9723  *
9724  * Fork - LGPL
9725  * <script type="text/javascript">
9726  */
9727  
9728 // Base class for reading structured data from a data source.  This class is intended to be
9729 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9730
9731 /**
9732  * @class Roo.data.DataReader
9733  * Base class for reading structured data from a data source.  This class is intended to be
9734  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9735  */
9736
9737 Roo.data.DataReader = function(meta, recordType){
9738     
9739     this.meta = meta;
9740     
9741     this.recordType = recordType instanceof Array ? 
9742         Roo.data.Record.create(recordType) : recordType;
9743 };
9744
9745 Roo.data.DataReader.prototype = {
9746      /**
9747      * Create an empty record
9748      * @param {Object} data (optional) - overlay some values
9749      * @return {Roo.data.Record} record created.
9750      */
9751     newRow :  function(d) {
9752         var da =  {};
9753         this.recordType.prototype.fields.each(function(c) {
9754             switch( c.type) {
9755                 case 'int' : da[c.name] = 0; break;
9756                 case 'date' : da[c.name] = new Date(); break;
9757                 case 'float' : da[c.name] = 0.0; break;
9758                 case 'boolean' : da[c.name] = false; break;
9759                 default : da[c.name] = ""; break;
9760             }
9761             
9762         });
9763         return new this.recordType(Roo.apply(da, d));
9764     }
9765     
9766 };/*
9767  * Based on:
9768  * Ext JS Library 1.1.1
9769  * Copyright(c) 2006-2007, Ext JS, LLC.
9770  *
9771  * Originally Released Under LGPL - original licence link has changed is not relivant.
9772  *
9773  * Fork - LGPL
9774  * <script type="text/javascript">
9775  */
9776
9777 /**
9778  * @class Roo.data.DataProxy
9779  * @extends Roo.data.Observable
9780  * This class is an abstract base class for implementations which provide retrieval of
9781  * unformatted data objects.<br>
9782  * <p>
9783  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9784  * (of the appropriate type which knows how to parse the data object) to provide a block of
9785  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9786  * <p>
9787  * Custom implementations must implement the load method as described in
9788  * {@link Roo.data.HttpProxy#load}.
9789  */
9790 Roo.data.DataProxy = function(){
9791     this.addEvents({
9792         /**
9793          * @event beforeload
9794          * Fires before a network request is made to retrieve a data object.
9795          * @param {Object} This DataProxy object.
9796          * @param {Object} params The params parameter to the load function.
9797          */
9798         beforeload : true,
9799         /**
9800          * @event load
9801          * Fires before the load method's callback is called.
9802          * @param {Object} This DataProxy object.
9803          * @param {Object} o The data object.
9804          * @param {Object} arg The callback argument object passed to the load function.
9805          */
9806         load : true,
9807         /**
9808          * @event loadexception
9809          * Fires if an Exception occurs during data retrieval.
9810          * @param {Object} This DataProxy object.
9811          * @param {Object} o The data object.
9812          * @param {Object} arg The callback argument object passed to the load function.
9813          * @param {Object} e The Exception.
9814          */
9815         loadexception : true
9816     });
9817     Roo.data.DataProxy.superclass.constructor.call(this);
9818 };
9819
9820 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9821
9822     /**
9823      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9824      */
9825 /*
9826  * Based on:
9827  * Ext JS Library 1.1.1
9828  * Copyright(c) 2006-2007, Ext JS, LLC.
9829  *
9830  * Originally Released Under LGPL - original licence link has changed is not relivant.
9831  *
9832  * Fork - LGPL
9833  * <script type="text/javascript">
9834  */
9835 /**
9836  * @class Roo.data.MemoryProxy
9837  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9838  * to the Reader when its load method is called.
9839  * @constructor
9840  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9841  */
9842 Roo.data.MemoryProxy = function(data){
9843     if (data.data) {
9844         data = data.data;
9845     }
9846     Roo.data.MemoryProxy.superclass.constructor.call(this);
9847     this.data = data;
9848 };
9849
9850 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9851     /**
9852      * Load data from the requested source (in this case an in-memory
9853      * data object passed to the constructor), read the data object into
9854      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9855      * process that block using the passed callback.
9856      * @param {Object} params This parameter is not used by the MemoryProxy class.
9857      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9858      * object into a block of Roo.data.Records.
9859      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9860      * The function must be passed <ul>
9861      * <li>The Record block object</li>
9862      * <li>The "arg" argument from the load function</li>
9863      * <li>A boolean success indicator</li>
9864      * </ul>
9865      * @param {Object} scope The scope in which to call the callback
9866      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9867      */
9868     load : function(params, reader, callback, scope, arg){
9869         params = params || {};
9870         var result;
9871         try {
9872             result = reader.readRecords(this.data);
9873         }catch(e){
9874             this.fireEvent("loadexception", this, arg, null, e);
9875             callback.call(scope, null, arg, false);
9876             return;
9877         }
9878         callback.call(scope, result, arg, true);
9879     },
9880     
9881     // private
9882     update : function(params, records){
9883         
9884     }
9885 });/*
9886  * Based on:
9887  * Ext JS Library 1.1.1
9888  * Copyright(c) 2006-2007, Ext JS, LLC.
9889  *
9890  * Originally Released Under LGPL - original licence link has changed is not relivant.
9891  *
9892  * Fork - LGPL
9893  * <script type="text/javascript">
9894  */
9895 /**
9896  * @class Roo.data.HttpProxy
9897  * @extends Roo.data.DataProxy
9898  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9899  * configured to reference a certain URL.<br><br>
9900  * <p>
9901  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9902  * from which the running page was served.<br><br>
9903  * <p>
9904  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9905  * <p>
9906  * Be aware that to enable the browser to parse an XML document, the server must set
9907  * the Content-Type header in the HTTP response to "text/xml".
9908  * @constructor
9909  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9910  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9911  * will be used to make the request.
9912  */
9913 Roo.data.HttpProxy = function(conn){
9914     Roo.data.HttpProxy.superclass.constructor.call(this);
9915     // is conn a conn config or a real conn?
9916     this.conn = conn;
9917     this.useAjax = !conn || !conn.events;
9918   
9919 };
9920
9921 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9922     // thse are take from connection...
9923     
9924     /**
9925      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9926      */
9927     /**
9928      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9929      * extra parameters to each request made by this object. (defaults to undefined)
9930      */
9931     /**
9932      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9933      *  to each request made by this object. (defaults to undefined)
9934      */
9935     /**
9936      * @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)
9937      */
9938     /**
9939      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9940      */
9941      /**
9942      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9943      * @type Boolean
9944      */
9945   
9946
9947     /**
9948      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9949      * @type Boolean
9950      */
9951     /**
9952      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9953      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9954      * a finer-grained basis than the DataProxy events.
9955      */
9956     getConnection : function(){
9957         return this.useAjax ? Roo.Ajax : this.conn;
9958     },
9959
9960     /**
9961      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9962      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9963      * process that block using the passed callback.
9964      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9965      * for the request to the remote server.
9966      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9967      * object into a block of Roo.data.Records.
9968      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9969      * The function must be passed <ul>
9970      * <li>The Record block object</li>
9971      * <li>The "arg" argument from the load function</li>
9972      * <li>A boolean success indicator</li>
9973      * </ul>
9974      * @param {Object} scope The scope in which to call the callback
9975      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9976      */
9977     load : function(params, reader, callback, scope, arg){
9978         if(this.fireEvent("beforeload", this, params) !== false){
9979             var  o = {
9980                 params : params || {},
9981                 request: {
9982                     callback : callback,
9983                     scope : scope,
9984                     arg : arg
9985                 },
9986                 reader: reader,
9987                 callback : this.loadResponse,
9988                 scope: this
9989             };
9990             if(this.useAjax){
9991                 Roo.applyIf(o, this.conn);
9992                 if(this.activeRequest){
9993                     Roo.Ajax.abort(this.activeRequest);
9994                 }
9995                 this.activeRequest = Roo.Ajax.request(o);
9996             }else{
9997                 this.conn.request(o);
9998             }
9999         }else{
10000             callback.call(scope||this, null, arg, false);
10001         }
10002     },
10003
10004     // private
10005     loadResponse : function(o, success, response){
10006         delete this.activeRequest;
10007         if(!success){
10008             this.fireEvent("loadexception", this, o, response);
10009             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10010             return;
10011         }
10012         var result;
10013         try {
10014             result = o.reader.read(response);
10015         }catch(e){
10016             this.fireEvent("loadexception", this, o, response, e);
10017             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10018             return;
10019         }
10020         
10021         this.fireEvent("load", this, o, o.request.arg);
10022         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10023     },
10024
10025     // private
10026     update : function(dataSet){
10027
10028     },
10029
10030     // private
10031     updateResponse : function(dataSet){
10032
10033     }
10034 });/*
10035  * Based on:
10036  * Ext JS Library 1.1.1
10037  * Copyright(c) 2006-2007, Ext JS, LLC.
10038  *
10039  * Originally Released Under LGPL - original licence link has changed is not relivant.
10040  *
10041  * Fork - LGPL
10042  * <script type="text/javascript">
10043  */
10044
10045 /**
10046  * @class Roo.data.ScriptTagProxy
10047  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10048  * other than the originating domain of the running page.<br><br>
10049  * <p>
10050  * <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
10051  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10052  * <p>
10053  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10054  * source code that is used as the source inside a &lt;script> tag.<br><br>
10055  * <p>
10056  * In order for the browser to process the returned data, the server must wrap the data object
10057  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10058  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10059  * depending on whether the callback name was passed:
10060  * <p>
10061  * <pre><code>
10062 boolean scriptTag = false;
10063 String cb = request.getParameter("callback");
10064 if (cb != null) {
10065     scriptTag = true;
10066     response.setContentType("text/javascript");
10067 } else {
10068     response.setContentType("application/x-json");
10069 }
10070 Writer out = response.getWriter();
10071 if (scriptTag) {
10072     out.write(cb + "(");
10073 }
10074 out.print(dataBlock.toJsonString());
10075 if (scriptTag) {
10076     out.write(");");
10077 }
10078 </pre></code>
10079  *
10080  * @constructor
10081  * @param {Object} config A configuration object.
10082  */
10083 Roo.data.ScriptTagProxy = function(config){
10084     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10085     Roo.apply(this, config);
10086     this.head = document.getElementsByTagName("head")[0];
10087 };
10088
10089 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10090
10091 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10092     /**
10093      * @cfg {String} url The URL from which to request the data object.
10094      */
10095     /**
10096      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10097      */
10098     timeout : 30000,
10099     /**
10100      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10101      * the server the name of the callback function set up by the load call to process the returned data object.
10102      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10103      * javascript output which calls this named function passing the data object as its only parameter.
10104      */
10105     callbackParam : "callback",
10106     /**
10107      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10108      * name to the request.
10109      */
10110     nocache : true,
10111
10112     /**
10113      * Load data from the configured URL, read the data object into
10114      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10115      * process that block using the passed callback.
10116      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10117      * for the request to the remote server.
10118      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10119      * object into a block of Roo.data.Records.
10120      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10121      * The function must be passed <ul>
10122      * <li>The Record block object</li>
10123      * <li>The "arg" argument from the load function</li>
10124      * <li>A boolean success indicator</li>
10125      * </ul>
10126      * @param {Object} scope The scope in which to call the callback
10127      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10128      */
10129     load : function(params, reader, callback, scope, arg){
10130         if(this.fireEvent("beforeload", this, params) !== false){
10131
10132             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10133
10134             var url = this.url;
10135             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10136             if(this.nocache){
10137                 url += "&_dc=" + (new Date().getTime());
10138             }
10139             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10140             var trans = {
10141                 id : transId,
10142                 cb : "stcCallback"+transId,
10143                 scriptId : "stcScript"+transId,
10144                 params : params,
10145                 arg : arg,
10146                 url : url,
10147                 callback : callback,
10148                 scope : scope,
10149                 reader : reader
10150             };
10151             var conn = this;
10152
10153             window[trans.cb] = function(o){
10154                 conn.handleResponse(o, trans);
10155             };
10156
10157             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10158
10159             if(this.autoAbort !== false){
10160                 this.abort();
10161             }
10162
10163             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10164
10165             var script = document.createElement("script");
10166             script.setAttribute("src", url);
10167             script.setAttribute("type", "text/javascript");
10168             script.setAttribute("id", trans.scriptId);
10169             this.head.appendChild(script);
10170
10171             this.trans = trans;
10172         }else{
10173             callback.call(scope||this, null, arg, false);
10174         }
10175     },
10176
10177     // private
10178     isLoading : function(){
10179         return this.trans ? true : false;
10180     },
10181
10182     /**
10183      * Abort the current server request.
10184      */
10185     abort : function(){
10186         if(this.isLoading()){
10187             this.destroyTrans(this.trans);
10188         }
10189     },
10190
10191     // private
10192     destroyTrans : function(trans, isLoaded){
10193         this.head.removeChild(document.getElementById(trans.scriptId));
10194         clearTimeout(trans.timeoutId);
10195         if(isLoaded){
10196             window[trans.cb] = undefined;
10197             try{
10198                 delete window[trans.cb];
10199             }catch(e){}
10200         }else{
10201             // if hasn't been loaded, wait for load to remove it to prevent script error
10202             window[trans.cb] = function(){
10203                 window[trans.cb] = undefined;
10204                 try{
10205                     delete window[trans.cb];
10206                 }catch(e){}
10207             };
10208         }
10209     },
10210
10211     // private
10212     handleResponse : function(o, trans){
10213         this.trans = false;
10214         this.destroyTrans(trans, true);
10215         var result;
10216         try {
10217             result = trans.reader.readRecords(o);
10218         }catch(e){
10219             this.fireEvent("loadexception", this, o, trans.arg, e);
10220             trans.callback.call(trans.scope||window, null, trans.arg, false);
10221             return;
10222         }
10223         this.fireEvent("load", this, o, trans.arg);
10224         trans.callback.call(trans.scope||window, result, trans.arg, true);
10225     },
10226
10227     // private
10228     handleFailure : function(trans){
10229         this.trans = false;
10230         this.destroyTrans(trans, false);
10231         this.fireEvent("loadexception", this, null, trans.arg);
10232         trans.callback.call(trans.scope||window, null, trans.arg, false);
10233     }
10234 });/*
10235  * Based on:
10236  * Ext JS Library 1.1.1
10237  * Copyright(c) 2006-2007, Ext JS, LLC.
10238  *
10239  * Originally Released Under LGPL - original licence link has changed is not relivant.
10240  *
10241  * Fork - LGPL
10242  * <script type="text/javascript">
10243  */
10244
10245 /**
10246  * @class Roo.data.JsonReader
10247  * @extends Roo.data.DataReader
10248  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10249  * based on mappings in a provided Roo.data.Record constructor.
10250  * 
10251  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10252  * in the reply previously. 
10253  * 
10254  * <p>
10255  * Example code:
10256  * <pre><code>
10257 var RecordDef = Roo.data.Record.create([
10258     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10259     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10260 ]);
10261 var myReader = new Roo.data.JsonReader({
10262     totalProperty: "results",    // The property which contains the total dataset size (optional)
10263     root: "rows",                // The property which contains an Array of row objects
10264     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10265 }, RecordDef);
10266 </code></pre>
10267  * <p>
10268  * This would consume a JSON file like this:
10269  * <pre><code>
10270 { 'results': 2, 'rows': [
10271     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10272     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10273 }
10274 </code></pre>
10275  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10276  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10277  * paged from the remote server.
10278  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10279  * @cfg {String} root name of the property which contains the Array of row objects.
10280  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10281  * @constructor
10282  * Create a new JsonReader
10283  * @param {Object} meta Metadata configuration options
10284  * @param {Object} recordType Either an Array of field definition objects,
10285  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10286  */
10287 Roo.data.JsonReader = function(meta, recordType){
10288     
10289     meta = meta || {};
10290     // set some defaults:
10291     Roo.applyIf(meta, {
10292         totalProperty: 'total',
10293         successProperty : 'success',
10294         root : 'data',
10295         id : 'id'
10296     });
10297     
10298     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10299 };
10300 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10301     
10302     /**
10303      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10304      * Used by Store query builder to append _requestMeta to params.
10305      * 
10306      */
10307     metaFromRemote : false,
10308     /**
10309      * This method is only used by a DataProxy which has retrieved data from a remote server.
10310      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10311      * @return {Object} data A data block which is used by an Roo.data.Store object as
10312      * a cache of Roo.data.Records.
10313      */
10314     read : function(response){
10315         var json = response.responseText;
10316        
10317         var o = /* eval:var:o */ eval("("+json+")");
10318         if(!o) {
10319             throw {message: "JsonReader.read: Json object not found"};
10320         }
10321         
10322         if(o.metaData){
10323             
10324             delete this.ef;
10325             this.metaFromRemote = true;
10326             this.meta = o.metaData;
10327             this.recordType = Roo.data.Record.create(o.metaData.fields);
10328             this.onMetaChange(this.meta, this.recordType, o);
10329         }
10330         return this.readRecords(o);
10331     },
10332
10333     // private function a store will implement
10334     onMetaChange : function(meta, recordType, o){
10335
10336     },
10337
10338     /**
10339          * @ignore
10340          */
10341     simpleAccess: function(obj, subsc) {
10342         return obj[subsc];
10343     },
10344
10345         /**
10346          * @ignore
10347          */
10348     getJsonAccessor: function(){
10349         var re = /[\[\.]/;
10350         return function(expr) {
10351             try {
10352                 return(re.test(expr))
10353                     ? new Function("obj", "return obj." + expr)
10354                     : function(obj){
10355                         return obj[expr];
10356                     };
10357             } catch(e){}
10358             return Roo.emptyFn;
10359         };
10360     }(),
10361
10362     /**
10363      * Create a data block containing Roo.data.Records from an XML document.
10364      * @param {Object} o An object which contains an Array of row objects in the property specified
10365      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10366      * which contains the total size of the dataset.
10367      * @return {Object} data A data block which is used by an Roo.data.Store object as
10368      * a cache of Roo.data.Records.
10369      */
10370     readRecords : function(o){
10371         /**
10372          * After any data loads, the raw JSON data is available for further custom processing.
10373          * @type Object
10374          */
10375         this.o = o;
10376         var s = this.meta, Record = this.recordType,
10377             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10378
10379 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10380         if (!this.ef) {
10381             if(s.totalProperty) {
10382                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10383                 }
10384                 if(s.successProperty) {
10385                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10386                 }
10387                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10388                 if (s.id) {
10389                         var g = this.getJsonAccessor(s.id);
10390                         this.getId = function(rec) {
10391                                 var r = g(rec);  
10392                                 return (r === undefined || r === "") ? null : r;
10393                         };
10394                 } else {
10395                         this.getId = function(){return null;};
10396                 }
10397             this.ef = [];
10398             for(var jj = 0; jj < fl; jj++){
10399                 f = fi[jj];
10400                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10401                 this.ef[jj] = this.getJsonAccessor(map);
10402             }
10403         }
10404
10405         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10406         if(s.totalProperty){
10407             var vt = parseInt(this.getTotal(o), 10);
10408             if(!isNaN(vt)){
10409                 totalRecords = vt;
10410             }
10411         }
10412         if(s.successProperty){
10413             var vs = this.getSuccess(o);
10414             if(vs === false || vs === 'false'){
10415                 success = false;
10416             }
10417         }
10418         var records = [];
10419         for(var i = 0; i < c; i++){
10420                 var n = root[i];
10421             var values = {};
10422             var id = this.getId(n);
10423             for(var j = 0; j < fl; j++){
10424                 f = fi[j];
10425             var v = this.ef[j](n);
10426             if (!f.convert) {
10427                 Roo.log('missing convert for ' + f.name);
10428                 Roo.log(f);
10429                 continue;
10430             }
10431             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10432             }
10433             var record = new Record(values, id);
10434             record.json = n;
10435             records[i] = record;
10436         }
10437         return {
10438             raw : o,
10439             success : success,
10440             records : records,
10441             totalRecords : totalRecords
10442         };
10443     }
10444 });/*
10445  * Based on:
10446  * Ext JS Library 1.1.1
10447  * Copyright(c) 2006-2007, Ext JS, LLC.
10448  *
10449  * Originally Released Under LGPL - original licence link has changed is not relivant.
10450  *
10451  * Fork - LGPL
10452  * <script type="text/javascript">
10453  */
10454
10455 /**
10456  * @class Roo.data.ArrayReader
10457  * @extends Roo.data.DataReader
10458  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10459  * Each element of that Array represents a row of data fields. The
10460  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10461  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10462  * <p>
10463  * Example code:.
10464  * <pre><code>
10465 var RecordDef = Roo.data.Record.create([
10466     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10467     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10468 ]);
10469 var myReader = new Roo.data.ArrayReader({
10470     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10471 }, RecordDef);
10472 </code></pre>
10473  * <p>
10474  * This would consume an Array like this:
10475  * <pre><code>
10476 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10477   </code></pre>
10478  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10479  * @constructor
10480  * Create a new JsonReader
10481  * @param {Object} meta Metadata configuration options.
10482  * @param {Object} recordType Either an Array of field definition objects
10483  * as specified to {@link Roo.data.Record#create},
10484  * or an {@link Roo.data.Record} object
10485  * created using {@link Roo.data.Record#create}.
10486  */
10487 Roo.data.ArrayReader = function(meta, recordType){
10488     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10489 };
10490
10491 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10492     /**
10493      * Create a data block containing Roo.data.Records from an XML document.
10494      * @param {Object} o An Array of row objects which represents the dataset.
10495      * @return {Object} data A data block which is used by an Roo.data.Store object as
10496      * a cache of Roo.data.Records.
10497      */
10498     readRecords : function(o){
10499         var sid = this.meta ? this.meta.id : null;
10500         var recordType = this.recordType, fields = recordType.prototype.fields;
10501         var records = [];
10502         var root = o;
10503             for(var i = 0; i < root.length; i++){
10504                     var n = root[i];
10505                 var values = {};
10506                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10507                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10508                 var f = fields.items[j];
10509                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10510                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10511                 v = f.convert(v);
10512                 values[f.name] = v;
10513             }
10514                 var record = new recordType(values, id);
10515                 record.json = n;
10516                 records[records.length] = record;
10517             }
10518             return {
10519                 records : records,
10520                 totalRecords : records.length
10521             };
10522     }
10523 });/*
10524  * - LGPL
10525  * * 
10526  */
10527
10528 /**
10529  * @class Roo.bootstrap.ComboBox
10530  * @extends Roo.bootstrap.TriggerField
10531  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10532  * @cfg {Boolean} append (true|false) default false
10533  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10534  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10535  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10536  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10537  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10538  * @constructor
10539  * Create a new ComboBox.
10540  * @param {Object} config Configuration options
10541  */
10542 Roo.bootstrap.ComboBox = function(config){
10543     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10544     this.addEvents({
10545         /**
10546          * @event expand
10547          * Fires when the dropdown list is expanded
10548              * @param {Roo.bootstrap.ComboBox} combo This combo box
10549              */
10550         'expand' : true,
10551         /**
10552          * @event collapse
10553          * Fires when the dropdown list is collapsed
10554              * @param {Roo.bootstrap.ComboBox} combo This combo box
10555              */
10556         'collapse' : true,
10557         /**
10558          * @event beforeselect
10559          * Fires before a list item is selected. Return false to cancel the selection.
10560              * @param {Roo.bootstrap.ComboBox} combo This combo box
10561              * @param {Roo.data.Record} record The data record returned from the underlying store
10562              * @param {Number} index The index of the selected item in the dropdown list
10563              */
10564         'beforeselect' : true,
10565         /**
10566          * @event select
10567          * Fires when a list item is selected
10568              * @param {Roo.bootstrap.ComboBox} combo This combo box
10569              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10570              * @param {Number} index The index of the selected item in the dropdown list
10571              */
10572         'select' : true,
10573         /**
10574          * @event beforequery
10575          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10576          * The event object passed has these properties:
10577              * @param {Roo.bootstrap.ComboBox} combo This combo box
10578              * @param {String} query The query
10579              * @param {Boolean} forceAll true to force "all" query
10580              * @param {Boolean} cancel true to cancel the query
10581              * @param {Object} e The query event object
10582              */
10583         'beforequery': true,
10584          /**
10585          * @event add
10586          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10587              * @param {Roo.bootstrap.ComboBox} combo This combo box
10588              */
10589         'add' : true,
10590         /**
10591          * @event edit
10592          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10593              * @param {Roo.bootstrap.ComboBox} combo This combo box
10594              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10595              */
10596         'edit' : true,
10597         /**
10598          * @event remove
10599          * Fires when the remove value from the combobox array
10600              * @param {Roo.bootstrap.ComboBox} combo This combo box
10601              */
10602         'remove' : true
10603         
10604     });
10605     
10606     this.item = [];
10607     this.tickItems = [];
10608     
10609     this.selectedIndex = -1;
10610     if(this.mode == 'local'){
10611         if(config.queryDelay === undefined){
10612             this.queryDelay = 10;
10613         }
10614         if(config.minChars === undefined){
10615             this.minChars = 0;
10616         }
10617     }
10618 };
10619
10620 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10621      
10622     /**
10623      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10624      * rendering into an Roo.Editor, defaults to false)
10625      */
10626     /**
10627      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10628      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10629      */
10630     /**
10631      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10632      */
10633     /**
10634      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10635      * the dropdown list (defaults to undefined, with no header element)
10636      */
10637
10638      /**
10639      * @cfg {String/Roo.Template} tpl The template to use to render the output
10640      */
10641      
10642      /**
10643      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10644      */
10645     listWidth: undefined,
10646     /**
10647      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10648      * mode = 'remote' or 'text' if mode = 'local')
10649      */
10650     displayField: undefined,
10651     /**
10652      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10653      * mode = 'remote' or 'value' if mode = 'local'). 
10654      * Note: use of a valueField requires the user make a selection
10655      * in order for a value to be mapped.
10656      */
10657     valueField: undefined,
10658     
10659     
10660     /**
10661      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10662      * field's data value (defaults to the underlying DOM element's name)
10663      */
10664     hiddenName: undefined,
10665     /**
10666      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10667      */
10668     listClass: '',
10669     /**
10670      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10671      */
10672     selectedClass: 'active',
10673     
10674     /**
10675      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10676      */
10677     shadow:'sides',
10678     /**
10679      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10680      * anchor positions (defaults to 'tl-bl')
10681      */
10682     listAlign: 'tl-bl?',
10683     /**
10684      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10685      */
10686     maxHeight: 300,
10687     /**
10688      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10689      * query specified by the allQuery config option (defaults to 'query')
10690      */
10691     triggerAction: 'query',
10692     /**
10693      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10694      * (defaults to 4, does not apply if editable = false)
10695      */
10696     minChars : 4,
10697     /**
10698      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10699      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10700      */
10701     typeAhead: false,
10702     /**
10703      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10704      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10705      */
10706     queryDelay: 500,
10707     /**
10708      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10709      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10710      */
10711     pageSize: 0,
10712     /**
10713      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10714      * when editable = true (defaults to false)
10715      */
10716     selectOnFocus:false,
10717     /**
10718      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10719      */
10720     queryParam: 'query',
10721     /**
10722      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10723      * when mode = 'remote' (defaults to 'Loading...')
10724      */
10725     loadingText: 'Loading...',
10726     /**
10727      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10728      */
10729     resizable: false,
10730     /**
10731      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10732      */
10733     handleHeight : 8,
10734     /**
10735      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10736      * traditional select (defaults to true)
10737      */
10738     editable: true,
10739     /**
10740      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10741      */
10742     allQuery: '',
10743     /**
10744      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10745      */
10746     mode: 'remote',
10747     /**
10748      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10749      * listWidth has a higher value)
10750      */
10751     minListWidth : 70,
10752     /**
10753      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10754      * allow the user to set arbitrary text into the field (defaults to false)
10755      */
10756     forceSelection:false,
10757     /**
10758      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10759      * if typeAhead = true (defaults to 250)
10760      */
10761     typeAheadDelay : 250,
10762     /**
10763      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10764      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10765      */
10766     valueNotFoundText : undefined,
10767     /**
10768      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10769      */
10770     blockFocus : false,
10771     
10772     /**
10773      * @cfg {Boolean} disableClear Disable showing of clear button.
10774      */
10775     disableClear : false,
10776     /**
10777      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10778      */
10779     alwaysQuery : false,
10780     
10781     /**
10782      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10783      */
10784     multiple : false,
10785     
10786     //private
10787     addicon : false,
10788     editicon: false,
10789     
10790     page: 0,
10791     hasQuery: false,
10792     append: false,
10793     loadNext: false,
10794     autoFocus : true,
10795     tickable : false,
10796     btnPosition : 'right',
10797     triggerList : true,
10798     showToggleBtn : true,
10799     // element that contains real text value.. (when hidden is used..)
10800     
10801     getAutoCreate : function()
10802     {
10803         var cfg = false;
10804         
10805         /*
10806          *  Normal ComboBox
10807          */
10808         if(!this.tickable){
10809             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10810             return cfg;
10811         }
10812         
10813         /*
10814          *  ComboBox with tickable selections
10815          */
10816              
10817         var align = this.labelAlign || this.parentLabelAlign();
10818         
10819         cfg = {
10820             cls : 'form-group roo-combobox-tickable' //input-group
10821         };
10822         
10823         
10824         var buttons = {
10825             tag : 'div',
10826             cls : 'tickable-buttons',
10827             cn : [
10828                 {
10829                     tag : 'button',
10830                     type : 'button',
10831                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10832                     html : 'Edit'
10833                 },
10834                 {
10835                     tag : 'button',
10836                     type : 'button',
10837                     name : 'ok',
10838                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10839                     html : 'Done'
10840                 },
10841                 {
10842                     tag : 'button',
10843                     type : 'button',
10844                     name : 'cancel',
10845                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10846                     html : 'Cancel'
10847                 }
10848             ]
10849         };
10850         
10851         var _this = this;
10852         Roo.each(buttons.cn, function(c){
10853             if (_this.size) {
10854                 c.cls += ' btn-' + _this.size;
10855             }
10856
10857             if (_this.disabled) {
10858                 c.disabled = true;
10859             }
10860         });
10861         
10862         var box = {
10863             tag: 'div',
10864             cn: [
10865                 {
10866                     tag: 'input',
10867                     type : 'hidden',
10868                     cls: 'form-hidden-field'
10869                 },
10870                 {
10871                     tag: 'ul',
10872                     cls: 'select2-choices',
10873                     cn:[
10874                         {
10875                             tag: 'li',
10876                             cls: 'select2-search-field',
10877                             cn: [
10878
10879                                 buttons
10880                             ]
10881                         }
10882                     ]
10883                 }
10884             ]
10885         }
10886         
10887         var combobox = {
10888             cls: 'select2-container input-group select2-container-multi',
10889             cn: [
10890                 box
10891 //                {
10892 //                    tag: 'ul',
10893 //                    cls: 'typeahead typeahead-long dropdown-menu',
10894 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10895 //                }
10896             ]
10897         };
10898         
10899         if (align ==='left' && this.fieldLabel.length) {
10900             
10901                 Roo.log("left and has label");
10902                 cfg.cn = [
10903                     
10904                     {
10905                         tag: 'label',
10906                         'for' :  id,
10907                         cls : 'control-label col-sm-' + this.labelWidth,
10908                         html : this.fieldLabel
10909                         
10910                     },
10911                     {
10912                         cls : "col-sm-" + (12 - this.labelWidth), 
10913                         cn: [
10914                             combobox
10915                         ]
10916                     }
10917                     
10918                 ];
10919         } else if ( this.fieldLabel.length) {
10920                 Roo.log(" label");
10921                  cfg.cn = [
10922                    
10923                     {
10924                         tag: 'label',
10925                         //cls : 'input-group-addon',
10926                         html : this.fieldLabel
10927                         
10928                     },
10929                     
10930                     combobox
10931                     
10932                 ];
10933
10934         } else {
10935             
10936                 Roo.log(" no label && no align");
10937                 cfg = combobox
10938                      
10939                 
10940         }
10941          
10942         var settings=this;
10943         ['xs','sm','md','lg'].map(function(size){
10944             if (settings[size]) {
10945                 cfg.cls += ' col-' + size + '-' + settings[size];
10946             }
10947         });
10948         
10949         return cfg;
10950         
10951     },
10952     
10953     // private
10954     initEvents: function()
10955     {
10956         
10957         if (!this.store) {
10958             throw "can not find store for combo";
10959         }
10960         this.store = Roo.factory(this.store, Roo.data);
10961         
10962         if(this.tickable){
10963             this.initTickableEvents();
10964             return;
10965         }
10966         
10967         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10968         
10969         if(this.hiddenName){
10970             
10971             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10972             
10973             this.hiddenField.dom.value =
10974                 this.hiddenValue !== undefined ? this.hiddenValue :
10975                 this.value !== undefined ? this.value : '';
10976
10977             // prevent input submission
10978             this.el.dom.removeAttribute('name');
10979             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10980              
10981              
10982         }
10983         //if(Roo.isGecko){
10984         //    this.el.dom.setAttribute('autocomplete', 'off');
10985         //}
10986         
10987         var cls = 'x-combo-list';
10988         
10989         //this.list = new Roo.Layer({
10990         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10991         //});
10992         
10993         var _this = this;
10994         
10995         (function(){
10996             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10997             _this.list.setWidth(lw);
10998         }).defer(100);
10999         
11000         this.list.on('mouseover', this.onViewOver, this);
11001         this.list.on('mousemove', this.onViewMove, this);
11002         
11003         this.list.on('scroll', this.onViewScroll, this);
11004         
11005         /*
11006         this.list.swallowEvent('mousewheel');
11007         this.assetHeight = 0;
11008
11009         if(this.title){
11010             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11011             this.assetHeight += this.header.getHeight();
11012         }
11013
11014         this.innerList = this.list.createChild({cls:cls+'-inner'});
11015         this.innerList.on('mouseover', this.onViewOver, this);
11016         this.innerList.on('mousemove', this.onViewMove, this);
11017         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11018         
11019         if(this.allowBlank && !this.pageSize && !this.disableClear){
11020             this.footer = this.list.createChild({cls:cls+'-ft'});
11021             this.pageTb = new Roo.Toolbar(this.footer);
11022            
11023         }
11024         if(this.pageSize){
11025             this.footer = this.list.createChild({cls:cls+'-ft'});
11026             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11027                     {pageSize: this.pageSize});
11028             
11029         }
11030         
11031         if (this.pageTb && this.allowBlank && !this.disableClear) {
11032             var _this = this;
11033             this.pageTb.add(new Roo.Toolbar.Fill(), {
11034                 cls: 'x-btn-icon x-btn-clear',
11035                 text: '&#160;',
11036                 handler: function()
11037                 {
11038                     _this.collapse();
11039                     _this.clearValue();
11040                     _this.onSelect(false, -1);
11041                 }
11042             });
11043         }
11044         if (this.footer) {
11045             this.assetHeight += this.footer.getHeight();
11046         }
11047         */
11048             
11049         if(!this.tpl){
11050             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11051         }
11052
11053         this.view = new Roo.View(this.list, this.tpl, {
11054             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11055         });
11056         //this.view.wrapEl.setDisplayed(false);
11057         this.view.on('click', this.onViewClick, this);
11058         
11059         
11060         
11061         this.store.on('beforeload', this.onBeforeLoad, this);
11062         this.store.on('load', this.onLoad, this);
11063         this.store.on('loadexception', this.onLoadException, this);
11064         /*
11065         if(this.resizable){
11066             this.resizer = new Roo.Resizable(this.list,  {
11067                pinned:true, handles:'se'
11068             });
11069             this.resizer.on('resize', function(r, w, h){
11070                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11071                 this.listWidth = w;
11072                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11073                 this.restrictHeight();
11074             }, this);
11075             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11076         }
11077         */
11078         if(!this.editable){
11079             this.editable = true;
11080             this.setEditable(false);
11081         }
11082         
11083         /*
11084         
11085         if (typeof(this.events.add.listeners) != 'undefined') {
11086             
11087             this.addicon = this.wrap.createChild(
11088                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11089        
11090             this.addicon.on('click', function(e) {
11091                 this.fireEvent('add', this);
11092             }, this);
11093         }
11094         if (typeof(this.events.edit.listeners) != 'undefined') {
11095             
11096             this.editicon = this.wrap.createChild(
11097                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11098             if (this.addicon) {
11099                 this.editicon.setStyle('margin-left', '40px');
11100             }
11101             this.editicon.on('click', function(e) {
11102                 
11103                 // we fire even  if inothing is selected..
11104                 this.fireEvent('edit', this, this.lastData );
11105                 
11106             }, this);
11107         }
11108         */
11109         
11110         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11111             "up" : function(e){
11112                 this.inKeyMode = true;
11113                 this.selectPrev();
11114             },
11115
11116             "down" : function(e){
11117                 if(!this.isExpanded()){
11118                     this.onTriggerClick();
11119                 }else{
11120                     this.inKeyMode = true;
11121                     this.selectNext();
11122                 }
11123             },
11124
11125             "enter" : function(e){
11126 //                this.onViewClick();
11127                 //return true;
11128                 this.collapse();
11129                 
11130                 if(this.fireEvent("specialkey", this, e)){
11131                     this.onViewClick(false);
11132                 }
11133                 
11134                 return true;
11135             },
11136
11137             "esc" : function(e){
11138                 this.collapse();
11139             },
11140
11141             "tab" : function(e){
11142                 this.collapse();
11143                 
11144                 if(this.fireEvent("specialkey", this, e)){
11145                     this.onViewClick(false);
11146                 }
11147                 
11148                 return true;
11149             },
11150
11151             scope : this,
11152
11153             doRelay : function(foo, bar, hname){
11154                 if(hname == 'down' || this.scope.isExpanded()){
11155                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11156                 }
11157                 return true;
11158             },
11159
11160             forceKeyDown: true
11161         });
11162         
11163         
11164         this.queryDelay = Math.max(this.queryDelay || 10,
11165                 this.mode == 'local' ? 10 : 250);
11166         
11167         
11168         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11169         
11170         if(this.typeAhead){
11171             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11172         }
11173         if(this.editable !== false){
11174             this.inputEl().on("keyup", this.onKeyUp, this);
11175         }
11176         if(this.forceSelection){
11177             this.inputEl().on('blur', this.doForce, this);
11178         }
11179         
11180         if(this.multiple){
11181             this.choices = this.el.select('ul.select2-choices', true).first();
11182             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11183         }
11184     },
11185     
11186     initTickableEvents: function()
11187     {   
11188         this.createList();
11189         
11190         if(this.hiddenName){
11191             
11192             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11193             
11194             this.hiddenField.dom.value =
11195                 this.hiddenValue !== undefined ? this.hiddenValue :
11196                 this.value !== undefined ? this.value : '';
11197
11198             // prevent input submission
11199             this.el.dom.removeAttribute('name');
11200             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11201              
11202              
11203         }
11204         
11205 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11206         
11207         this.choices = this.el.select('ul.select2-choices', true).first();
11208         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11209         if(this.triggerList){
11210             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11211         }
11212          
11213         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11214         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11215         
11216         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11217         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11218         
11219         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11220         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11221         
11222         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11223         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11224         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11225         
11226         this.okBtn.hide();
11227         this.cancelBtn.hide();
11228         
11229         var _this = this;
11230         
11231         (function(){
11232             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11233             _this.list.setWidth(lw);
11234         }).defer(100);
11235         
11236         this.list.on('mouseover', this.onViewOver, this);
11237         this.list.on('mousemove', this.onViewMove, this);
11238         
11239         this.list.on('scroll', this.onViewScroll, this);
11240         
11241         if(!this.tpl){
11242             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>';
11243         }
11244
11245         this.view = new Roo.View(this.list, this.tpl, {
11246             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11247         });
11248         
11249         //this.view.wrapEl.setDisplayed(false);
11250         this.view.on('click', this.onViewClick, this);
11251         
11252         
11253         
11254         this.store.on('beforeload', this.onBeforeLoad, this);
11255         this.store.on('load', this.onLoad, this);
11256         this.store.on('loadexception', this.onLoadException, this);
11257         
11258 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11259 //            "up" : function(e){
11260 //                this.inKeyMode = true;
11261 //                this.selectPrev();
11262 //            },
11263 //
11264 //            "down" : function(e){
11265 //                if(!this.isExpanded()){
11266 //                    this.onTriggerClick();
11267 //                }else{
11268 //                    this.inKeyMode = true;
11269 //                    this.selectNext();
11270 //                }
11271 //            },
11272 //
11273 //            "enter" : function(e){
11274 ////                this.onViewClick();
11275 //                //return true;
11276 //                this.collapse();
11277 //                
11278 //                if(this.fireEvent("specialkey", this, e)){
11279 //                    this.onViewClick(false);
11280 //                }
11281 //                
11282 //                return true;
11283 //            },
11284 //
11285 //            "esc" : function(e){
11286 //                this.collapse();
11287 //            },
11288 //
11289 //            "tab" : function(e){
11290 //                this.collapse();
11291 //                
11292 //                if(this.fireEvent("specialkey", this, e)){
11293 //                    this.onViewClick(false);
11294 //                }
11295 //                
11296 //                return true;
11297 //            },
11298 //
11299 //            scope : this,
11300 //
11301 //            doRelay : function(foo, bar, hname){
11302 //                if(hname == 'down' || this.scope.isExpanded()){
11303 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11304 //                }
11305 //                return true;
11306 //            },
11307 //
11308 //            forceKeyDown: true
11309 //        });
11310         
11311         
11312         this.queryDelay = Math.max(this.queryDelay || 10,
11313                 this.mode == 'local' ? 10 : 250);
11314         
11315         
11316         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11317         
11318         if(this.typeAhead){
11319             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11320         }
11321         
11322     },
11323
11324     onDestroy : function(){
11325         if(this.view){
11326             this.view.setStore(null);
11327             this.view.el.removeAllListeners();
11328             this.view.el.remove();
11329             this.view.purgeListeners();
11330         }
11331         if(this.list){
11332             this.list.dom.innerHTML  = '';
11333         }
11334         
11335         if(this.store){
11336             this.store.un('beforeload', this.onBeforeLoad, this);
11337             this.store.un('load', this.onLoad, this);
11338             this.store.un('loadexception', this.onLoadException, this);
11339         }
11340         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11341     },
11342
11343     // private
11344     fireKey : function(e){
11345         if(e.isNavKeyPress() && !this.list.isVisible()){
11346             this.fireEvent("specialkey", this, e);
11347         }
11348     },
11349
11350     // private
11351     onResize: function(w, h){
11352 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11353 //        
11354 //        if(typeof w != 'number'){
11355 //            // we do not handle it!?!?
11356 //            return;
11357 //        }
11358 //        var tw = this.trigger.getWidth();
11359 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11360 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11361 //        var x = w - tw;
11362 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11363 //            
11364 //        //this.trigger.setStyle('left', x+'px');
11365 //        
11366 //        if(this.list && this.listWidth === undefined){
11367 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11368 //            this.list.setWidth(lw);
11369 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11370 //        }
11371         
11372     
11373         
11374     },
11375
11376     /**
11377      * Allow or prevent the user from directly editing the field text.  If false is passed,
11378      * the user will only be able to select from the items defined in the dropdown list.  This method
11379      * is the runtime equivalent of setting the 'editable' config option at config time.
11380      * @param {Boolean} value True to allow the user to directly edit the field text
11381      */
11382     setEditable : function(value){
11383         if(value == this.editable){
11384             return;
11385         }
11386         this.editable = value;
11387         if(!value){
11388             this.inputEl().dom.setAttribute('readOnly', true);
11389             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11390             this.inputEl().addClass('x-combo-noedit');
11391         }else{
11392             this.inputEl().dom.setAttribute('readOnly', false);
11393             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11394             this.inputEl().removeClass('x-combo-noedit');
11395         }
11396     },
11397
11398     // private
11399     
11400     onBeforeLoad : function(combo,opts){
11401         if(!this.hasFocus){
11402             return;
11403         }
11404          if (!opts.add) {
11405             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11406          }
11407         this.restrictHeight();
11408         this.selectedIndex = -1;
11409     },
11410
11411     // private
11412     onLoad : function(){
11413         
11414         this.hasQuery = false;
11415         
11416         if(!this.hasFocus){
11417             return;
11418         }
11419         
11420         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11421             this.loading.hide();
11422         }
11423         
11424         if(this.store.getCount() > 0){
11425             this.expand();
11426 //            this.restrictHeight();
11427             if(this.lastQuery == this.allQuery){
11428                 if(this.editable && !this.tickable){
11429                     this.inputEl().dom.select();
11430                 }
11431                 
11432                 if(
11433                     !this.selectByValue(this.value, true) &&
11434                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11435                     this.store.lastOptions.add != true)
11436                 ){
11437                     this.select(0, true);
11438                 }
11439             }else{
11440                 if(this.autoFocus){
11441                     this.selectNext();
11442                 }
11443                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11444                     this.taTask.delay(this.typeAheadDelay);
11445                 }
11446             }
11447         }else{
11448             this.onEmptyResults();
11449         }
11450         
11451         //this.el.focus();
11452     },
11453     // private
11454     onLoadException : function()
11455     {
11456         this.hasQuery = false;
11457         
11458         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11459             this.loading.hide();
11460         }
11461         
11462         this.collapse();
11463         Roo.log(this.store.reader.jsonData);
11464         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11465             // fixme
11466             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11467         }
11468         
11469         
11470     },
11471     // private
11472     onTypeAhead : function(){
11473         if(this.store.getCount() > 0){
11474             var r = this.store.getAt(0);
11475             var newValue = r.data[this.displayField];
11476             var len = newValue.length;
11477             var selStart = this.getRawValue().length;
11478             
11479             if(selStart != len){
11480                 this.setRawValue(newValue);
11481                 this.selectText(selStart, newValue.length);
11482             }
11483         }
11484     },
11485
11486     // private
11487     onSelect : function(record, index){
11488         
11489         if(this.fireEvent('beforeselect', this, record, index) !== false){
11490         
11491             this.setFromData(index > -1 ? record.data : false);
11492             
11493             this.collapse();
11494             this.fireEvent('select', this, record, index);
11495         }
11496     },
11497
11498     /**
11499      * Returns the currently selected field value or empty string if no value is set.
11500      * @return {String} value The selected value
11501      */
11502     getValue : function(){
11503         
11504         if(this.multiple){
11505             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11506         }
11507         
11508         if(this.valueField){
11509             return typeof this.value != 'undefined' ? this.value : '';
11510         }else{
11511             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11512         }
11513     },
11514
11515     /**
11516      * Clears any text/value currently set in the field
11517      */
11518     clearValue : function(){
11519         if(this.hiddenField){
11520             this.hiddenField.dom.value = '';
11521         }
11522         this.value = '';
11523         this.setRawValue('');
11524         this.lastSelectionText = '';
11525         this.lastData = false;
11526         
11527     },
11528
11529     /**
11530      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11531      * will be displayed in the field.  If the value does not match the data value of an existing item,
11532      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11533      * Otherwise the field will be blank (although the value will still be set).
11534      * @param {String} value The value to match
11535      */
11536     setValue : function(v){
11537         if(this.multiple){
11538             this.syncValue();
11539             return;
11540         }
11541         
11542         var text = v;
11543         if(this.valueField){
11544             var r = this.findRecord(this.valueField, v);
11545             if(r){
11546                 text = r.data[this.displayField];
11547             }else if(this.valueNotFoundText !== undefined){
11548                 text = this.valueNotFoundText;
11549             }
11550         }
11551         this.lastSelectionText = text;
11552         if(this.hiddenField){
11553             this.hiddenField.dom.value = v;
11554         }
11555         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11556         this.value = v;
11557     },
11558     /**
11559      * @property {Object} the last set data for the element
11560      */
11561     
11562     lastData : false,
11563     /**
11564      * Sets the value of the field based on a object which is related to the record format for the store.
11565      * @param {Object} value the value to set as. or false on reset?
11566      */
11567     setFromData : function(o){
11568         
11569         if(this.multiple){
11570             this.addItem(o);
11571             return;
11572         }
11573             
11574         var dv = ''; // display value
11575         var vv = ''; // value value..
11576         this.lastData = o;
11577         if (this.displayField) {
11578             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11579         } else {
11580             // this is an error condition!!!
11581             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11582         }
11583         
11584         if(this.valueField){
11585             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11586         }
11587         
11588         if(this.hiddenField){
11589             this.hiddenField.dom.value = vv;
11590             
11591             this.lastSelectionText = dv;
11592             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11593             this.value = vv;
11594             return;
11595         }
11596         // no hidden field.. - we store the value in 'value', but still display
11597         // display field!!!!
11598         this.lastSelectionText = dv;
11599         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11600         this.value = vv;
11601         
11602         
11603     },
11604     // private
11605     reset : function(){
11606         // overridden so that last data is reset..
11607         this.setValue(this.originalValue);
11608         this.clearInvalid();
11609         this.lastData = false;
11610         if (this.view) {
11611             this.view.clearSelections();
11612         }
11613     },
11614     // private
11615     findRecord : function(prop, value){
11616         var record;
11617         if(this.store.getCount() > 0){
11618             this.store.each(function(r){
11619                 if(r.data[prop] == value){
11620                     record = r;
11621                     return false;
11622                 }
11623                 return true;
11624             });
11625         }
11626         return record;
11627     },
11628     
11629     getName: function()
11630     {
11631         // returns hidden if it's set..
11632         if (!this.rendered) {return ''};
11633         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11634         
11635     },
11636     // private
11637     onViewMove : function(e, t){
11638         this.inKeyMode = false;
11639     },
11640
11641     // private
11642     onViewOver : function(e, t){
11643         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11644             return;
11645         }
11646         var item = this.view.findItemFromChild(t);
11647         
11648         if(item){
11649             var index = this.view.indexOf(item);
11650             this.select(index, false);
11651         }
11652     },
11653
11654     // private
11655     onViewClick : function(view, doFocus, el, e)
11656     {
11657         var index = this.view.getSelectedIndexes()[0];
11658         
11659         var r = this.store.getAt(index);
11660         
11661         if(this.tickable){
11662             
11663             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11664                 return;
11665             }
11666             
11667             var rm = false;
11668             var _this = this;
11669             
11670             Roo.each(this.tickItems, function(v,k){
11671                 
11672                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11673                     _this.tickItems.splice(k, 1);
11674                     rm = true;
11675                     return;
11676                 }
11677             });
11678             
11679             if(rm){
11680                 return;
11681             }
11682             
11683             this.tickItems.push(r.data);
11684             return;
11685         }
11686         
11687         if(r){
11688             this.onSelect(r, index);
11689         }
11690         if(doFocus !== false && !this.blockFocus){
11691             this.inputEl().focus();
11692         }
11693     },
11694
11695     // private
11696     restrictHeight : function(){
11697         //this.innerList.dom.style.height = '';
11698         //var inner = this.innerList.dom;
11699         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11700         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11701         //this.list.beginUpdate();
11702         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11703         this.list.alignTo(this.inputEl(), this.listAlign);
11704         this.list.alignTo(this.inputEl(), this.listAlign);
11705         //this.list.endUpdate();
11706     },
11707
11708     // private
11709     onEmptyResults : function(){
11710         this.collapse();
11711     },
11712
11713     /**
11714      * Returns true if the dropdown list is expanded, else false.
11715      */
11716     isExpanded : function(){
11717         return this.list.isVisible();
11718     },
11719
11720     /**
11721      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11722      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11723      * @param {String} value The data value of the item to select
11724      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11725      * selected item if it is not currently in view (defaults to true)
11726      * @return {Boolean} True if the value matched an item in the list, else false
11727      */
11728     selectByValue : function(v, scrollIntoView){
11729         if(v !== undefined && v !== null){
11730             var r = this.findRecord(this.valueField || this.displayField, v);
11731             if(r){
11732                 this.select(this.store.indexOf(r), scrollIntoView);
11733                 return true;
11734             }
11735         }
11736         return false;
11737     },
11738
11739     /**
11740      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11741      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11742      * @param {Number} index The zero-based index of the list item to select
11743      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11744      * selected item if it is not currently in view (defaults to true)
11745      */
11746     select : function(index, scrollIntoView){
11747         this.selectedIndex = index;
11748         this.view.select(index);
11749         if(scrollIntoView !== false){
11750             var el = this.view.getNode(index);
11751             if(el && !this.multiple && !this.tickable){
11752                 this.list.scrollChildIntoView(el, false);
11753             }
11754         }
11755     },
11756
11757     // private
11758     selectNext : function(){
11759         var ct = this.store.getCount();
11760         if(ct > 0){
11761             if(this.selectedIndex == -1){
11762                 this.select(0);
11763             }else if(this.selectedIndex < ct-1){
11764                 this.select(this.selectedIndex+1);
11765             }
11766         }
11767     },
11768
11769     // private
11770     selectPrev : function(){
11771         var ct = this.store.getCount();
11772         if(ct > 0){
11773             if(this.selectedIndex == -1){
11774                 this.select(0);
11775             }else if(this.selectedIndex != 0){
11776                 this.select(this.selectedIndex-1);
11777             }
11778         }
11779     },
11780
11781     // private
11782     onKeyUp : function(e){
11783         if(this.editable !== false && !e.isSpecialKey()){
11784             this.lastKey = e.getKey();
11785             this.dqTask.delay(this.queryDelay);
11786         }
11787     },
11788
11789     // private
11790     validateBlur : function(){
11791         return !this.list || !this.list.isVisible();   
11792     },
11793
11794     // private
11795     initQuery : function(){
11796         this.doQuery(this.getRawValue());
11797     },
11798
11799     // private
11800     doForce : function(){
11801         if(this.inputEl().dom.value.length > 0){
11802             this.inputEl().dom.value =
11803                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11804              
11805         }
11806     },
11807
11808     /**
11809      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11810      * query allowing the query action to be canceled if needed.
11811      * @param {String} query The SQL query to execute
11812      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11813      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11814      * saved in the current store (defaults to false)
11815      */
11816     doQuery : function(q, forceAll){
11817         
11818         if(q === undefined || q === null){
11819             q = '';
11820         }
11821         var qe = {
11822             query: q,
11823             forceAll: forceAll,
11824             combo: this,
11825             cancel:false
11826         };
11827         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11828             return false;
11829         }
11830         q = qe.query;
11831         
11832         forceAll = qe.forceAll;
11833         if(forceAll === true || (q.length >= this.minChars)){
11834             
11835             this.hasQuery = true;
11836             
11837             if(this.lastQuery != q || this.alwaysQuery){
11838                 this.lastQuery = q;
11839                 if(this.mode == 'local'){
11840                     this.selectedIndex = -1;
11841                     if(forceAll){
11842                         this.store.clearFilter();
11843                     }else{
11844                         this.store.filter(this.displayField, q);
11845                     }
11846                     this.onLoad();
11847                 }else{
11848                     this.store.baseParams[this.queryParam] = q;
11849                     
11850                     var options = {params : this.getParams(q)};
11851                     
11852                     if(this.loadNext){
11853                         options.add = true;
11854                         options.params.start = this.page * this.pageSize;
11855                     }
11856                     
11857                     this.store.load(options);
11858                     /*
11859                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11860                      *  we should expand the list on onLoad
11861                      *  so command out it
11862                      */
11863 //                    this.expand();
11864                 }
11865             }else{
11866                 this.selectedIndex = -1;
11867                 this.onLoad();   
11868             }
11869         }
11870         
11871         this.loadNext = false;
11872     },
11873
11874     // private
11875     getParams : function(q){
11876         var p = {};
11877         //p[this.queryParam] = q;
11878         
11879         if(this.pageSize){
11880             p.start = 0;
11881             p.limit = this.pageSize;
11882         }
11883         return p;
11884     },
11885
11886     /**
11887      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11888      */
11889     collapse : function(){
11890         if(!this.isExpanded()){
11891             return;
11892         }
11893         
11894         this.list.hide();
11895         
11896         if(this.tickable){
11897             this.hasFocus = false;
11898             this.okBtn.hide();
11899             this.cancelBtn.hide();
11900             this.trigger.show();
11901         }
11902         
11903         Roo.get(document).un('mousedown', this.collapseIf, this);
11904         Roo.get(document).un('mousewheel', this.collapseIf, this);
11905         if (!this.editable) {
11906             Roo.get(document).un('keydown', this.listKeyPress, this);
11907         }
11908         this.fireEvent('collapse', this);
11909     },
11910
11911     // private
11912     collapseIf : function(e){
11913         var in_combo  = e.within(this.el);
11914         var in_list =  e.within(this.list);
11915         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11916         
11917         if (in_combo || in_list || is_list) {
11918             //e.stopPropagation();
11919             return;
11920         }
11921         
11922         if(this.tickable){
11923             this.onTickableFooterButtonClick(e, false, false);
11924         }
11925
11926         this.collapse();
11927         
11928     },
11929
11930     /**
11931      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11932      */
11933     expand : function(){
11934        
11935         if(this.isExpanded() || !this.hasFocus){
11936             return;
11937         }
11938         
11939         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11940         this.list.setWidth(lw);
11941         
11942         
11943          Roo.log('expand');
11944         
11945         this.list.show();
11946         
11947         this.restrictHeight();
11948         
11949         if(this.tickable){
11950             
11951             this.tickItems = Roo.apply([], this.item);
11952             
11953             this.okBtn.show();
11954             this.cancelBtn.show();
11955             this.trigger.hide();
11956             
11957         }
11958         
11959         Roo.get(document).on('mousedown', this.collapseIf, this);
11960         Roo.get(document).on('mousewheel', this.collapseIf, this);
11961         if (!this.editable) {
11962             Roo.get(document).on('keydown', this.listKeyPress, this);
11963         }
11964         
11965         this.fireEvent('expand', this);
11966     },
11967
11968     // private
11969     // Implements the default empty TriggerField.onTriggerClick function
11970     onTriggerClick : function(e)
11971     {
11972         Roo.log('trigger click');
11973         
11974         if(this.disabled || !this.triggerList){
11975             return;
11976         }
11977         
11978         this.page = 0;
11979         this.loadNext = false;
11980         
11981         if(this.isExpanded()){
11982             this.collapse();
11983             if (!this.blockFocus) {
11984                 this.inputEl().focus();
11985             }
11986             
11987         }else {
11988             this.hasFocus = true;
11989             if(this.triggerAction == 'all') {
11990                 this.doQuery(this.allQuery, true);
11991             } else {
11992                 this.doQuery(this.getRawValue());
11993             }
11994             if (!this.blockFocus) {
11995                 this.inputEl().focus();
11996             }
11997         }
11998     },
11999     
12000     onTickableTriggerClick : function(e)
12001     {
12002         if(this.disabled){
12003             return;
12004         }
12005         
12006         this.page = 0;
12007         this.loadNext = false;
12008         this.hasFocus = true;
12009         
12010         if(this.triggerAction == 'all') {
12011             this.doQuery(this.allQuery, true);
12012         } else {
12013             this.doQuery(this.getRawValue());
12014         }
12015     },
12016     
12017     onSearchFieldClick : function(e)
12018     {
12019         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12020             this.onTickableFooterButtonClick(e, false, false);
12021             return;
12022         }
12023         
12024         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12025             return;
12026         }
12027         
12028         this.page = 0;
12029         this.loadNext = false;
12030         this.hasFocus = true;
12031         
12032         if(this.triggerAction == 'all') {
12033             this.doQuery(this.allQuery, true);
12034         } else {
12035             this.doQuery(this.getRawValue());
12036         }
12037     },
12038     
12039     listKeyPress : function(e)
12040     {
12041         //Roo.log('listkeypress');
12042         // scroll to first matching element based on key pres..
12043         if (e.isSpecialKey()) {
12044             return false;
12045         }
12046         var k = String.fromCharCode(e.getKey()).toUpperCase();
12047         //Roo.log(k);
12048         var match  = false;
12049         var csel = this.view.getSelectedNodes();
12050         var cselitem = false;
12051         if (csel.length) {
12052             var ix = this.view.indexOf(csel[0]);
12053             cselitem  = this.store.getAt(ix);
12054             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12055                 cselitem = false;
12056             }
12057             
12058         }
12059         
12060         this.store.each(function(v) { 
12061             if (cselitem) {
12062                 // start at existing selection.
12063                 if (cselitem.id == v.id) {
12064                     cselitem = false;
12065                 }
12066                 return true;
12067             }
12068                 
12069             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12070                 match = this.store.indexOf(v);
12071                 return false;
12072             }
12073             return true;
12074         }, this);
12075         
12076         if (match === false) {
12077             return true; // no more action?
12078         }
12079         // scroll to?
12080         this.view.select(match);
12081         var sn = Roo.get(this.view.getSelectedNodes()[0])
12082         sn.scrollIntoView(sn.dom.parentNode, false);
12083     },
12084     
12085     onViewScroll : function(e, t){
12086         
12087         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){
12088             return;
12089         }
12090         
12091         this.hasQuery = true;
12092         
12093         this.loading = this.list.select('.loading', true).first();
12094         
12095         if(this.loading === null){
12096             this.list.createChild({
12097                 tag: 'div',
12098                 cls: 'loading select2-more-results select2-active',
12099                 html: 'Loading more results...'
12100             })
12101             
12102             this.loading = this.list.select('.loading', true).first();
12103             
12104             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12105             
12106             this.loading.hide();
12107         }
12108         
12109         this.loading.show();
12110         
12111         var _combo = this;
12112         
12113         this.page++;
12114         this.loadNext = true;
12115         
12116         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12117         
12118         return;
12119     },
12120     
12121     addItem : function(o)
12122     {   
12123         var dv = ''; // display value
12124         
12125         if (this.displayField) {
12126             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12127         } else {
12128             // this is an error condition!!!
12129             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12130         }
12131         
12132         if(!dv.length){
12133             return;
12134         }
12135         
12136         var choice = this.choices.createChild({
12137             tag: 'li',
12138             cls: 'select2-search-choice',
12139             cn: [
12140                 {
12141                     tag: 'div',
12142                     html: dv
12143                 },
12144                 {
12145                     tag: 'a',
12146                     href: '#',
12147                     cls: 'select2-search-choice-close',
12148                     tabindex: '-1'
12149                 }
12150             ]
12151             
12152         }, this.searchField);
12153         
12154         var close = choice.select('a.select2-search-choice-close', true).first()
12155         
12156         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12157         
12158         this.item.push(o);
12159         
12160         this.lastData = o;
12161         
12162         this.syncValue();
12163         
12164         this.inputEl().dom.value = '';
12165         
12166     },
12167     
12168     onRemoveItem : function(e, _self, o)
12169     {
12170         e.preventDefault();
12171         
12172         this.lastItem = Roo.apply([], this.item);
12173         
12174         var index = this.item.indexOf(o.data) * 1;
12175         
12176         if( index < 0){
12177             Roo.log('not this item?!');
12178             return;
12179         }
12180         
12181         this.item.splice(index, 1);
12182         o.item.remove();
12183         
12184         this.syncValue();
12185         
12186         this.fireEvent('remove', this, e);
12187         
12188     },
12189     
12190     syncValue : function()
12191     {
12192         if(!this.item.length){
12193             this.clearValue();
12194             return;
12195         }
12196             
12197         var value = [];
12198         var _this = this;
12199         Roo.each(this.item, function(i){
12200             if(_this.valueField){
12201                 value.push(i[_this.valueField]);
12202                 return;
12203             }
12204
12205             value.push(i);
12206         });
12207
12208         this.value = value.join(',');
12209
12210         if(this.hiddenField){
12211             this.hiddenField.dom.value = this.value;
12212         }
12213     },
12214     
12215     clearItem : function()
12216     {
12217         if(!this.multiple){
12218             return;
12219         }
12220         
12221         this.item = [];
12222         
12223         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12224            c.remove();
12225         });
12226         
12227         this.syncValue();
12228     },
12229     
12230     inputEl: function ()
12231     {
12232         if(this.tickable){
12233             return this.searchField;
12234         }
12235         return this.el.select('input.form-control',true).first();
12236     },
12237     
12238     
12239     onTickableFooterButtonClick : function(e, btn, el)
12240     {
12241         e.preventDefault();
12242         
12243         this.lastItem = Roo.apply([], this.item);
12244         
12245         if(btn && btn.name == 'cancel'){
12246             this.tickItems = Roo.apply([], this.item);
12247             this.collapse();
12248             return;
12249         }
12250         
12251         this.clearItem();
12252         
12253         var _this = this;
12254         
12255         Roo.each(this.tickItems, function(o){
12256             _this.addItem(o);
12257         });
12258         
12259         this.collapse();
12260         
12261     },
12262     
12263     validate : function()
12264     {
12265         var v = this.getRawValue();
12266         
12267         if(this.multiple){
12268             v = this.getValue();
12269         }
12270         
12271         if(this.disabled || this.validateValue(v)){
12272             this.clearInvalid();
12273             return true;
12274         }
12275         return false;
12276     }
12277     
12278     
12279
12280     /** 
12281     * @cfg {Boolean} grow 
12282     * @hide 
12283     */
12284     /** 
12285     * @cfg {Number} growMin 
12286     * @hide 
12287     */
12288     /** 
12289     * @cfg {Number} growMax 
12290     * @hide 
12291     */
12292     /**
12293      * @hide
12294      * @method autoSize
12295      */
12296 });
12297 /*
12298  * Based on:
12299  * Ext JS Library 1.1.1
12300  * Copyright(c) 2006-2007, Ext JS, LLC.
12301  *
12302  * Originally Released Under LGPL - original licence link has changed is not relivant.
12303  *
12304  * Fork - LGPL
12305  * <script type="text/javascript">
12306  */
12307
12308 /**
12309  * @class Roo.View
12310  * @extends Roo.util.Observable
12311  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12312  * This class also supports single and multi selection modes. <br>
12313  * Create a data model bound view:
12314  <pre><code>
12315  var store = new Roo.data.Store(...);
12316
12317  var view = new Roo.View({
12318     el : "my-element",
12319     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12320  
12321     singleSelect: true,
12322     selectedClass: "ydataview-selected",
12323     store: store
12324  });
12325
12326  // listen for node click?
12327  view.on("click", function(vw, index, node, e){
12328  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12329  });
12330
12331  // load XML data
12332  dataModel.load("foobar.xml");
12333  </code></pre>
12334  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12335  * <br><br>
12336  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12337  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12338  * 
12339  * Note: old style constructor is still suported (container, template, config)
12340  * 
12341  * @constructor
12342  * Create a new View
12343  * @param {Object} config The config object
12344  * 
12345  */
12346 Roo.View = function(config, depreciated_tpl, depreciated_config){
12347     
12348     this.parent = false;
12349     
12350     if (typeof(depreciated_tpl) == 'undefined') {
12351         // new way.. - universal constructor.
12352         Roo.apply(this, config);
12353         this.el  = Roo.get(this.el);
12354     } else {
12355         // old format..
12356         this.el  = Roo.get(config);
12357         this.tpl = depreciated_tpl;
12358         Roo.apply(this, depreciated_config);
12359     }
12360     this.wrapEl  = this.el.wrap().wrap();
12361     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12362     
12363     
12364     if(typeof(this.tpl) == "string"){
12365         this.tpl = new Roo.Template(this.tpl);
12366     } else {
12367         // support xtype ctors..
12368         this.tpl = new Roo.factory(this.tpl, Roo);
12369     }
12370     
12371     
12372     this.tpl.compile();
12373     
12374     /** @private */
12375     this.addEvents({
12376         /**
12377          * @event beforeclick
12378          * Fires before a click is processed. Returns false to cancel the default action.
12379          * @param {Roo.View} this
12380          * @param {Number} index The index of the target node
12381          * @param {HTMLElement} node The target node
12382          * @param {Roo.EventObject} e The raw event object
12383          */
12384             "beforeclick" : true,
12385         /**
12386          * @event click
12387          * Fires when a template node is clicked.
12388          * @param {Roo.View} this
12389          * @param {Number} index The index of the target node
12390          * @param {HTMLElement} node The target node
12391          * @param {Roo.EventObject} e The raw event object
12392          */
12393             "click" : true,
12394         /**
12395          * @event dblclick
12396          * Fires when a template node is double clicked.
12397          * @param {Roo.View} this
12398          * @param {Number} index The index of the target node
12399          * @param {HTMLElement} node The target node
12400          * @param {Roo.EventObject} e The raw event object
12401          */
12402             "dblclick" : true,
12403         /**
12404          * @event contextmenu
12405          * Fires when a template node is right clicked.
12406          * @param {Roo.View} this
12407          * @param {Number} index The index of the target node
12408          * @param {HTMLElement} node The target node
12409          * @param {Roo.EventObject} e The raw event object
12410          */
12411             "contextmenu" : true,
12412         /**
12413          * @event selectionchange
12414          * Fires when the selected nodes change.
12415          * @param {Roo.View} this
12416          * @param {Array} selections Array of the selected nodes
12417          */
12418             "selectionchange" : true,
12419     
12420         /**
12421          * @event beforeselect
12422          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12423          * @param {Roo.View} this
12424          * @param {HTMLElement} node The node to be selected
12425          * @param {Array} selections Array of currently selected nodes
12426          */
12427             "beforeselect" : true,
12428         /**
12429          * @event preparedata
12430          * Fires on every row to render, to allow you to change the data.
12431          * @param {Roo.View} this
12432          * @param {Object} data to be rendered (change this)
12433          */
12434           "preparedata" : true
12435           
12436           
12437         });
12438
12439
12440
12441     this.el.on({
12442         "click": this.onClick,
12443         "dblclick": this.onDblClick,
12444         "contextmenu": this.onContextMenu,
12445         scope:this
12446     });
12447
12448     this.selections = [];
12449     this.nodes = [];
12450     this.cmp = new Roo.CompositeElementLite([]);
12451     if(this.store){
12452         this.store = Roo.factory(this.store, Roo.data);
12453         this.setStore(this.store, true);
12454     }
12455     
12456     if ( this.footer && this.footer.xtype) {
12457            
12458          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12459         
12460         this.footer.dataSource = this.store
12461         this.footer.container = fctr;
12462         this.footer = Roo.factory(this.footer, Roo);
12463         fctr.insertFirst(this.el);
12464         
12465         // this is a bit insane - as the paging toolbar seems to detach the el..
12466 //        dom.parentNode.parentNode.parentNode
12467          // they get detached?
12468     }
12469     
12470     
12471     Roo.View.superclass.constructor.call(this);
12472     
12473     
12474 };
12475
12476 Roo.extend(Roo.View, Roo.util.Observable, {
12477     
12478      /**
12479      * @cfg {Roo.data.Store} store Data store to load data from.
12480      */
12481     store : false,
12482     
12483     /**
12484      * @cfg {String|Roo.Element} el The container element.
12485      */
12486     el : '',
12487     
12488     /**
12489      * @cfg {String|Roo.Template} tpl The template used by this View 
12490      */
12491     tpl : false,
12492     /**
12493      * @cfg {String} dataName the named area of the template to use as the data area
12494      *                          Works with domtemplates roo-name="name"
12495      */
12496     dataName: false,
12497     /**
12498      * @cfg {String} selectedClass The css class to add to selected nodes
12499      */
12500     selectedClass : "x-view-selected",
12501      /**
12502      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12503      */
12504     emptyText : "",
12505     
12506     /**
12507      * @cfg {String} text to display on mask (default Loading)
12508      */
12509     mask : false,
12510     /**
12511      * @cfg {Boolean} multiSelect Allow multiple selection
12512      */
12513     multiSelect : false,
12514     /**
12515      * @cfg {Boolean} singleSelect Allow single selection
12516      */
12517     singleSelect:  false,
12518     
12519     /**
12520      * @cfg {Boolean} toggleSelect - selecting 
12521      */
12522     toggleSelect : false,
12523     
12524     /**
12525      * @cfg {Boolean} tickable - selecting 
12526      */
12527     tickable : false,
12528     
12529     /**
12530      * Returns the element this view is bound to.
12531      * @return {Roo.Element}
12532      */
12533     getEl : function(){
12534         return this.wrapEl;
12535     },
12536     
12537     
12538
12539     /**
12540      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12541      */
12542     refresh : function(){
12543         //Roo.log('refresh');
12544         var t = this.tpl;
12545         
12546         // if we are using something like 'domtemplate', then
12547         // the what gets used is:
12548         // t.applySubtemplate(NAME, data, wrapping data..)
12549         // the outer template then get' applied with
12550         //     the store 'extra data'
12551         // and the body get's added to the
12552         //      roo-name="data" node?
12553         //      <span class='roo-tpl-{name}'></span> ?????
12554         
12555         
12556         
12557         this.clearSelections();
12558         this.el.update("");
12559         var html = [];
12560         var records = this.store.getRange();
12561         if(records.length < 1) {
12562             
12563             // is this valid??  = should it render a template??
12564             
12565             this.el.update(this.emptyText);
12566             return;
12567         }
12568         var el = this.el;
12569         if (this.dataName) {
12570             this.el.update(t.apply(this.store.meta)); //????
12571             el = this.el.child('.roo-tpl-' + this.dataName);
12572         }
12573         
12574         for(var i = 0, len = records.length; i < len; i++){
12575             var data = this.prepareData(records[i].data, i, records[i]);
12576             this.fireEvent("preparedata", this, data, i, records[i]);
12577             
12578             var d = Roo.apply({}, data);
12579             
12580             if(this.tickable){
12581                 Roo.apply(d, {'roo-id' : Roo.id()});
12582                 
12583                 var _this = this;
12584             
12585                 Roo.each(this.parent.item, function(item){
12586                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12587                         return;
12588                     }
12589                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12590                 });
12591             }
12592             
12593             html[html.length] = Roo.util.Format.trim(
12594                 this.dataName ?
12595                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12596                     t.apply(d)
12597             );
12598         }
12599         
12600         
12601         
12602         el.update(html.join(""));
12603         this.nodes = el.dom.childNodes;
12604         this.updateIndexes(0);
12605     },
12606     
12607
12608     /**
12609      * Function to override to reformat the data that is sent to
12610      * the template for each node.
12611      * DEPRICATED - use the preparedata event handler.
12612      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12613      * a JSON object for an UpdateManager bound view).
12614      */
12615     prepareData : function(data, index, record)
12616     {
12617         this.fireEvent("preparedata", this, data, index, record);
12618         return data;
12619     },
12620
12621     onUpdate : function(ds, record){
12622         // Roo.log('on update');   
12623         this.clearSelections();
12624         var index = this.store.indexOf(record);
12625         var n = this.nodes[index];
12626         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12627         n.parentNode.removeChild(n);
12628         this.updateIndexes(index, index);
12629     },
12630
12631     
12632     
12633 // --------- FIXME     
12634     onAdd : function(ds, records, index)
12635     {
12636         //Roo.log(['on Add', ds, records, index] );        
12637         this.clearSelections();
12638         if(this.nodes.length == 0){
12639             this.refresh();
12640             return;
12641         }
12642         var n = this.nodes[index];
12643         for(var i = 0, len = records.length; i < len; i++){
12644             var d = this.prepareData(records[i].data, i, records[i]);
12645             if(n){
12646                 this.tpl.insertBefore(n, d);
12647             }else{
12648                 
12649                 this.tpl.append(this.el, d);
12650             }
12651         }
12652         this.updateIndexes(index);
12653     },
12654
12655     onRemove : function(ds, record, index){
12656        // Roo.log('onRemove');
12657         this.clearSelections();
12658         var el = this.dataName  ?
12659             this.el.child('.roo-tpl-' + this.dataName) :
12660             this.el; 
12661         
12662         el.dom.removeChild(this.nodes[index]);
12663         this.updateIndexes(index);
12664     },
12665
12666     /**
12667      * Refresh an individual node.
12668      * @param {Number} index
12669      */
12670     refreshNode : function(index){
12671         this.onUpdate(this.store, this.store.getAt(index));
12672     },
12673
12674     updateIndexes : function(startIndex, endIndex){
12675         var ns = this.nodes;
12676         startIndex = startIndex || 0;
12677         endIndex = endIndex || ns.length - 1;
12678         for(var i = startIndex; i <= endIndex; i++){
12679             ns[i].nodeIndex = i;
12680         }
12681     },
12682
12683     /**
12684      * Changes the data store this view uses and refresh the view.
12685      * @param {Store} store
12686      */
12687     setStore : function(store, initial){
12688         if(!initial && this.store){
12689             this.store.un("datachanged", this.refresh);
12690             this.store.un("add", this.onAdd);
12691             this.store.un("remove", this.onRemove);
12692             this.store.un("update", this.onUpdate);
12693             this.store.un("clear", this.refresh);
12694             this.store.un("beforeload", this.onBeforeLoad);
12695             this.store.un("load", this.onLoad);
12696             this.store.un("loadexception", this.onLoad);
12697         }
12698         if(store){
12699           
12700             store.on("datachanged", this.refresh, this);
12701             store.on("add", this.onAdd, this);
12702             store.on("remove", this.onRemove, this);
12703             store.on("update", this.onUpdate, this);
12704             store.on("clear", this.refresh, this);
12705             store.on("beforeload", this.onBeforeLoad, this);
12706             store.on("load", this.onLoad, this);
12707             store.on("loadexception", this.onLoad, this);
12708         }
12709         
12710         if(store){
12711             this.refresh();
12712         }
12713     },
12714     /**
12715      * onbeforeLoad - masks the loading area.
12716      *
12717      */
12718     onBeforeLoad : function(store,opts)
12719     {
12720          //Roo.log('onBeforeLoad');   
12721         if (!opts.add) {
12722             this.el.update("");
12723         }
12724         this.el.mask(this.mask ? this.mask : "Loading" ); 
12725     },
12726     onLoad : function ()
12727     {
12728         this.el.unmask();
12729     },
12730     
12731
12732     /**
12733      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12734      * @param {HTMLElement} node
12735      * @return {HTMLElement} The template node
12736      */
12737     findItemFromChild : function(node){
12738         var el = this.dataName  ?
12739             this.el.child('.roo-tpl-' + this.dataName,true) :
12740             this.el.dom; 
12741         
12742         if(!node || node.parentNode == el){
12743                     return node;
12744             }
12745             var p = node.parentNode;
12746             while(p && p != el){
12747             if(p.parentNode == el){
12748                 return p;
12749             }
12750             p = p.parentNode;
12751         }
12752             return null;
12753     },
12754
12755     /** @ignore */
12756     onClick : function(e){
12757         var item = this.findItemFromChild(e.getTarget());
12758         if(item){
12759             var index = this.indexOf(item);
12760             if(this.onItemClick(item, index, e) !== false){
12761                 this.fireEvent("click", this, index, item, e);
12762             }
12763         }else{
12764             this.clearSelections();
12765         }
12766     },
12767
12768     /** @ignore */
12769     onContextMenu : function(e){
12770         var item = this.findItemFromChild(e.getTarget());
12771         if(item){
12772             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12773         }
12774     },
12775
12776     /** @ignore */
12777     onDblClick : function(e){
12778         var item = this.findItemFromChild(e.getTarget());
12779         if(item){
12780             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12781         }
12782     },
12783
12784     onItemClick : function(item, index, e)
12785     {
12786         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12787             return false;
12788         }
12789         if (this.toggleSelect) {
12790             var m = this.isSelected(item) ? 'unselect' : 'select';
12791             //Roo.log(m);
12792             var _t = this;
12793             _t[m](item, true, false);
12794             return true;
12795         }
12796         if(this.multiSelect || this.singleSelect){
12797             if(this.multiSelect && e.shiftKey && this.lastSelection){
12798                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12799             }else{
12800                 this.select(item, this.multiSelect && e.ctrlKey);
12801                 this.lastSelection = item;
12802             }
12803             
12804             if(!this.tickable){
12805                 e.preventDefault();
12806             }
12807             
12808         }
12809         return true;
12810     },
12811
12812     /**
12813      * Get the number of selected nodes.
12814      * @return {Number}
12815      */
12816     getSelectionCount : function(){
12817         return this.selections.length;
12818     },
12819
12820     /**
12821      * Get the currently selected nodes.
12822      * @return {Array} An array of HTMLElements
12823      */
12824     getSelectedNodes : function(){
12825         return this.selections;
12826     },
12827
12828     /**
12829      * Get the indexes of the selected nodes.
12830      * @return {Array}
12831      */
12832     getSelectedIndexes : function(){
12833         var indexes = [], s = this.selections;
12834         for(var i = 0, len = s.length; i < len; i++){
12835             indexes.push(s[i].nodeIndex);
12836         }
12837         return indexes;
12838     },
12839
12840     /**
12841      * Clear all selections
12842      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12843      */
12844     clearSelections : function(suppressEvent){
12845         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12846             this.cmp.elements = this.selections;
12847             this.cmp.removeClass(this.selectedClass);
12848             this.selections = [];
12849             if(!suppressEvent){
12850                 this.fireEvent("selectionchange", this, this.selections);
12851             }
12852         }
12853     },
12854
12855     /**
12856      * Returns true if the passed node is selected
12857      * @param {HTMLElement/Number} node The node or node index
12858      * @return {Boolean}
12859      */
12860     isSelected : function(node){
12861         var s = this.selections;
12862         if(s.length < 1){
12863             return false;
12864         }
12865         node = this.getNode(node);
12866         return s.indexOf(node) !== -1;
12867     },
12868
12869     /**
12870      * Selects nodes.
12871      * @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
12872      * @param {Boolean} keepExisting (optional) true to keep existing selections
12873      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12874      */
12875     select : function(nodeInfo, keepExisting, suppressEvent){
12876         if(nodeInfo instanceof Array){
12877             if(!keepExisting){
12878                 this.clearSelections(true);
12879             }
12880             for(var i = 0, len = nodeInfo.length; i < len; i++){
12881                 this.select(nodeInfo[i], true, true);
12882             }
12883             return;
12884         } 
12885         var node = this.getNode(nodeInfo);
12886         if(!node || this.isSelected(node)){
12887             return; // already selected.
12888         }
12889         if(!keepExisting){
12890             this.clearSelections(true);
12891         }
12892         
12893         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12894             Roo.fly(node).addClass(this.selectedClass);
12895             this.selections.push(node);
12896             if(!suppressEvent){
12897                 this.fireEvent("selectionchange", this, this.selections);
12898             }
12899         }
12900         
12901         
12902     },
12903       /**
12904      * Unselects nodes.
12905      * @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
12906      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12907      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12908      */
12909     unselect : function(nodeInfo, keepExisting, suppressEvent)
12910     {
12911         if(nodeInfo instanceof Array){
12912             Roo.each(this.selections, function(s) {
12913                 this.unselect(s, nodeInfo);
12914             }, this);
12915             return;
12916         }
12917         var node = this.getNode(nodeInfo);
12918         if(!node || !this.isSelected(node)){
12919             //Roo.log("not selected");
12920             return; // not selected.
12921         }
12922         // fireevent???
12923         var ns = [];
12924         Roo.each(this.selections, function(s) {
12925             if (s == node ) {
12926                 Roo.fly(node).removeClass(this.selectedClass);
12927
12928                 return;
12929             }
12930             ns.push(s);
12931         },this);
12932         
12933         this.selections= ns;
12934         this.fireEvent("selectionchange", this, this.selections);
12935     },
12936
12937     /**
12938      * Gets a template node.
12939      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12940      * @return {HTMLElement} The node or null if it wasn't found
12941      */
12942     getNode : function(nodeInfo){
12943         if(typeof nodeInfo == "string"){
12944             return document.getElementById(nodeInfo);
12945         }else if(typeof nodeInfo == "number"){
12946             return this.nodes[nodeInfo];
12947         }
12948         return nodeInfo;
12949     },
12950
12951     /**
12952      * Gets a range template nodes.
12953      * @param {Number} startIndex
12954      * @param {Number} endIndex
12955      * @return {Array} An array of nodes
12956      */
12957     getNodes : function(start, end){
12958         var ns = this.nodes;
12959         start = start || 0;
12960         end = typeof end == "undefined" ? ns.length - 1 : end;
12961         var nodes = [];
12962         if(start <= end){
12963             for(var i = start; i <= end; i++){
12964                 nodes.push(ns[i]);
12965             }
12966         } else{
12967             for(var i = start; i >= end; i--){
12968                 nodes.push(ns[i]);
12969             }
12970         }
12971         return nodes;
12972     },
12973
12974     /**
12975      * Finds the index of the passed node
12976      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12977      * @return {Number} The index of the node or -1
12978      */
12979     indexOf : function(node){
12980         node = this.getNode(node);
12981         if(typeof node.nodeIndex == "number"){
12982             return node.nodeIndex;
12983         }
12984         var ns = this.nodes;
12985         for(var i = 0, len = ns.length; i < len; i++){
12986             if(ns[i] == node){
12987                 return i;
12988             }
12989         }
12990         return -1;
12991     }
12992 });
12993 /*
12994  * - LGPL
12995  *
12996  * based on jquery fullcalendar
12997  * 
12998  */
12999
13000 Roo.bootstrap = Roo.bootstrap || {};
13001 /**
13002  * @class Roo.bootstrap.Calendar
13003  * @extends Roo.bootstrap.Component
13004  * Bootstrap Calendar class
13005  * @cfg {Boolean} loadMask (true|false) default false
13006  * @cfg {Object} header generate the user specific header of the calendar, default false
13007
13008  * @constructor
13009  * Create a new Container
13010  * @param {Object} config The config object
13011  */
13012
13013
13014
13015 Roo.bootstrap.Calendar = function(config){
13016     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13017      this.addEvents({
13018         /**
13019              * @event select
13020              * Fires when a date is selected
13021              * @param {DatePicker} this
13022              * @param {Date} date The selected date
13023              */
13024         'select': true,
13025         /**
13026              * @event monthchange
13027              * Fires when the displayed month changes 
13028              * @param {DatePicker} this
13029              * @param {Date} date The selected month
13030              */
13031         'monthchange': true,
13032         /**
13033              * @event evententer
13034              * Fires when mouse over an event
13035              * @param {Calendar} this
13036              * @param {event} Event
13037              */
13038         'evententer': true,
13039         /**
13040              * @event eventleave
13041              * Fires when the mouse leaves an
13042              * @param {Calendar} this
13043              * @param {event}
13044              */
13045         'eventleave': true,
13046         /**
13047              * @event eventclick
13048              * Fires when the mouse click an
13049              * @param {Calendar} this
13050              * @param {event}
13051              */
13052         'eventclick': true
13053         
13054     });
13055
13056 };
13057
13058 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13059     
13060      /**
13061      * @cfg {Number} startDay
13062      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13063      */
13064     startDay : 0,
13065     
13066     loadMask : false,
13067     
13068     header : false,
13069       
13070     getAutoCreate : function(){
13071         
13072         
13073         var fc_button = function(name, corner, style, content ) {
13074             return Roo.apply({},{
13075                 tag : 'span',
13076                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13077                          (corner.length ?
13078                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13079                             ''
13080                         ),
13081                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13082                 unselectable: 'on'
13083             });
13084         };
13085         
13086         var header = {};
13087         
13088         if(!this.header){
13089             header = {
13090                 tag : 'table',
13091                 cls : 'fc-header',
13092                 style : 'width:100%',
13093                 cn : [
13094                     {
13095                         tag: 'tr',
13096                         cn : [
13097                             {
13098                                 tag : 'td',
13099                                 cls : 'fc-header-left',
13100                                 cn : [
13101                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13102                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13103                                     { tag: 'span', cls: 'fc-header-space' },
13104                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13105
13106
13107                                 ]
13108                             },
13109
13110                             {
13111                                 tag : 'td',
13112                                 cls : 'fc-header-center',
13113                                 cn : [
13114                                     {
13115                                         tag: 'span',
13116                                         cls: 'fc-header-title',
13117                                         cn : {
13118                                             tag: 'H2',
13119                                             html : 'month / year'
13120                                         }
13121                                     }
13122
13123                                 ]
13124                             },
13125                             {
13126                                 tag : 'td',
13127                                 cls : 'fc-header-right',
13128                                 cn : [
13129                               /*      fc_button('month', 'left', '', 'month' ),
13130                                     fc_button('week', '', '', 'week' ),
13131                                     fc_button('day', 'right', '', 'day' )
13132                                 */    
13133
13134                                 ]
13135                             }
13136
13137                         ]
13138                     }
13139                 ]
13140             };
13141         }
13142         
13143         header = this.header;
13144         
13145        
13146         var cal_heads = function() {
13147             var ret = [];
13148             // fixme - handle this.
13149             
13150             for (var i =0; i < Date.dayNames.length; i++) {
13151                 var d = Date.dayNames[i];
13152                 ret.push({
13153                     tag: 'th',
13154                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13155                     html : d.substring(0,3)
13156                 });
13157                 
13158             }
13159             ret[0].cls += ' fc-first';
13160             ret[6].cls += ' fc-last';
13161             return ret;
13162         };
13163         var cal_cell = function(n) {
13164             return  {
13165                 tag: 'td',
13166                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13167                 cn : [
13168                     {
13169                         cn : [
13170                             {
13171                                 cls: 'fc-day-number',
13172                                 html: 'D'
13173                             },
13174                             {
13175                                 cls: 'fc-day-content',
13176                              
13177                                 cn : [
13178                                      {
13179                                         style: 'position: relative;' // height: 17px;
13180                                     }
13181                                 ]
13182                             }
13183                             
13184                             
13185                         ]
13186                     }
13187                 ]
13188                 
13189             }
13190         };
13191         var cal_rows = function() {
13192             
13193             var ret = [];
13194             for (var r = 0; r < 6; r++) {
13195                 var row= {
13196                     tag : 'tr',
13197                     cls : 'fc-week',
13198                     cn : []
13199                 };
13200                 
13201                 for (var i =0; i < Date.dayNames.length; i++) {
13202                     var d = Date.dayNames[i];
13203                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13204
13205                 }
13206                 row.cn[0].cls+=' fc-first';
13207                 row.cn[0].cn[0].style = 'min-height:90px';
13208                 row.cn[6].cls+=' fc-last';
13209                 ret.push(row);
13210                 
13211             }
13212             ret[0].cls += ' fc-first';
13213             ret[4].cls += ' fc-prev-last';
13214             ret[5].cls += ' fc-last';
13215             return ret;
13216             
13217         };
13218         
13219         var cal_table = {
13220             tag: 'table',
13221             cls: 'fc-border-separate',
13222             style : 'width:100%',
13223             cellspacing  : 0,
13224             cn : [
13225                 { 
13226                     tag: 'thead',
13227                     cn : [
13228                         { 
13229                             tag: 'tr',
13230                             cls : 'fc-first fc-last',
13231                             cn : cal_heads()
13232                         }
13233                     ]
13234                 },
13235                 { 
13236                     tag: 'tbody',
13237                     cn : cal_rows()
13238                 }
13239                   
13240             ]
13241         };
13242          
13243          var cfg = {
13244             cls : 'fc fc-ltr',
13245             cn : [
13246                 header,
13247                 {
13248                     cls : 'fc-content',
13249                     style : "position: relative;",
13250                     cn : [
13251                         {
13252                             cls : 'fc-view fc-view-month fc-grid',
13253                             style : 'position: relative',
13254                             unselectable : 'on',
13255                             cn : [
13256                                 {
13257                                     cls : 'fc-event-container',
13258                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13259                                 },
13260                                 cal_table
13261                             ]
13262                         }
13263                     ]
13264     
13265                 }
13266            ] 
13267             
13268         };
13269         
13270          
13271         
13272         return cfg;
13273     },
13274     
13275     
13276     initEvents : function()
13277     {
13278         if(!this.store){
13279             throw "can not find store for calendar";
13280         }
13281         
13282         var mark = {
13283             tag: "div",
13284             cls:"x-dlg-mask",
13285             style: "text-align:center",
13286             cn: [
13287                 {
13288                     tag: "div",
13289                     style: "background-color:white;width:50%;margin:250 auto",
13290                     cn: [
13291                         {
13292                             tag: "img",
13293                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13294                         },
13295                         {
13296                             tag: "span",
13297                             html: "Loading"
13298                         }
13299                         
13300                     ]
13301                 }
13302             ]
13303         }
13304         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13305         
13306         var size = this.el.select('.fc-content', true).first().getSize();
13307         this.maskEl.setSize(size.width, size.height);
13308         this.maskEl.enableDisplayMode("block");
13309         if(!this.loadMask){
13310             this.maskEl.hide();
13311         }
13312         
13313         this.store = Roo.factory(this.store, Roo.data);
13314         this.store.on('load', this.onLoad, this);
13315         this.store.on('beforeload', this.onBeforeLoad, this);
13316         
13317         this.resize();
13318         
13319         this.cells = this.el.select('.fc-day',true);
13320         //Roo.log(this.cells);
13321         this.textNodes = this.el.query('.fc-day-number');
13322         this.cells.addClassOnOver('fc-state-hover');
13323         
13324         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13325         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13326         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13327         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13328         
13329         this.on('monthchange', this.onMonthChange, this);
13330         
13331         this.update(new Date().clearTime());
13332     },
13333     
13334     resize : function() {
13335         var sz  = this.el.getSize();
13336         
13337         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13338         this.el.select('.fc-day-content div',true).setHeight(34);
13339     },
13340     
13341     
13342     // private
13343     showPrevMonth : function(e){
13344         this.update(this.activeDate.add("mo", -1));
13345     },
13346     showToday : function(e){
13347         this.update(new Date().clearTime());
13348     },
13349     // private
13350     showNextMonth : function(e){
13351         this.update(this.activeDate.add("mo", 1));
13352     },
13353
13354     // private
13355     showPrevYear : function(){
13356         this.update(this.activeDate.add("y", -1));
13357     },
13358
13359     // private
13360     showNextYear : function(){
13361         this.update(this.activeDate.add("y", 1));
13362     },
13363
13364     
13365    // private
13366     update : function(date)
13367     {
13368         var vd = this.activeDate;
13369         this.activeDate = date;
13370 //        if(vd && this.el){
13371 //            var t = date.getTime();
13372 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13373 //                Roo.log('using add remove');
13374 //                
13375 //                this.fireEvent('monthchange', this, date);
13376 //                
13377 //                this.cells.removeClass("fc-state-highlight");
13378 //                this.cells.each(function(c){
13379 //                   if(c.dateValue == t){
13380 //                       c.addClass("fc-state-highlight");
13381 //                       setTimeout(function(){
13382 //                            try{c.dom.firstChild.focus();}catch(e){}
13383 //                       }, 50);
13384 //                       return false;
13385 //                   }
13386 //                   return true;
13387 //                });
13388 //                return;
13389 //            }
13390 //        }
13391         
13392         var days = date.getDaysInMonth();
13393         
13394         var firstOfMonth = date.getFirstDateOfMonth();
13395         var startingPos = firstOfMonth.getDay()-this.startDay;
13396         
13397         if(startingPos < this.startDay){
13398             startingPos += 7;
13399         }
13400         
13401         var pm = date.add(Date.MONTH, -1);
13402         var prevStart = pm.getDaysInMonth()-startingPos;
13403 //        
13404         this.cells = this.el.select('.fc-day',true);
13405         this.textNodes = this.el.query('.fc-day-number');
13406         this.cells.addClassOnOver('fc-state-hover');
13407         
13408         var cells = this.cells.elements;
13409         var textEls = this.textNodes;
13410         
13411         Roo.each(cells, function(cell){
13412             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13413         });
13414         
13415         days += startingPos;
13416
13417         // convert everything to numbers so it's fast
13418         var day = 86400000;
13419         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13420         //Roo.log(d);
13421         //Roo.log(pm);
13422         //Roo.log(prevStart);
13423         
13424         var today = new Date().clearTime().getTime();
13425         var sel = date.clearTime().getTime();
13426         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13427         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13428         var ddMatch = this.disabledDatesRE;
13429         var ddText = this.disabledDatesText;
13430         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13431         var ddaysText = this.disabledDaysText;
13432         var format = this.format;
13433         
13434         var setCellClass = function(cal, cell){
13435             cell.row = 0;
13436             cell.events = [];
13437             cell.more = [];
13438             //Roo.log('set Cell Class');
13439             cell.title = "";
13440             var t = d.getTime();
13441             
13442             //Roo.log(d);
13443             
13444             cell.dateValue = t;
13445             if(t == today){
13446                 cell.className += " fc-today";
13447                 cell.className += " fc-state-highlight";
13448                 cell.title = cal.todayText;
13449             }
13450             if(t == sel){
13451                 // disable highlight in other month..
13452                 //cell.className += " fc-state-highlight";
13453                 
13454             }
13455             // disabling
13456             if(t < min) {
13457                 cell.className = " fc-state-disabled";
13458                 cell.title = cal.minText;
13459                 return;
13460             }
13461             if(t > max) {
13462                 cell.className = " fc-state-disabled";
13463                 cell.title = cal.maxText;
13464                 return;
13465             }
13466             if(ddays){
13467                 if(ddays.indexOf(d.getDay()) != -1){
13468                     cell.title = ddaysText;
13469                     cell.className = " fc-state-disabled";
13470                 }
13471             }
13472             if(ddMatch && format){
13473                 var fvalue = d.dateFormat(format);
13474                 if(ddMatch.test(fvalue)){
13475                     cell.title = ddText.replace("%0", fvalue);
13476                     cell.className = " fc-state-disabled";
13477                 }
13478             }
13479             
13480             if (!cell.initialClassName) {
13481                 cell.initialClassName = cell.dom.className;
13482             }
13483             
13484             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13485         };
13486
13487         var i = 0;
13488         
13489         for(; i < startingPos; i++) {
13490             textEls[i].innerHTML = (++prevStart);
13491             d.setDate(d.getDate()+1);
13492             
13493             cells[i].className = "fc-past fc-other-month";
13494             setCellClass(this, cells[i]);
13495         }
13496         
13497         var intDay = 0;
13498         
13499         for(; i < days; i++){
13500             intDay = i - startingPos + 1;
13501             textEls[i].innerHTML = (intDay);
13502             d.setDate(d.getDate()+1);
13503             
13504             cells[i].className = ''; // "x-date-active";
13505             setCellClass(this, cells[i]);
13506         }
13507         var extraDays = 0;
13508         
13509         for(; i < 42; i++) {
13510             textEls[i].innerHTML = (++extraDays);
13511             d.setDate(d.getDate()+1);
13512             
13513             cells[i].className = "fc-future fc-other-month";
13514             setCellClass(this, cells[i]);
13515         }
13516         
13517         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13518         
13519         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13520         
13521         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13522         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13523         
13524         if(totalRows != 6){
13525             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13526             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13527         }
13528         
13529         this.fireEvent('monthchange', this, date);
13530         
13531         
13532         /*
13533         if(!this.internalRender){
13534             var main = this.el.dom.firstChild;
13535             var w = main.offsetWidth;
13536             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13537             Roo.fly(main).setWidth(w);
13538             this.internalRender = true;
13539             // opera does not respect the auto grow header center column
13540             // then, after it gets a width opera refuses to recalculate
13541             // without a second pass
13542             if(Roo.isOpera && !this.secondPass){
13543                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13544                 this.secondPass = true;
13545                 this.update.defer(10, this, [date]);
13546             }
13547         }
13548         */
13549         
13550     },
13551     
13552     findCell : function(dt) {
13553         dt = dt.clearTime().getTime();
13554         var ret = false;
13555         this.cells.each(function(c){
13556             //Roo.log("check " +c.dateValue + '?=' + dt);
13557             if(c.dateValue == dt){
13558                 ret = c;
13559                 return false;
13560             }
13561             return true;
13562         });
13563         
13564         return ret;
13565     },
13566     
13567     findCells : function(ev) {
13568         var s = ev.start.clone().clearTime().getTime();
13569        // Roo.log(s);
13570         var e= ev.end.clone().clearTime().getTime();
13571        // Roo.log(e);
13572         var ret = [];
13573         this.cells.each(function(c){
13574              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13575             
13576             if(c.dateValue > e){
13577                 return ;
13578             }
13579             if(c.dateValue < s){
13580                 return ;
13581             }
13582             ret.push(c);
13583         });
13584         
13585         return ret;    
13586     },
13587     
13588 //    findBestRow: function(cells)
13589 //    {
13590 //        var ret = 0;
13591 //        
13592 //        for (var i =0 ; i < cells.length;i++) {
13593 //            ret  = Math.max(cells[i].rows || 0,ret);
13594 //        }
13595 //        return ret;
13596 //        
13597 //    },
13598     
13599     
13600     addItem : function(ev)
13601     {
13602         // look for vertical location slot in
13603         var cells = this.findCells(ev);
13604         
13605 //        ev.row = this.findBestRow(cells);
13606         
13607         // work out the location.
13608         
13609         var crow = false;
13610         var rows = [];
13611         for(var i =0; i < cells.length; i++) {
13612             
13613             cells[i].row = cells[0].row;
13614             
13615             if(i == 0){
13616                 cells[i].row = cells[i].row + 1;
13617             }
13618             
13619             if (!crow) {
13620                 crow = {
13621                     start : cells[i],
13622                     end :  cells[i]
13623                 };
13624                 continue;
13625             }
13626             if (crow.start.getY() == cells[i].getY()) {
13627                 // on same row.
13628                 crow.end = cells[i];
13629                 continue;
13630             }
13631             // different row.
13632             rows.push(crow);
13633             crow = {
13634                 start: cells[i],
13635                 end : cells[i]
13636             };
13637             
13638         }
13639         
13640         rows.push(crow);
13641         ev.els = [];
13642         ev.rows = rows;
13643         ev.cells = cells;
13644         
13645         cells[0].events.push(ev);
13646         
13647         this.calevents.push(ev);
13648     },
13649     
13650     clearEvents: function() {
13651         
13652         if(!this.calevents){
13653             return;
13654         }
13655         
13656         Roo.each(this.cells.elements, function(c){
13657             c.row = 0;
13658             c.events = [];
13659             c.more = [];
13660         });
13661         
13662         Roo.each(this.calevents, function(e) {
13663             Roo.each(e.els, function(el) {
13664                 el.un('mouseenter' ,this.onEventEnter, this);
13665                 el.un('mouseleave' ,this.onEventLeave, this);
13666                 el.remove();
13667             },this);
13668         },this);
13669         
13670         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13671             e.remove();
13672         });
13673         
13674     },
13675     
13676     renderEvents: function()
13677     {   
13678         var _this = this;
13679         
13680         this.cells.each(function(c) {
13681             
13682             if(c.row < 5){
13683                 return;
13684             }
13685             
13686             var ev = c.events;
13687             
13688             var r = 4;
13689             if(c.row != c.events.length){
13690                 r = 4 - (4 - (c.row - c.events.length));
13691             }
13692             
13693             c.events = ev.slice(0, r);
13694             c.more = ev.slice(r);
13695             
13696             if(c.more.length && c.more.length == 1){
13697                 c.events.push(c.more.pop());
13698             }
13699             
13700             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13701             
13702         });
13703             
13704         this.cells.each(function(c) {
13705             
13706             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13707             
13708             
13709             for (var e = 0; e < c.events.length; e++){
13710                 var ev = c.events[e];
13711                 var rows = ev.rows;
13712                 
13713                 for(var i = 0; i < rows.length; i++) {
13714                 
13715                     // how many rows should it span..
13716
13717                     var  cfg = {
13718                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13719                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13720
13721                         unselectable : "on",
13722                         cn : [
13723                             {
13724                                 cls: 'fc-event-inner',
13725                                 cn : [
13726     //                                {
13727     //                                  tag:'span',
13728     //                                  cls: 'fc-event-time',
13729     //                                  html : cells.length > 1 ? '' : ev.time
13730     //                                },
13731                                     {
13732                                       tag:'span',
13733                                       cls: 'fc-event-title',
13734                                       html : String.format('{0}', ev.title)
13735                                     }
13736
13737
13738                                 ]
13739                             },
13740                             {
13741                                 cls: 'ui-resizable-handle ui-resizable-e',
13742                                 html : '&nbsp;&nbsp;&nbsp'
13743                             }
13744
13745                         ]
13746                     };
13747
13748                     if (i == 0) {
13749                         cfg.cls += ' fc-event-start';
13750                     }
13751                     if ((i+1) == rows.length) {
13752                         cfg.cls += ' fc-event-end';
13753                     }
13754
13755                     var ctr = _this.el.select('.fc-event-container',true).first();
13756                     var cg = ctr.createChild(cfg);
13757
13758                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13759                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13760
13761                     var r = (c.more.length) ? 1 : 0;
13762                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13763                     cg.setWidth(ebox.right - sbox.x -2);
13764
13765                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13766                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13767                     cg.on('click', _this.onEventClick, _this, ev);
13768
13769                     ev.els.push(cg);
13770                     
13771                 }
13772                 
13773             }
13774             
13775             
13776             if(c.more.length){
13777                 var  cfg = {
13778                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13779                     style : 'position: absolute',
13780                     unselectable : "on",
13781                     cn : [
13782                         {
13783                             cls: 'fc-event-inner',
13784                             cn : [
13785                                 {
13786                                   tag:'span',
13787                                   cls: 'fc-event-title',
13788                                   html : 'More'
13789                                 }
13790
13791
13792                             ]
13793                         },
13794                         {
13795                             cls: 'ui-resizable-handle ui-resizable-e',
13796                             html : '&nbsp;&nbsp;&nbsp'
13797                         }
13798
13799                     ]
13800                 };
13801
13802                 var ctr = _this.el.select('.fc-event-container',true).first();
13803                 var cg = ctr.createChild(cfg);
13804
13805                 var sbox = c.select('.fc-day-content',true).first().getBox();
13806                 var ebox = c.select('.fc-day-content',true).first().getBox();
13807                 //Roo.log(cg);
13808                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13809                 cg.setWidth(ebox.right - sbox.x -2);
13810
13811                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13812                 
13813             }
13814             
13815         });
13816         
13817         
13818         
13819     },
13820     
13821     onEventEnter: function (e, el,event,d) {
13822         this.fireEvent('evententer', this, el, event);
13823     },
13824     
13825     onEventLeave: function (e, el,event,d) {
13826         this.fireEvent('eventleave', this, el, event);
13827     },
13828     
13829     onEventClick: function (e, el,event,d) {
13830         this.fireEvent('eventclick', this, el, event);
13831     },
13832     
13833     onMonthChange: function () {
13834         this.store.load();
13835     },
13836     
13837     onMoreEventClick: function(e, el, more)
13838     {
13839         var _this = this;
13840         
13841         this.calpopover.placement = 'right';
13842         this.calpopover.setTitle('More');
13843         
13844         this.calpopover.setContent('');
13845         
13846         var ctr = this.calpopover.el.select('.popover-content', true).first();
13847         
13848         Roo.each(more, function(m){
13849             var cfg = {
13850                 cls : 'fc-event-hori fc-event-draggable',
13851                 html : m.title
13852             }
13853             var cg = ctr.createChild(cfg);
13854             
13855             cg.on('click', _this.onEventClick, _this, m);
13856         });
13857         
13858         this.calpopover.show(el);
13859         
13860         
13861     },
13862     
13863     onLoad: function () 
13864     {   
13865         this.calevents = [];
13866         var cal = this;
13867         
13868         if(this.store.getCount() > 0){
13869             this.store.data.each(function(d){
13870                cal.addItem({
13871                     id : d.data.id,
13872                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13873                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13874                     time : d.data.start_time,
13875                     title : d.data.title,
13876                     description : d.data.description,
13877                     venue : d.data.venue
13878                 });
13879             });
13880         }
13881         
13882         this.renderEvents();
13883         
13884         if(this.calevents.length && this.loadMask){
13885             this.maskEl.hide();
13886         }
13887     },
13888     
13889     onBeforeLoad: function()
13890     {
13891         this.clearEvents();
13892         if(this.loadMask){
13893             this.maskEl.show();
13894         }
13895     }
13896 });
13897
13898  
13899  /*
13900  * - LGPL
13901  *
13902  * element
13903  * 
13904  */
13905
13906 /**
13907  * @class Roo.bootstrap.Popover
13908  * @extends Roo.bootstrap.Component
13909  * Bootstrap Popover class
13910  * @cfg {String} html contents of the popover   (or false to use children..)
13911  * @cfg {String} title of popover (or false to hide)
13912  * @cfg {String} placement how it is placed
13913  * @cfg {String} trigger click || hover (or false to trigger manually)
13914  * @cfg {String} over what (parent or false to trigger manually.)
13915  * @cfg {Number} delay - delay before showing
13916  
13917  * @constructor
13918  * Create a new Popover
13919  * @param {Object} config The config object
13920  */
13921
13922 Roo.bootstrap.Popover = function(config){
13923     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13924 };
13925
13926 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13927     
13928     title: 'Fill in a title',
13929     html: false,
13930     
13931     placement : 'right',
13932     trigger : 'hover', // hover
13933     
13934     delay : 0,
13935     
13936     over: 'parent',
13937     
13938     can_build_overlaid : false,
13939     
13940     getChildContainer : function()
13941     {
13942         return this.el.select('.popover-content',true).first();
13943     },
13944     
13945     getAutoCreate : function(){
13946          Roo.log('make popover?');
13947         var cfg = {
13948            cls : 'popover roo-dynamic',
13949            style: 'display:block',
13950            cn : [
13951                 {
13952                     cls : 'arrow'
13953                 },
13954                 {
13955                     cls : 'popover-inner',
13956                     cn : [
13957                         {
13958                             tag: 'h3',
13959                             cls: 'popover-title',
13960                             html : this.title
13961                         },
13962                         {
13963                             cls : 'popover-content',
13964                             html : this.html
13965                         }
13966                     ]
13967                     
13968                 }
13969            ]
13970         };
13971         
13972         return cfg;
13973     },
13974     setTitle: function(str)
13975     {
13976         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13977     },
13978     setContent: function(str)
13979     {
13980         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13981     },
13982     // as it get's added to the bottom of the page.
13983     onRender : function(ct, position)
13984     {
13985         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13986         if(!this.el){
13987             var cfg = Roo.apply({},  this.getAutoCreate());
13988             cfg.id = Roo.id();
13989             
13990             if (this.cls) {
13991                 cfg.cls += ' ' + this.cls;
13992             }
13993             if (this.style) {
13994                 cfg.style = this.style;
13995             }
13996             Roo.log("adding to ")
13997             this.el = Roo.get(document.body).createChild(cfg, position);
13998             Roo.log(this.el);
13999         }
14000         this.initEvents();
14001     },
14002     
14003     initEvents : function()
14004     {
14005         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14006         this.el.enableDisplayMode('block');
14007         this.el.hide();
14008         if (this.over === false) {
14009             return; 
14010         }
14011         if (this.triggers === false) {
14012             return;
14013         }
14014         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14015         var triggers = this.trigger ? this.trigger.split(' ') : [];
14016         Roo.each(triggers, function(trigger) {
14017         
14018             if (trigger == 'click') {
14019                 on_el.on('click', this.toggle, this);
14020             } else if (trigger != 'manual') {
14021                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14022                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14023       
14024                 on_el.on(eventIn  ,this.enter, this);
14025                 on_el.on(eventOut, this.leave, this);
14026             }
14027         }, this);
14028         
14029     },
14030     
14031     
14032     // private
14033     timeout : null,
14034     hoverState : null,
14035     
14036     toggle : function () {
14037         this.hoverState == 'in' ? this.leave() : this.enter();
14038     },
14039     
14040     enter : function () {
14041        
14042     
14043         clearTimeout(this.timeout);
14044     
14045         this.hoverState = 'in';
14046     
14047         if (!this.delay || !this.delay.show) {
14048             this.show();
14049             return;
14050         }
14051         var _t = this;
14052         this.timeout = setTimeout(function () {
14053             if (_t.hoverState == 'in') {
14054                 _t.show();
14055             }
14056         }, this.delay.show)
14057     },
14058     leave : function() {
14059         clearTimeout(this.timeout);
14060     
14061         this.hoverState = 'out';
14062     
14063         if (!this.delay || !this.delay.hide) {
14064             this.hide();
14065             return;
14066         }
14067         var _t = this;
14068         this.timeout = setTimeout(function () {
14069             if (_t.hoverState == 'out') {
14070                 _t.hide();
14071             }
14072         }, this.delay.hide)
14073     },
14074     
14075     show : function (on_el)
14076     {
14077         if (!on_el) {
14078             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14079         }
14080         // set content.
14081         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14082         if (this.html !== false) {
14083             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14084         }
14085         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14086         if (!this.title.length) {
14087             this.el.select('.popover-title',true).hide();
14088         }
14089         
14090         var placement = typeof this.placement == 'function' ?
14091             this.placement.call(this, this.el, on_el) :
14092             this.placement;
14093             
14094         var autoToken = /\s?auto?\s?/i;
14095         var autoPlace = autoToken.test(placement);
14096         if (autoPlace) {
14097             placement = placement.replace(autoToken, '') || 'top';
14098         }
14099         
14100         //this.el.detach()
14101         //this.el.setXY([0,0]);
14102         this.el.show();
14103         this.el.dom.style.display='block';
14104         this.el.addClass(placement);
14105         
14106         //this.el.appendTo(on_el);
14107         
14108         var p = this.getPosition();
14109         var box = this.el.getBox();
14110         
14111         if (autoPlace) {
14112             // fixme..
14113         }
14114         var align = Roo.bootstrap.Popover.alignment[placement];
14115         this.el.alignTo(on_el, align[0],align[1]);
14116         //var arrow = this.el.select('.arrow',true).first();
14117         //arrow.set(align[2], 
14118         
14119         this.el.addClass('in');
14120         this.hoverState = null;
14121         
14122         if (this.el.hasClass('fade')) {
14123             // fade it?
14124         }
14125         
14126     },
14127     hide : function()
14128     {
14129         this.el.setXY([0,0]);
14130         this.el.removeClass('in');
14131         this.el.hide();
14132         
14133     }
14134     
14135 });
14136
14137 Roo.bootstrap.Popover.alignment = {
14138     'left' : ['r-l', [-10,0], 'right'],
14139     'right' : ['l-r', [10,0], 'left'],
14140     'bottom' : ['t-b', [0,10], 'top'],
14141     'top' : [ 'b-t', [0,-10], 'bottom']
14142 };
14143
14144  /*
14145  * - LGPL
14146  *
14147  * Progress
14148  * 
14149  */
14150
14151 /**
14152  * @class Roo.bootstrap.Progress
14153  * @extends Roo.bootstrap.Component
14154  * Bootstrap Progress class
14155  * @cfg {Boolean} striped striped of the progress bar
14156  * @cfg {Boolean} active animated of the progress bar
14157  * 
14158  * 
14159  * @constructor
14160  * Create a new Progress
14161  * @param {Object} config The config object
14162  */
14163
14164 Roo.bootstrap.Progress = function(config){
14165     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14166 };
14167
14168 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14169     
14170     striped : false,
14171     active: false,
14172     
14173     getAutoCreate : function(){
14174         var cfg = {
14175             tag: 'div',
14176             cls: 'progress'
14177         };
14178         
14179         
14180         if(this.striped){
14181             cfg.cls += ' progress-striped';
14182         }
14183       
14184         if(this.active){
14185             cfg.cls += ' active';
14186         }
14187         
14188         
14189         return cfg;
14190     }
14191    
14192 });
14193
14194  
14195
14196  /*
14197  * - LGPL
14198  *
14199  * ProgressBar
14200  * 
14201  */
14202
14203 /**
14204  * @class Roo.bootstrap.ProgressBar
14205  * @extends Roo.bootstrap.Component
14206  * Bootstrap ProgressBar class
14207  * @cfg {Number} aria_valuenow aria-value now
14208  * @cfg {Number} aria_valuemin aria-value min
14209  * @cfg {Number} aria_valuemax aria-value max
14210  * @cfg {String} label label for the progress bar
14211  * @cfg {String} panel (success | info | warning | danger )
14212  * @cfg {String} role role of the progress bar
14213  * @cfg {String} sr_only text
14214  * 
14215  * 
14216  * @constructor
14217  * Create a new ProgressBar
14218  * @param {Object} config The config object
14219  */
14220
14221 Roo.bootstrap.ProgressBar = function(config){
14222     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14223 };
14224
14225 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14226     
14227     aria_valuenow : 0,
14228     aria_valuemin : 0,
14229     aria_valuemax : 100,
14230     label : false,
14231     panel : false,
14232     role : false,
14233     sr_only: false,
14234     
14235     getAutoCreate : function()
14236     {
14237         
14238         var cfg = {
14239             tag: 'div',
14240             cls: 'progress-bar',
14241             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14242         };
14243         
14244         if(this.sr_only){
14245             cfg.cn = {
14246                 tag: 'span',
14247                 cls: 'sr-only',
14248                 html: this.sr_only
14249             }
14250         }
14251         
14252         if(this.role){
14253             cfg.role = this.role;
14254         }
14255         
14256         if(this.aria_valuenow){
14257             cfg['aria-valuenow'] = this.aria_valuenow;
14258         }
14259         
14260         if(this.aria_valuemin){
14261             cfg['aria-valuemin'] = this.aria_valuemin;
14262         }
14263         
14264         if(this.aria_valuemax){
14265             cfg['aria-valuemax'] = this.aria_valuemax;
14266         }
14267         
14268         if(this.label && !this.sr_only){
14269             cfg.html = this.label;
14270         }
14271         
14272         if(this.panel){
14273             cfg.cls += ' progress-bar-' + this.panel;
14274         }
14275         
14276         return cfg;
14277     },
14278     
14279     update : function(aria_valuenow)
14280     {
14281         this.aria_valuenow = aria_valuenow;
14282         
14283         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14284     }
14285    
14286 });
14287
14288  
14289
14290  /*
14291  * - LGPL
14292  *
14293  * column
14294  * 
14295  */
14296
14297 /**
14298  * @class Roo.bootstrap.TabGroup
14299  * @extends Roo.bootstrap.Column
14300  * Bootstrap Column class
14301  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14302  * @cfg {Boolean} carousel true to make the group behave like a carousel
14303  * 
14304  * @constructor
14305  * Create a new TabGroup
14306  * @param {Object} config The config object
14307  */
14308
14309 Roo.bootstrap.TabGroup = function(config){
14310     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14311     if (!this.navId) {
14312         this.navId = Roo.id();
14313     }
14314     this.tabs = [];
14315     Roo.bootstrap.TabGroup.register(this);
14316     
14317 };
14318
14319 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14320     
14321     carousel : false,
14322     transition : false,
14323      
14324     getAutoCreate : function()
14325     {
14326         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14327         
14328         cfg.cls += ' tab-content';
14329         
14330         if (this.carousel) {
14331             cfg.cls += ' carousel slide';
14332             cfg.cn = [{
14333                cls : 'carousel-inner'
14334             }]
14335         }
14336         
14337         
14338         return cfg;
14339     },
14340     getChildContainer : function()
14341     {
14342         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14343     },
14344     
14345     /**
14346     * register a Navigation item
14347     * @param {Roo.bootstrap.NavItem} the navitem to add
14348     */
14349     register : function(item)
14350     {
14351         this.tabs.push( item);
14352         item.navId = this.navId; // not really needed..
14353     
14354     },
14355     
14356     getActivePanel : function()
14357     {
14358         var r = false;
14359         Roo.each(this.tabs, function(t) {
14360             if (t.active) {
14361                 r = t;
14362                 return false;
14363             }
14364             return null;
14365         });
14366         return r;
14367         
14368     },
14369     getPanelByName : function(n)
14370     {
14371         var r = false;
14372         Roo.each(this.tabs, function(t) {
14373             if (t.tabId == n) {
14374                 r = t;
14375                 return false;
14376             }
14377             return null;
14378         });
14379         return r;
14380     },
14381     indexOfPanel : function(p)
14382     {
14383         var r = false;
14384         Roo.each(this.tabs, function(t,i) {
14385             if (t.tabId == p.tabId) {
14386                 r = i;
14387                 return false;
14388             }
14389             return null;
14390         });
14391         return r;
14392     },
14393     /**
14394      * show a specific panel
14395      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14396      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14397      */
14398     showPanel : function (pan)
14399     {
14400         
14401         if (typeof(pan) == 'number') {
14402             pan = this.tabs[pan];
14403         }
14404         if (typeof(pan) == 'string') {
14405             pan = this.getPanelByName(pan);
14406         }
14407         if (pan.tabId == this.getActivePanel().tabId) {
14408             return true;
14409         }
14410         var cur = this.getActivePanel();
14411         
14412         if (false === cur.fireEvent('beforedeactivate')) {
14413             return false;
14414         }
14415         
14416         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14417             
14418             this.transition = true;
14419             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14420             var lr = dir == 'next' ? 'left' : 'right';
14421             pan.el.addClass(dir); // or prev
14422             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14423             cur.el.addClass(lr); // or right
14424             pan.el.addClass(lr);
14425             
14426             var _this = this;
14427             cur.el.on('transitionend', function() {
14428                 Roo.log("trans end?");
14429                 
14430                 pan.el.removeClass([lr,dir]);
14431                 pan.setActive(true);
14432                 
14433                 cur.el.removeClass([lr]);
14434                 cur.setActive(false);
14435                 
14436                 _this.transition = false;
14437                 
14438             }, this, { single:  true } );
14439             return true;
14440         }
14441         
14442         cur.setActive(false);
14443         pan.setActive(true);
14444         return true;
14445         
14446     },
14447     showPanelNext : function()
14448     {
14449         var i = this.indexOfPanel(this.getActivePanel());
14450         if (i > this.tabs.length) {
14451             return;
14452         }
14453         this.showPanel(this.tabs[i+1]);
14454     },
14455     showPanelPrev : function()
14456     {
14457         var i = this.indexOfPanel(this.getActivePanel());
14458         if (i  < 1) {
14459             return;
14460         }
14461         this.showPanel(this.tabs[i-1]);
14462     }
14463     
14464     
14465   
14466 });
14467
14468  
14469
14470  
14471  
14472 Roo.apply(Roo.bootstrap.TabGroup, {
14473     
14474     groups: {},
14475      /**
14476     * register a Navigation Group
14477     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14478     */
14479     register : function(navgrp)
14480     {
14481         this.groups[navgrp.navId] = navgrp;
14482         
14483     },
14484     /**
14485     * fetch a Navigation Group based on the navigation ID
14486     * if one does not exist , it will get created.
14487     * @param {string} the navgroup to add
14488     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14489     */
14490     get: function(navId) {
14491         if (typeof(this.groups[navId]) == 'undefined') {
14492             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14493         }
14494         return this.groups[navId] ;
14495     }
14496     
14497     
14498     
14499 });
14500
14501  /*
14502  * - LGPL
14503  *
14504  * TabPanel
14505  * 
14506  */
14507
14508 /**
14509  * @class Roo.bootstrap.TabPanel
14510  * @extends Roo.bootstrap.Component
14511  * Bootstrap TabPanel class
14512  * @cfg {Boolean} active panel active
14513  * @cfg {String} html panel content
14514  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14515  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14516  * 
14517  * 
14518  * @constructor
14519  * Create a new TabPanel
14520  * @param {Object} config The config object
14521  */
14522
14523 Roo.bootstrap.TabPanel = function(config){
14524     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14525     this.addEvents({
14526         /**
14527              * @event changed
14528              * Fires when the active status changes
14529              * @param {Roo.bootstrap.TabPanel} this
14530              * @param {Boolean} state the new state
14531             
14532          */
14533         'changed': true,
14534         /**
14535              * @event beforedeactivate
14536              * Fires before a tab is de-activated - can be used to do validation on a form.
14537              * @param {Roo.bootstrap.TabPanel} this
14538              * @return {Boolean} false if there is an error
14539             
14540          */
14541         'beforedeactivate': true
14542      });
14543     
14544     this.tabId = this.tabId || Roo.id();
14545   
14546 };
14547
14548 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14549     
14550     active: false,
14551     html: false,
14552     tabId: false,
14553     navId : false,
14554     
14555     getAutoCreate : function(){
14556         var cfg = {
14557             tag: 'div',
14558             // item is needed for carousel - not sure if it has any effect otherwise
14559             cls: 'tab-pane item',
14560             html: this.html || ''
14561         };
14562         
14563         if(this.active){
14564             cfg.cls += ' active';
14565         }
14566         
14567         if(this.tabId){
14568             cfg.tabId = this.tabId;
14569         }
14570         
14571         
14572         return cfg;
14573     },
14574     
14575     initEvents:  function()
14576     {
14577         Roo.log('-------- init events on tab panel ---------');
14578         
14579         var p = this.parent();
14580         this.navId = this.navId || p.navId;
14581         
14582         if (typeof(this.navId) != 'undefined') {
14583             // not really needed.. but just in case.. parent should be a NavGroup.
14584             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14585             Roo.log(['register', tg, this]);
14586             tg.register(this);
14587         }
14588     },
14589     
14590     
14591     onRender : function(ct, position)
14592     {
14593        // Roo.log("Call onRender: " + this.xtype);
14594         
14595         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14596         
14597         
14598         
14599         
14600         
14601     },
14602     
14603     setActive: function(state)
14604     {
14605         Roo.log("panel - set active " + this.tabId + "=" + state);
14606         
14607         this.active = state;
14608         if (!state) {
14609             this.el.removeClass('active');
14610             
14611         } else  if (!this.el.hasClass('active')) {
14612             this.el.addClass('active');
14613         }
14614         this.fireEvent('changed', this, state);
14615     }
14616     
14617     
14618 });
14619  
14620
14621  
14622
14623  /*
14624  * - LGPL
14625  *
14626  * DateField
14627  * 
14628  */
14629
14630 /**
14631  * @class Roo.bootstrap.DateField
14632  * @extends Roo.bootstrap.Input
14633  * Bootstrap DateField class
14634  * @cfg {Number} weekStart default 0
14635  * @cfg {String} viewMode default empty, (months|years)
14636  * @cfg {String} minViewMode default empty, (months|years)
14637  * @cfg {Number} startDate default -Infinity
14638  * @cfg {Number} endDate default Infinity
14639  * @cfg {Boolean} todayHighlight default false
14640  * @cfg {Boolean} todayBtn default false
14641  * @cfg {Boolean} calendarWeeks default false
14642  * @cfg {Object} daysOfWeekDisabled default empty
14643  * @cfg {Boolean} singleMode default false (true | false)
14644  * 
14645  * @cfg {Boolean} keyboardNavigation default true
14646  * @cfg {String} language default en
14647  * 
14648  * @constructor
14649  * Create a new DateField
14650  * @param {Object} config The config object
14651  */
14652
14653 Roo.bootstrap.DateField = function(config){
14654     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14655      this.addEvents({
14656             /**
14657              * @event show
14658              * Fires when this field show.
14659              * @param {Roo.bootstrap.DateField} this
14660              * @param {Mixed} date The date value
14661              */
14662             show : true,
14663             /**
14664              * @event show
14665              * Fires when this field hide.
14666              * @param {Roo.bootstrap.DateField} this
14667              * @param {Mixed} date The date value
14668              */
14669             hide : true,
14670             /**
14671              * @event select
14672              * Fires when select a date.
14673              * @param {Roo.bootstrap.DateField} this
14674              * @param {Mixed} date The date value
14675              */
14676             select : true
14677         });
14678 };
14679
14680 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14681     
14682     /**
14683      * @cfg {String} format
14684      * The default date format string which can be overriden for localization support.  The format must be
14685      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14686      */
14687     format : "m/d/y",
14688     /**
14689      * @cfg {String} altFormats
14690      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14691      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14692      */
14693     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14694     
14695     weekStart : 0,
14696     
14697     viewMode : '',
14698     
14699     minViewMode : '',
14700     
14701     todayHighlight : false,
14702     
14703     todayBtn: false,
14704     
14705     language: 'en',
14706     
14707     keyboardNavigation: true,
14708     
14709     calendarWeeks: false,
14710     
14711     startDate: -Infinity,
14712     
14713     endDate: Infinity,
14714     
14715     daysOfWeekDisabled: [],
14716     
14717     _events: [],
14718     
14719     singleMode : false,
14720     
14721     UTCDate: function()
14722     {
14723         return new Date(Date.UTC.apply(Date, arguments));
14724     },
14725     
14726     UTCToday: function()
14727     {
14728         var today = new Date();
14729         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14730     },
14731     
14732     getDate: function() {
14733             var d = this.getUTCDate();
14734             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14735     },
14736     
14737     getUTCDate: function() {
14738             return this.date;
14739     },
14740     
14741     setDate: function(d) {
14742             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14743     },
14744     
14745     setUTCDate: function(d) {
14746             this.date = d;
14747             this.setValue(this.formatDate(this.date));
14748     },
14749         
14750     onRender: function(ct, position)
14751     {
14752         
14753         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14754         
14755         this.language = this.language || 'en';
14756         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14757         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14758         
14759         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14760         this.format = this.format || 'm/d/y';
14761         this.isInline = false;
14762         this.isInput = true;
14763         this.component = this.el.select('.add-on', true).first() || false;
14764         this.component = (this.component && this.component.length === 0) ? false : this.component;
14765         this.hasInput = this.component && this.inputEL().length;
14766         
14767         if (typeof(this.minViewMode === 'string')) {
14768             switch (this.minViewMode) {
14769                 case 'months':
14770                     this.minViewMode = 1;
14771                     break;
14772                 case 'years':
14773                     this.minViewMode = 2;
14774                     break;
14775                 default:
14776                     this.minViewMode = 0;
14777                     break;
14778             }
14779         }
14780         
14781         if (typeof(this.viewMode === 'string')) {
14782             switch (this.viewMode) {
14783                 case 'months':
14784                     this.viewMode = 1;
14785                     break;
14786                 case 'years':
14787                     this.viewMode = 2;
14788                     break;
14789                 default:
14790                     this.viewMode = 0;
14791                     break;
14792             }
14793         }
14794                 
14795         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14796         
14797 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14798         
14799         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14800         
14801         this.picker().on('mousedown', this.onMousedown, this);
14802         this.picker().on('click', this.onClick, this);
14803         
14804         this.picker().addClass('datepicker-dropdown');
14805         
14806         this.startViewMode = this.viewMode;
14807         
14808         if(this.singleMode){
14809             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
14810                 v.setVisibilityMode(Roo.Element.DISPLAY)
14811                 v.hide();
14812             });
14813             
14814             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
14815                 v.setStyle('width', '189px');
14816             });
14817         }
14818         
14819         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14820             if(!this.calendarWeeks){
14821                 v.remove();
14822                 return;
14823             }
14824             
14825             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14826             v.attr('colspan', function(i, val){
14827                 return parseInt(val) + 1;
14828             });
14829         })
14830                         
14831         
14832         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14833         
14834         this.setStartDate(this.startDate);
14835         this.setEndDate(this.endDate);
14836         
14837         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14838         
14839         this.fillDow();
14840         this.fillMonths();
14841         this.update();
14842         this.showMode();
14843         
14844         if(this.isInline) {
14845             this.show();
14846         }
14847     },
14848     
14849     picker : function()
14850     {
14851         return this.pickerEl;
14852 //        return this.el.select('.datepicker', true).first();
14853     },
14854     
14855     fillDow: function()
14856     {
14857         var dowCnt = this.weekStart;
14858         
14859         var dow = {
14860             tag: 'tr',
14861             cn: [
14862                 
14863             ]
14864         };
14865         
14866         if(this.calendarWeeks){
14867             dow.cn.push({
14868                 tag: 'th',
14869                 cls: 'cw',
14870                 html: '&nbsp;'
14871             })
14872         }
14873         
14874         while (dowCnt < this.weekStart + 7) {
14875             dow.cn.push({
14876                 tag: 'th',
14877                 cls: 'dow',
14878                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14879             });
14880         }
14881         
14882         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14883     },
14884     
14885     fillMonths: function()
14886     {    
14887         var i = 0;
14888         var months = this.picker().select('>.datepicker-months td', true).first();
14889         
14890         months.dom.innerHTML = '';
14891         
14892         while (i < 12) {
14893             var month = {
14894                 tag: 'span',
14895                 cls: 'month',
14896                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14897             }
14898             
14899             months.createChild(month);
14900         }
14901         
14902     },
14903     
14904     update: function()
14905     {
14906         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;
14907         
14908         if (this.date < this.startDate) {
14909             this.viewDate = new Date(this.startDate);
14910         } else if (this.date > this.endDate) {
14911             this.viewDate = new Date(this.endDate);
14912         } else {
14913             this.viewDate = new Date(this.date);
14914         }
14915         
14916         this.fill();
14917     },
14918     
14919     fill: function() 
14920     {
14921         var d = new Date(this.viewDate),
14922                 year = d.getUTCFullYear(),
14923                 month = d.getUTCMonth(),
14924                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14925                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14926                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14927                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14928                 currentDate = this.date && this.date.valueOf(),
14929                 today = this.UTCToday();
14930         
14931         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14932         
14933 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14934         
14935 //        this.picker.select('>tfoot th.today').
14936 //                                              .text(dates[this.language].today)
14937 //                                              .toggle(this.todayBtn !== false);
14938     
14939         this.updateNavArrows();
14940         this.fillMonths();
14941                                                 
14942         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14943         
14944         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14945          
14946         prevMonth.setUTCDate(day);
14947         
14948         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14949         
14950         var nextMonth = new Date(prevMonth);
14951         
14952         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14953         
14954         nextMonth = nextMonth.valueOf();
14955         
14956         var fillMonths = false;
14957         
14958         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14959         
14960         while(prevMonth.valueOf() < nextMonth) {
14961             var clsName = '';
14962             
14963             if (prevMonth.getUTCDay() === this.weekStart) {
14964                 if(fillMonths){
14965                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14966                 }
14967                     
14968                 fillMonths = {
14969                     tag: 'tr',
14970                     cn: []
14971                 };
14972                 
14973                 if(this.calendarWeeks){
14974                     // ISO 8601: First week contains first thursday.
14975                     // ISO also states week starts on Monday, but we can be more abstract here.
14976                     var
14977                     // Start of current week: based on weekstart/current date
14978                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14979                     // Thursday of this week
14980                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14981                     // First Thursday of year, year from thursday
14982                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14983                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14984                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14985                     
14986                     fillMonths.cn.push({
14987                         tag: 'td',
14988                         cls: 'cw',
14989                         html: calWeek
14990                     });
14991                 }
14992             }
14993             
14994             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14995                 clsName += ' old';
14996             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14997                 clsName += ' new';
14998             }
14999             if (this.todayHighlight &&
15000                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15001                 prevMonth.getUTCMonth() == today.getMonth() &&
15002                 prevMonth.getUTCDate() == today.getDate()) {
15003                 clsName += ' today';
15004             }
15005             
15006             if (currentDate && prevMonth.valueOf() === currentDate) {
15007                 clsName += ' active';
15008             }
15009             
15010             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15011                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15012                     clsName += ' disabled';
15013             }
15014             
15015             fillMonths.cn.push({
15016                 tag: 'td',
15017                 cls: 'day ' + clsName,
15018                 html: prevMonth.getDate()
15019             })
15020             
15021             prevMonth.setDate(prevMonth.getDate()+1);
15022         }
15023           
15024         var currentYear = this.date && this.date.getUTCFullYear();
15025         var currentMonth = this.date && this.date.getUTCMonth();
15026         
15027         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15028         
15029         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15030             v.removeClass('active');
15031             
15032             if(currentYear === year && k === currentMonth){
15033                 v.addClass('active');
15034             }
15035             
15036             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15037                 v.addClass('disabled');
15038             }
15039             
15040         });
15041         
15042         
15043         year = parseInt(year/10, 10) * 10;
15044         
15045         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15046         
15047         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15048         
15049         year -= 1;
15050         for (var i = -1; i < 11; i++) {
15051             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15052                 tag: 'span',
15053                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15054                 html: year
15055             })
15056             
15057             year += 1;
15058         }
15059     },
15060     
15061     showMode: function(dir) 
15062     {
15063         if (dir) {
15064             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15065         }
15066         
15067         Roo.each(this.picker().select('>div',true).elements, function(v){
15068             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15069             v.hide();
15070         });
15071         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15072     },
15073     
15074     place: function()
15075     {
15076         if(this.isInline) return;
15077         
15078         this.picker().removeClass(['bottom', 'top']);
15079         
15080         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15081             /*
15082              * place to the top of element!
15083              *
15084              */
15085             
15086             this.picker().addClass('top');
15087             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15088             
15089             return;
15090         }
15091         
15092         this.picker().addClass('bottom');
15093         
15094         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15095     },
15096     
15097     parseDate : function(value)
15098     {
15099         if(!value || value instanceof Date){
15100             return value;
15101         }
15102         var v = Date.parseDate(value, this.format);
15103         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15104             v = Date.parseDate(value, 'Y-m-d');
15105         }
15106         if(!v && this.altFormats){
15107             if(!this.altFormatsArray){
15108                 this.altFormatsArray = this.altFormats.split("|");
15109             }
15110             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15111                 v = Date.parseDate(value, this.altFormatsArray[i]);
15112             }
15113         }
15114         return v;
15115     },
15116     
15117     formatDate : function(date, fmt)
15118     {   
15119         return (!date || !(date instanceof Date)) ?
15120         date : date.dateFormat(fmt || this.format);
15121     },
15122     
15123     onFocus : function()
15124     {
15125         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15126         this.show();
15127     },
15128     
15129     onBlur : function()
15130     {
15131         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15132         
15133         var d = this.inputEl().getValue();
15134         
15135         this.setValue(d);
15136                 
15137         this.hide();
15138     },
15139     
15140     show : function()
15141     {
15142         this.picker().show();
15143         this.update();
15144         this.place();
15145         
15146         this.fireEvent('show', this, this.date);
15147     },
15148     
15149     hide : function()
15150     {
15151         if(this.isInline) return;
15152         this.picker().hide();
15153         this.viewMode = this.startViewMode;
15154         this.showMode();
15155         
15156         this.fireEvent('hide', this, this.date);
15157         
15158     },
15159     
15160     onMousedown: function(e)
15161     {
15162         e.stopPropagation();
15163         e.preventDefault();
15164     },
15165     
15166     keyup: function(e)
15167     {
15168         Roo.bootstrap.DateField.superclass.keyup.call(this);
15169         this.update();
15170     },
15171
15172     setValue: function(v)
15173     {
15174         
15175         // v can be a string or a date..
15176         
15177         
15178         var d = new Date(this.parseDate(v) ).clearTime();
15179         
15180         if(isNaN(d.getTime())){
15181             this.date = this.viewDate = '';
15182             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15183             return;
15184         }
15185         
15186         v = this.formatDate(d);
15187         
15188         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15189         
15190         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15191      
15192         this.update();
15193
15194         this.fireEvent('select', this, this.date);
15195         
15196     },
15197     
15198     getValue: function()
15199     {
15200         return this.formatDate(this.date);
15201     },
15202     
15203     fireKey: function(e)
15204     {
15205         if (!this.picker().isVisible()){
15206             if (e.keyCode == 27) // allow escape to hide and re-show picker
15207                 this.show();
15208             return;
15209         }
15210         
15211         var dateChanged = false,
15212         dir, day, month,
15213         newDate, newViewDate;
15214         
15215         switch(e.keyCode){
15216             case 27: // escape
15217                 this.hide();
15218                 e.preventDefault();
15219                 break;
15220             case 37: // left
15221             case 39: // right
15222                 if (!this.keyboardNavigation) break;
15223                 dir = e.keyCode == 37 ? -1 : 1;
15224                 
15225                 if (e.ctrlKey){
15226                     newDate = this.moveYear(this.date, dir);
15227                     newViewDate = this.moveYear(this.viewDate, dir);
15228                 } else if (e.shiftKey){
15229                     newDate = this.moveMonth(this.date, dir);
15230                     newViewDate = this.moveMonth(this.viewDate, dir);
15231                 } else {
15232                     newDate = new Date(this.date);
15233                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15234                     newViewDate = new Date(this.viewDate);
15235                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15236                 }
15237                 if (this.dateWithinRange(newDate)){
15238                     this.date = newDate;
15239                     this.viewDate = newViewDate;
15240                     this.setValue(this.formatDate(this.date));
15241 //                    this.update();
15242                     e.preventDefault();
15243                     dateChanged = true;
15244                 }
15245                 break;
15246             case 38: // up
15247             case 40: // down
15248                 if (!this.keyboardNavigation) break;
15249                 dir = e.keyCode == 38 ? -1 : 1;
15250                 if (e.ctrlKey){
15251                     newDate = this.moveYear(this.date, dir);
15252                     newViewDate = this.moveYear(this.viewDate, dir);
15253                 } else if (e.shiftKey){
15254                     newDate = this.moveMonth(this.date, dir);
15255                     newViewDate = this.moveMonth(this.viewDate, dir);
15256                 } else {
15257                     newDate = new Date(this.date);
15258                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15259                     newViewDate = new Date(this.viewDate);
15260                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15261                 }
15262                 if (this.dateWithinRange(newDate)){
15263                     this.date = newDate;
15264                     this.viewDate = newViewDate;
15265                     this.setValue(this.formatDate(this.date));
15266 //                    this.update();
15267                     e.preventDefault();
15268                     dateChanged = true;
15269                 }
15270                 break;
15271             case 13: // enter
15272                 this.setValue(this.formatDate(this.date));
15273                 this.hide();
15274                 e.preventDefault();
15275                 break;
15276             case 9: // tab
15277                 this.setValue(this.formatDate(this.date));
15278                 this.hide();
15279                 break;
15280             case 16: // shift
15281             case 17: // ctrl
15282             case 18: // alt
15283                 break;
15284             default :
15285                 this.hide();
15286                 
15287         }
15288     },
15289     
15290     
15291     onClick: function(e) 
15292     {
15293         e.stopPropagation();
15294         e.preventDefault();
15295         
15296         var target = e.getTarget();
15297         
15298         if(target.nodeName.toLowerCase() === 'i'){
15299             target = Roo.get(target).dom.parentNode;
15300         }
15301         
15302         var nodeName = target.nodeName;
15303         var className = target.className;
15304         var html = target.innerHTML;
15305         //Roo.log(nodeName);
15306         
15307         switch(nodeName.toLowerCase()) {
15308             case 'th':
15309                 switch(className) {
15310                     case 'switch':
15311                         this.showMode(1);
15312                         break;
15313                     case 'prev':
15314                     case 'next':
15315                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15316                         switch(this.viewMode){
15317                                 case 0:
15318                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15319                                         break;
15320                                 case 1:
15321                                 case 2:
15322                                         this.viewDate = this.moveYear(this.viewDate, dir);
15323                                         break;
15324                         }
15325                         this.fill();
15326                         break;
15327                     case 'today':
15328                         var date = new Date();
15329                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15330 //                        this.fill()
15331                         this.setValue(this.formatDate(this.date));
15332                         
15333                         this.hide();
15334                         break;
15335                 }
15336                 break;
15337             case 'span':
15338                 if (className.indexOf('disabled') < 0) {
15339                     this.viewDate.setUTCDate(1);
15340                     if (className.indexOf('month') > -1) {
15341                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15342                     } else {
15343                         var year = parseInt(html, 10) || 0;
15344                         this.viewDate.setUTCFullYear(year);
15345                         
15346                     }
15347                     
15348                     if(this.singleMode){
15349                         this.setValue(this.formatDate(this.viewDate));
15350                         this.hide();
15351                         return;
15352                     }
15353                     
15354                     this.showMode(-1);
15355                     this.fill();
15356                 }
15357                 break;
15358                 
15359             case 'td':
15360                 //Roo.log(className);
15361                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15362                     var day = parseInt(html, 10) || 1;
15363                     var year = this.viewDate.getUTCFullYear(),
15364                         month = this.viewDate.getUTCMonth();
15365
15366                     if (className.indexOf('old') > -1) {
15367                         if(month === 0 ){
15368                             month = 11;
15369                             year -= 1;
15370                         }else{
15371                             month -= 1;
15372                         }
15373                     } else if (className.indexOf('new') > -1) {
15374                         if (month == 11) {
15375                             month = 0;
15376                             year += 1;
15377                         } else {
15378                             month += 1;
15379                         }
15380                     }
15381                     //Roo.log([year,month,day]);
15382                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15383                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15384 //                    this.fill();
15385                     //Roo.log(this.formatDate(this.date));
15386                     this.setValue(this.formatDate(this.date));
15387                     this.hide();
15388                 }
15389                 break;
15390         }
15391     },
15392     
15393     setStartDate: function(startDate)
15394     {
15395         this.startDate = startDate || -Infinity;
15396         if (this.startDate !== -Infinity) {
15397             this.startDate = this.parseDate(this.startDate);
15398         }
15399         this.update();
15400         this.updateNavArrows();
15401     },
15402
15403     setEndDate: function(endDate)
15404     {
15405         this.endDate = endDate || Infinity;
15406         if (this.endDate !== Infinity) {
15407             this.endDate = this.parseDate(this.endDate);
15408         }
15409         this.update();
15410         this.updateNavArrows();
15411     },
15412     
15413     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15414     {
15415         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15416         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15417             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15418         }
15419         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15420             return parseInt(d, 10);
15421         });
15422         this.update();
15423         this.updateNavArrows();
15424     },
15425     
15426     updateNavArrows: function() 
15427     {
15428         if(this.singleMode){
15429             return;
15430         }
15431         
15432         var d = new Date(this.viewDate),
15433         year = d.getUTCFullYear(),
15434         month = d.getUTCMonth();
15435         
15436         Roo.each(this.picker().select('.prev', true).elements, function(v){
15437             v.show();
15438             switch (this.viewMode) {
15439                 case 0:
15440
15441                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15442                         v.hide();
15443                     }
15444                     break;
15445                 case 1:
15446                 case 2:
15447                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15448                         v.hide();
15449                     }
15450                     break;
15451             }
15452         });
15453         
15454         Roo.each(this.picker().select('.next', true).elements, function(v){
15455             v.show();
15456             switch (this.viewMode) {
15457                 case 0:
15458
15459                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15460                         v.hide();
15461                     }
15462                     break;
15463                 case 1:
15464                 case 2:
15465                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15466                         v.hide();
15467                     }
15468                     break;
15469             }
15470         })
15471     },
15472     
15473     moveMonth: function(date, dir)
15474     {
15475         if (!dir) return date;
15476         var new_date = new Date(date.valueOf()),
15477         day = new_date.getUTCDate(),
15478         month = new_date.getUTCMonth(),
15479         mag = Math.abs(dir),
15480         new_month, test;
15481         dir = dir > 0 ? 1 : -1;
15482         if (mag == 1){
15483             test = dir == -1
15484             // If going back one month, make sure month is not current month
15485             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15486             ? function(){
15487                 return new_date.getUTCMonth() == month;
15488             }
15489             // If going forward one month, make sure month is as expected
15490             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15491             : function(){
15492                 return new_date.getUTCMonth() != new_month;
15493             };
15494             new_month = month + dir;
15495             new_date.setUTCMonth(new_month);
15496             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15497             if (new_month < 0 || new_month > 11)
15498                 new_month = (new_month + 12) % 12;
15499         } else {
15500             // For magnitudes >1, move one month at a time...
15501             for (var i=0; i<mag; i++)
15502                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15503                 new_date = this.moveMonth(new_date, dir);
15504             // ...then reset the day, keeping it in the new month
15505             new_month = new_date.getUTCMonth();
15506             new_date.setUTCDate(day);
15507             test = function(){
15508                 return new_month != new_date.getUTCMonth();
15509             };
15510         }
15511         // Common date-resetting loop -- if date is beyond end of month, make it
15512         // end of month
15513         while (test()){
15514             new_date.setUTCDate(--day);
15515             new_date.setUTCMonth(new_month);
15516         }
15517         return new_date;
15518     },
15519
15520     moveYear: function(date, dir)
15521     {
15522         return this.moveMonth(date, dir*12);
15523     },
15524
15525     dateWithinRange: function(date)
15526     {
15527         return date >= this.startDate && date <= this.endDate;
15528     },
15529
15530     
15531     remove: function() 
15532     {
15533         this.picker().remove();
15534     }
15535    
15536 });
15537
15538 Roo.apply(Roo.bootstrap.DateField,  {
15539     
15540     head : {
15541         tag: 'thead',
15542         cn: [
15543         {
15544             tag: 'tr',
15545             cn: [
15546             {
15547                 tag: 'th',
15548                 cls: 'prev',
15549                 html: '<i class="fa fa-arrow-left"/>'
15550             },
15551             {
15552                 tag: 'th',
15553                 cls: 'switch',
15554                 colspan: '5'
15555             },
15556             {
15557                 tag: 'th',
15558                 cls: 'next',
15559                 html: '<i class="fa fa-arrow-right"/>'
15560             }
15561
15562             ]
15563         }
15564         ]
15565     },
15566     
15567     content : {
15568         tag: 'tbody',
15569         cn: [
15570         {
15571             tag: 'tr',
15572             cn: [
15573             {
15574                 tag: 'td',
15575                 colspan: '7'
15576             }
15577             ]
15578         }
15579         ]
15580     },
15581     
15582     footer : {
15583         tag: 'tfoot',
15584         cn: [
15585         {
15586             tag: 'tr',
15587             cn: [
15588             {
15589                 tag: 'th',
15590                 colspan: '7',
15591                 cls: 'today'
15592             }
15593                     
15594             ]
15595         }
15596         ]
15597     },
15598     
15599     dates:{
15600         en: {
15601             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15602             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15603             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15604             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15605             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15606             today: "Today"
15607         }
15608     },
15609     
15610     modes: [
15611     {
15612         clsName: 'days',
15613         navFnc: 'Month',
15614         navStep: 1
15615     },
15616     {
15617         clsName: 'months',
15618         navFnc: 'FullYear',
15619         navStep: 1
15620     },
15621     {
15622         clsName: 'years',
15623         navFnc: 'FullYear',
15624         navStep: 10
15625     }]
15626 });
15627
15628 Roo.apply(Roo.bootstrap.DateField,  {
15629   
15630     template : {
15631         tag: 'div',
15632         cls: 'datepicker dropdown-menu roo-dynamic',
15633         cn: [
15634         {
15635             tag: 'div',
15636             cls: 'datepicker-days',
15637             cn: [
15638             {
15639                 tag: 'table',
15640                 cls: 'table-condensed',
15641                 cn:[
15642                 Roo.bootstrap.DateField.head,
15643                 {
15644                     tag: 'tbody'
15645                 },
15646                 Roo.bootstrap.DateField.footer
15647                 ]
15648             }
15649             ]
15650         },
15651         {
15652             tag: 'div',
15653             cls: 'datepicker-months',
15654             cn: [
15655             {
15656                 tag: 'table',
15657                 cls: 'table-condensed',
15658                 cn:[
15659                 Roo.bootstrap.DateField.head,
15660                 Roo.bootstrap.DateField.content,
15661                 Roo.bootstrap.DateField.footer
15662                 ]
15663             }
15664             ]
15665         },
15666         {
15667             tag: 'div',
15668             cls: 'datepicker-years',
15669             cn: [
15670             {
15671                 tag: 'table',
15672                 cls: 'table-condensed',
15673                 cn:[
15674                 Roo.bootstrap.DateField.head,
15675                 Roo.bootstrap.DateField.content,
15676                 Roo.bootstrap.DateField.footer
15677                 ]
15678             }
15679             ]
15680         }
15681         ]
15682     }
15683 });
15684
15685  
15686
15687  /*
15688  * - LGPL
15689  *
15690  * TimeField
15691  * 
15692  */
15693
15694 /**
15695  * @class Roo.bootstrap.TimeField
15696  * @extends Roo.bootstrap.Input
15697  * Bootstrap DateField class
15698  * 
15699  * 
15700  * @constructor
15701  * Create a new TimeField
15702  * @param {Object} config The config object
15703  */
15704
15705 Roo.bootstrap.TimeField = function(config){
15706     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15707     this.addEvents({
15708             /**
15709              * @event show
15710              * Fires when this field show.
15711              * @param {Roo.bootstrap.DateField} thisthis
15712              * @param {Mixed} date The date value
15713              */
15714             show : true,
15715             /**
15716              * @event show
15717              * Fires when this field hide.
15718              * @param {Roo.bootstrap.DateField} this
15719              * @param {Mixed} date The date value
15720              */
15721             hide : true,
15722             /**
15723              * @event select
15724              * Fires when select a date.
15725              * @param {Roo.bootstrap.DateField} this
15726              * @param {Mixed} date The date value
15727              */
15728             select : true
15729         });
15730 };
15731
15732 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15733     
15734     /**
15735      * @cfg {String} format
15736      * The default time format string which can be overriden for localization support.  The format must be
15737      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15738      */
15739     format : "H:i",
15740        
15741     onRender: function(ct, position)
15742     {
15743         
15744         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15745                 
15746         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15747         
15748         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15749         
15750         this.pop = this.picker().select('>.datepicker-time',true).first();
15751         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15752         
15753         this.picker().on('mousedown', this.onMousedown, this);
15754         this.picker().on('click', this.onClick, this);
15755         
15756         this.picker().addClass('datepicker-dropdown');
15757     
15758         this.fillTime();
15759         this.update();
15760             
15761         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15762         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15763         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15764         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15765         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15766         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15767
15768     },
15769     
15770     fireKey: function(e){
15771         if (!this.picker().isVisible()){
15772             if (e.keyCode == 27) { // allow escape to hide and re-show picker
15773                 this.show();
15774             }
15775             return;
15776         }
15777
15778         e.preventDefault();
15779         
15780         switch(e.keyCode){
15781             case 27: // escape
15782                 this.hide();
15783                 break;
15784             case 37: // left
15785             case 39: // right
15786                 this.onTogglePeriod();
15787                 break;
15788             case 38: // up
15789                 this.onIncrementMinutes();
15790                 break;
15791             case 40: // down
15792                 this.onDecrementMinutes();
15793                 break;
15794             case 13: // enter
15795             case 9: // tab
15796                 this.setTime();
15797                 break;
15798         }
15799     },
15800     
15801     onClick: function(e) {
15802         e.stopPropagation();
15803         e.preventDefault();
15804     },
15805     
15806     picker : function()
15807     {
15808         return this.el.select('.datepicker', true).first();
15809     },
15810     
15811     fillTime: function()
15812     {    
15813         var time = this.pop.select('tbody', true).first();
15814         
15815         time.dom.innerHTML = '';
15816         
15817         time.createChild({
15818             tag: 'tr',
15819             cn: [
15820                 {
15821                     tag: 'td',
15822                     cn: [
15823                         {
15824                             tag: 'a',
15825                             href: '#',
15826                             cls: 'btn',
15827                             cn: [
15828                                 {
15829                                     tag: 'span',
15830                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15831                                 }
15832                             ]
15833                         } 
15834                     ]
15835                 },
15836                 {
15837                     tag: 'td',
15838                     cls: 'separator'
15839                 },
15840                 {
15841                     tag: 'td',
15842                     cn: [
15843                         {
15844                             tag: 'a',
15845                             href: '#',
15846                             cls: 'btn',
15847                             cn: [
15848                                 {
15849                                     tag: 'span',
15850                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15851                                 }
15852                             ]
15853                         }
15854                     ]
15855                 },
15856                 {
15857                     tag: 'td',
15858                     cls: 'separator'
15859                 }
15860             ]
15861         });
15862         
15863         time.createChild({
15864             tag: 'tr',
15865             cn: [
15866                 {
15867                     tag: 'td',
15868                     cn: [
15869                         {
15870                             tag: 'span',
15871                             cls: 'timepicker-hour',
15872                             html: '00'
15873                         }  
15874                     ]
15875                 },
15876                 {
15877                     tag: 'td',
15878                     cls: 'separator',
15879                     html: ':'
15880                 },
15881                 {
15882                     tag: 'td',
15883                     cn: [
15884                         {
15885                             tag: 'span',
15886                             cls: 'timepicker-minute',
15887                             html: '00'
15888                         }  
15889                     ]
15890                 },
15891                 {
15892                     tag: 'td',
15893                     cls: 'separator'
15894                 },
15895                 {
15896                     tag: 'td',
15897                     cn: [
15898                         {
15899                             tag: 'button',
15900                             type: 'button',
15901                             cls: 'btn btn-primary period',
15902                             html: 'AM'
15903                             
15904                         }
15905                     ]
15906                 }
15907             ]
15908         });
15909         
15910         time.createChild({
15911             tag: 'tr',
15912             cn: [
15913                 {
15914                     tag: 'td',
15915                     cn: [
15916                         {
15917                             tag: 'a',
15918                             href: '#',
15919                             cls: 'btn',
15920                             cn: [
15921                                 {
15922                                     tag: 'span',
15923                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15924                                 }
15925                             ]
15926                         }
15927                     ]
15928                 },
15929                 {
15930                     tag: 'td',
15931                     cls: 'separator'
15932                 },
15933                 {
15934                     tag: 'td',
15935                     cn: [
15936                         {
15937                             tag: 'a',
15938                             href: '#',
15939                             cls: 'btn',
15940                             cn: [
15941                                 {
15942                                     tag: 'span',
15943                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15944                                 }
15945                             ]
15946                         }
15947                     ]
15948                 },
15949                 {
15950                     tag: 'td',
15951                     cls: 'separator'
15952                 }
15953             ]
15954         });
15955         
15956     },
15957     
15958     update: function()
15959     {
15960         
15961         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15962         
15963         this.fill();
15964     },
15965     
15966     fill: function() 
15967     {
15968         var hours = this.time.getHours();
15969         var minutes = this.time.getMinutes();
15970         var period = 'AM';
15971         
15972         if(hours > 11){
15973             period = 'PM';
15974         }
15975         
15976         if(hours == 0){
15977             hours = 12;
15978         }
15979         
15980         
15981         if(hours > 12){
15982             hours = hours - 12;
15983         }
15984         
15985         if(hours < 10){
15986             hours = '0' + hours;
15987         }
15988         
15989         if(minutes < 10){
15990             minutes = '0' + minutes;
15991         }
15992         
15993         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15994         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15995         this.pop.select('button', true).first().dom.innerHTML = period;
15996         
15997     },
15998     
15999     place: function()
16000     {   
16001         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16002         
16003         var cls = ['bottom'];
16004         
16005         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16006             cls.pop();
16007             cls.push('top');
16008         }
16009         
16010         cls.push('right');
16011         
16012         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16013             cls.pop();
16014             cls.push('left');
16015         }
16016         
16017         this.picker().addClass(cls.join('-'));
16018         
16019         var _this = this;
16020         
16021         Roo.each(cls, function(c){
16022             if(c == 'bottom'){
16023                 _this.picker().setTop(_this.inputEl().getHeight());
16024                 return;
16025             }
16026             if(c == 'top'){
16027                 _this.picker().setTop(0 - _this.picker().getHeight());
16028                 return;
16029             }
16030             
16031             if(c == 'left'){
16032                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16033                 return;
16034             }
16035             if(c == 'right'){
16036                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16037                 return;
16038             }
16039         });
16040         
16041     },
16042   
16043     onFocus : function()
16044     {
16045         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16046         this.show();
16047     },
16048     
16049     onBlur : function()
16050     {
16051         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16052         this.hide();
16053     },
16054     
16055     show : function()
16056     {
16057         this.picker().show();
16058         this.pop.show();
16059         this.update();
16060         this.place();
16061         
16062         this.fireEvent('show', this, this.date);
16063     },
16064     
16065     hide : function()
16066     {
16067         this.picker().hide();
16068         this.pop.hide();
16069         
16070         this.fireEvent('hide', this, this.date);
16071     },
16072     
16073     setTime : function()
16074     {
16075         this.hide();
16076         this.setValue(this.time.format(this.format));
16077         
16078         this.fireEvent('select', this, this.date);
16079         
16080         
16081     },
16082     
16083     onMousedown: function(e){
16084         e.stopPropagation();
16085         e.preventDefault();
16086     },
16087     
16088     onIncrementHours: function()
16089     {
16090         Roo.log('onIncrementHours');
16091         this.time = this.time.add(Date.HOUR, 1);
16092         this.update();
16093         
16094     },
16095     
16096     onDecrementHours: function()
16097     {
16098         Roo.log('onDecrementHours');
16099         this.time = this.time.add(Date.HOUR, -1);
16100         this.update();
16101     },
16102     
16103     onIncrementMinutes: function()
16104     {
16105         Roo.log('onIncrementMinutes');
16106         this.time = this.time.add(Date.MINUTE, 1);
16107         this.update();
16108     },
16109     
16110     onDecrementMinutes: function()
16111     {
16112         Roo.log('onDecrementMinutes');
16113         this.time = this.time.add(Date.MINUTE, -1);
16114         this.update();
16115     },
16116     
16117     onTogglePeriod: function()
16118     {
16119         Roo.log('onTogglePeriod');
16120         this.time = this.time.add(Date.HOUR, 12);
16121         this.update();
16122     }
16123     
16124    
16125 });
16126
16127 Roo.apply(Roo.bootstrap.TimeField,  {
16128     
16129     content : {
16130         tag: 'tbody',
16131         cn: [
16132             {
16133                 tag: 'tr',
16134                 cn: [
16135                 {
16136                     tag: 'td',
16137                     colspan: '7'
16138                 }
16139                 ]
16140             }
16141         ]
16142     },
16143     
16144     footer : {
16145         tag: 'tfoot',
16146         cn: [
16147             {
16148                 tag: 'tr',
16149                 cn: [
16150                 {
16151                     tag: 'th',
16152                     colspan: '7',
16153                     cls: '',
16154                     cn: [
16155                         {
16156                             tag: 'button',
16157                             cls: 'btn btn-info ok',
16158                             html: 'OK'
16159                         }
16160                     ]
16161                 }
16162
16163                 ]
16164             }
16165         ]
16166     }
16167 });
16168
16169 Roo.apply(Roo.bootstrap.TimeField,  {
16170   
16171     template : {
16172         tag: 'div',
16173         cls: 'datepicker dropdown-menu',
16174         cn: [
16175             {
16176                 tag: 'div',
16177                 cls: 'datepicker-time',
16178                 cn: [
16179                 {
16180                     tag: 'table',
16181                     cls: 'table-condensed',
16182                     cn:[
16183                     Roo.bootstrap.TimeField.content,
16184                     Roo.bootstrap.TimeField.footer
16185                     ]
16186                 }
16187                 ]
16188             }
16189         ]
16190     }
16191 });
16192
16193  
16194
16195  /*
16196  * - LGPL
16197  *
16198  * MonthField
16199  * 
16200  */
16201
16202 /**
16203  * @class Roo.bootstrap.MonthField
16204  * @extends Roo.bootstrap.Input
16205  * Bootstrap MonthField class
16206  * 
16207  * @cfg {String} language default en
16208  * 
16209  * @constructor
16210  * Create a new MonthField
16211  * @param {Object} config The config object
16212  */
16213
16214 Roo.bootstrap.MonthField = function(config){
16215     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16216     
16217     this.addEvents({
16218         /**
16219          * @event show
16220          * Fires when this field show.
16221          * @param {Roo.bootstrap.MonthField} this
16222          * @param {Mixed} date The date value
16223          */
16224         show : true,
16225         /**
16226          * @event show
16227          * Fires when this field hide.
16228          * @param {Roo.bootstrap.MonthField} this
16229          * @param {Mixed} date The date value
16230          */
16231         hide : true,
16232         /**
16233          * @event select
16234          * Fires when select a date.
16235          * @param {Roo.bootstrap.MonthField} this
16236          * @param {Mixed} date The date value
16237          */
16238         select : true
16239     });
16240 };
16241
16242 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16243     
16244     onRender: function(ct, position)
16245     {
16246         
16247         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16248         
16249         this.language = this.language || 'en';
16250         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16251         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16252         
16253         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16254         this.isInline = false;
16255         this.isInput = true;
16256         this.component = this.el.select('.add-on', true).first() || false;
16257         this.component = (this.component && this.component.length === 0) ? false : this.component;
16258         this.hasInput = this.component && this.inputEL().length;
16259         
16260         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16261         
16262         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16263         
16264         this.picker().on('mousedown', this.onMousedown, this);
16265         this.picker().on('click', this.onClick, this);
16266         
16267         this.picker().addClass('datepicker-dropdown');
16268         
16269         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16270             v.setStyle('width', '189px');
16271         });
16272         
16273         this.fillMonths();
16274         
16275         this.update();
16276         
16277         if(this.isInline) {
16278             this.show();
16279         }
16280         
16281     },
16282     
16283     setValue: function(v)
16284     {   
16285         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16286         
16287         this.update();
16288
16289         this.fireEvent('select', this, this.date);
16290         
16291     },
16292     
16293     getValue: function()
16294     {
16295         return this.value;
16296     },
16297     
16298     onClick: function(e) 
16299     {
16300         e.stopPropagation();
16301         e.preventDefault();
16302         
16303         var target = e.getTarget();
16304         
16305         if(target.nodeName.toLowerCase() === 'i'){
16306             target = Roo.get(target).dom.parentNode;
16307         }
16308         
16309         var nodeName = target.nodeName;
16310         var className = target.className;
16311         var html = target.innerHTML;
16312         
16313         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16314             return;
16315         }
16316         
16317         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16318         
16319         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16320         
16321         this.hide();
16322                         
16323     },
16324     
16325     picker : function()
16326     {
16327         return this.pickerEl;
16328     },
16329     
16330     fillMonths: function()
16331     {    
16332         var i = 0;
16333         var months = this.picker().select('>.datepicker-months td', true).first();
16334         
16335         months.dom.innerHTML = '';
16336         
16337         while (i < 12) {
16338             var month = {
16339                 tag: 'span',
16340                 cls: 'month',
16341                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16342             }
16343             
16344             months.createChild(month);
16345         }
16346         
16347     },
16348     
16349     update: function()
16350     {
16351         var _this = this;
16352         
16353         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16354             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16355         }
16356         
16357         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16358             e.removeClass('active');
16359             
16360             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16361                 e.addClass('active');
16362             }
16363         })
16364     },
16365     
16366     place: function()
16367     {
16368         if(this.isInline) return;
16369         
16370         this.picker().removeClass(['bottom', 'top']);
16371         
16372         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16373             /*
16374              * place to the top of element!
16375              *
16376              */
16377             
16378             this.picker().addClass('top');
16379             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16380             
16381             return;
16382         }
16383         
16384         this.picker().addClass('bottom');
16385         
16386         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16387     },
16388     
16389     onFocus : function()
16390     {
16391         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
16392         this.show();
16393     },
16394     
16395     onBlur : function()
16396     {
16397         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
16398         
16399         var d = this.inputEl().getValue();
16400         
16401         this.setValue(d);
16402                 
16403         this.hide();
16404     },
16405     
16406     show : function()
16407     {
16408         this.picker().show();
16409         this.picker().select('>.datepicker-months', true).first().show();
16410         this.update();
16411         this.place();
16412         
16413         this.fireEvent('show', this, this.date);
16414     },
16415     
16416     hide : function()
16417     {
16418         if(this.isInline) return;
16419         this.picker().hide();
16420         this.fireEvent('hide', this, this.date);
16421         
16422     },
16423     
16424     onMousedown: function(e)
16425     {
16426         e.stopPropagation();
16427         e.preventDefault();
16428     },
16429     
16430     keyup: function(e)
16431     {
16432         Roo.bootstrap.MonthField.superclass.keyup.call(this);
16433         this.update();
16434     },
16435
16436     fireKey: function(e)
16437     {
16438         if (!this.picker().isVisible()){
16439             if (e.keyCode == 27) // allow escape to hide and re-show picker
16440                 this.show();
16441             return;
16442         }
16443         
16444         var dir;
16445         
16446         switch(e.keyCode){
16447             case 27: // escape
16448                 this.hide();
16449                 e.preventDefault();
16450                 break;
16451             case 37: // left
16452             case 39: // right
16453                 dir = e.keyCode == 37 ? -1 : 1;
16454                 
16455                 this.vIndex = this.vIndex + dir;
16456                 
16457                 if(this.vIndex < 0){
16458                     this.vIndex = 0;
16459                 }
16460                 
16461                 if(this.vIndex > 11){
16462                     this.vIndex = 11;
16463                 }
16464                 
16465                 if(isNaN(this.vIndex)){
16466                     this.vIndex = 0;
16467                 }
16468                 
16469                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16470                 
16471                 break;
16472             case 38: // up
16473             case 40: // down
16474                 
16475                 dir = e.keyCode == 38 ? -1 : 1;
16476                 
16477                 this.vIndex = this.vIndex + dir * 4;
16478                 
16479                 if(this.vIndex < 0){
16480                     this.vIndex = 0;
16481                 }
16482                 
16483                 if(this.vIndex > 11){
16484                     this.vIndex = 11;
16485                 }
16486                 
16487                 if(isNaN(this.vIndex)){
16488                     this.vIndex = 0;
16489                 }
16490                 
16491                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16492                 break;
16493                 
16494             case 13: // enter
16495                 
16496                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16497                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16498                 }
16499                 
16500                 this.hide();
16501                 e.preventDefault();
16502                 break;
16503             case 9: // tab
16504                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16505                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16506                 }
16507                 this.hide();
16508                 break;
16509             case 16: // shift
16510             case 17: // ctrl
16511             case 18: // alt
16512                 break;
16513             default :
16514                 this.hide();
16515                 
16516         }
16517     },
16518     
16519     remove: function() 
16520     {
16521         this.picker().remove();
16522     }
16523    
16524 });
16525
16526 Roo.apply(Roo.bootstrap.MonthField,  {
16527     
16528     content : {
16529         tag: 'tbody',
16530         cn: [
16531         {
16532             tag: 'tr',
16533             cn: [
16534             {
16535                 tag: 'td',
16536                 colspan: '7'
16537             }
16538             ]
16539         }
16540         ]
16541     },
16542     
16543     dates:{
16544         en: {
16545             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16546             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
16547         }
16548     }
16549 });
16550
16551 Roo.apply(Roo.bootstrap.MonthField,  {
16552   
16553     template : {
16554         tag: 'div',
16555         cls: 'datepicker dropdown-menu roo-dynamic',
16556         cn: [
16557             {
16558                 tag: 'div',
16559                 cls: 'datepicker-months',
16560                 cn: [
16561                 {
16562                     tag: 'table',
16563                     cls: 'table-condensed',
16564                     cn:[
16565                         Roo.bootstrap.DateField.content
16566                     ]
16567                 }
16568                 ]
16569             }
16570         ]
16571     }
16572 });
16573
16574  
16575
16576  
16577  /*
16578  * - LGPL
16579  *
16580  * CheckBox
16581  * 
16582  */
16583
16584 /**
16585  * @class Roo.bootstrap.CheckBox
16586  * @extends Roo.bootstrap.Input
16587  * Bootstrap CheckBox class
16588  * 
16589  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
16590  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
16591  * @cfg {String} boxLabel The text that appears beside the checkbox
16592  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
16593  * @cfg {Boolean} checked initnal the element
16594  * @cfg {Boolean} inline inline the element (default false)
16595  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
16596  * 
16597  * @constructor
16598  * Create a new CheckBox
16599  * @param {Object} config The config object
16600  */
16601
16602 Roo.bootstrap.CheckBox = function(config){
16603     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
16604    
16605     this.addEvents({
16606         /**
16607         * @event check
16608         * Fires when the element is checked or unchecked.
16609         * @param {Roo.bootstrap.CheckBox} this This input
16610         * @param {Boolean} checked The new checked value
16611         */
16612        check : true
16613     });
16614     
16615 };
16616
16617 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
16618     
16619     invalidClass : "has-warning",
16620     validClass : "has-success",
16621     inputType: 'checkbox',
16622     inputValue: 1,
16623     valueOff: 0,
16624     boxLabel: false,
16625     checked: false,
16626     weight : false,
16627     inline: false,
16628     
16629     getAutoCreate : function()
16630     {
16631         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16632         
16633         var id = Roo.id();
16634         
16635         var cfg = {};
16636         
16637         cfg.cls = 'form-group ' + this.inputType; //input-group
16638         
16639         if(this.inline){
16640             cfg.cls += ' ' + this.inputType + '-inline';
16641         }
16642         
16643         var input =  {
16644             tag: 'input',
16645             id : id,
16646             type : this.inputType,
16647             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
16648             cls : 'roo-' + this.inputType, //'form-box',
16649             placeholder : this.placeholder || ''
16650             
16651         };
16652         
16653         if (this.weight) { // Validity check?
16654             cfg.cls += " " + this.inputType + "-" + this.weight;
16655         }
16656         
16657         if (this.disabled) {
16658             input.disabled=true;
16659         }
16660         
16661         if(this.checked){
16662             input.checked = this.checked;
16663         }
16664         
16665         if (this.name) {
16666             input.name = this.name;
16667         }
16668         
16669         if (this.size) {
16670             input.cls += ' input-' + this.size;
16671         }
16672         
16673         var settings=this;
16674         
16675         ['xs','sm','md','lg'].map(function(size){
16676             if (settings[size]) {
16677                 cfg.cls += ' col-' + size + '-' + settings[size];
16678             }
16679         });
16680         
16681         var inputblock = input;
16682          
16683         if (this.before || this.after) {
16684             
16685             inputblock = {
16686                 cls : 'input-group',
16687                 cn :  [] 
16688             };
16689             
16690             if (this.before) {
16691                 inputblock.cn.push({
16692                     tag :'span',
16693                     cls : 'input-group-addon',
16694                     html : this.before
16695                 });
16696             }
16697             
16698             inputblock.cn.push(input);
16699             
16700             if (this.after) {
16701                 inputblock.cn.push({
16702                     tag :'span',
16703                     cls : 'input-group-addon',
16704                     html : this.after
16705                 });
16706             }
16707             
16708         }
16709         
16710         if (align ==='left' && this.fieldLabel.length) {
16711                 Roo.log("left and has label");
16712                 cfg.cn = [
16713                     
16714                     {
16715                         tag: 'label',
16716                         'for' :  id,
16717                         cls : 'control-label col-md-' + this.labelWidth,
16718                         html : this.fieldLabel
16719                         
16720                     },
16721                     {
16722                         cls : "col-md-" + (12 - this.labelWidth), 
16723                         cn: [
16724                             inputblock
16725                         ]
16726                     }
16727                     
16728                 ];
16729         } else if ( this.fieldLabel.length) {
16730                 Roo.log(" label");
16731                 cfg.cn = [
16732                    
16733                     {
16734                         tag: this.boxLabel ? 'span' : 'label',
16735                         'for': id,
16736                         cls: 'control-label box-input-label',
16737                         //cls : 'input-group-addon',
16738                         html : this.fieldLabel
16739                         
16740                     },
16741                     
16742                     inputblock
16743                     
16744                 ];
16745
16746         } else {
16747             
16748                 Roo.log(" no label && no align");
16749                 cfg.cn = [  inputblock ] ;
16750                 
16751                 
16752         }
16753         if(this.boxLabel){
16754              var boxLabelCfg = {
16755                 tag: 'label',
16756                 //'for': id, // box label is handled by onclick - so no for...
16757                 cls: 'box-label',
16758                 html: this.boxLabel
16759             }
16760             
16761             if(this.tooltip){
16762                 boxLabelCfg.tooltip = this.tooltip;
16763             }
16764              
16765             cfg.cn.push(boxLabelCfg);
16766         }
16767         
16768         
16769        
16770         return cfg;
16771         
16772     },
16773     
16774     /**
16775      * return the real input element.
16776      */
16777     inputEl: function ()
16778     {
16779         return this.el.select('input.roo-' + this.inputType,true).first();
16780     },
16781     
16782     labelEl: function()
16783     {
16784         return this.el.select('label.control-label',true).first();
16785     },
16786     /* depricated... */
16787     
16788     label: function()
16789     {
16790         return this.labelEl();
16791     },
16792     
16793     initEvents : function()
16794     {
16795 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16796         
16797         this.inputEl().on('click', this.onClick,  this);
16798         
16799         if (this.boxLabel) { 
16800             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
16801         }
16802         
16803         this.startValue = this.getValue();
16804         
16805         if(this.groupId){
16806             Roo.bootstrap.CheckBox.register(this);
16807         }
16808     },
16809     
16810     onClick : function()
16811     {   
16812         this.setChecked(!this.checked);
16813     },
16814     
16815     setChecked : function(state,suppressEvent)
16816     {
16817         this.startValue = this.getValue();
16818         
16819         if(this.inputType == 'radio'){
16820             
16821             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
16822                 e.dom.checked = false;
16823             });
16824             
16825             this.inputEl().dom.checked = true;
16826             
16827             this.inputEl().dom.value = this.inputValue;
16828             
16829             if(suppressEvent !== true){
16830                 this.fireEvent('check', this, true);
16831             }
16832             
16833             return;
16834         }
16835         
16836         this.checked = state;
16837         
16838         this.inputEl().dom.checked = state;
16839         
16840         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16841         
16842         if(suppressEvent !== true){
16843             this.fireEvent('check', this, state);
16844         }
16845     },
16846     
16847     getValue : function()
16848     {
16849         if(this.inputType == 'radio'){
16850             return this.getGroupValue();
16851         }
16852         
16853         return this.inputEl().getValue();
16854         
16855     },
16856     
16857     getGroupValue : function()
16858     {
16859         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
16860             return '';
16861         }
16862         
16863         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
16864     },
16865     
16866     setValue : function(v,suppressEvent)
16867     {
16868         if(this.inputType == 'radio'){
16869             this.setGroupValue(v, suppressEvent);
16870             this.validate();
16871             return;
16872         }
16873         
16874         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16875         this.validate();
16876     },
16877     
16878     setGroupValue : function(v, suppressEvent)
16879     {
16880         this.startValue = this.getValue();
16881         
16882         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
16883             e.dom.checked = false;
16884             
16885             if(e.dom.value == v){
16886                 e.dom.checked = true;
16887             }
16888         });
16889         
16890         if(suppressEvent !== true){
16891             this.fireEvent('check', this, true);
16892         }
16893
16894         return;
16895     },
16896     
16897     validate : function()
16898     {
16899         if(
16900                 this.disabled || 
16901                 (this.inputType == 'radio' && this.getValue().length) ||
16902                 (this.inputType == 'checkbox' && this.validateGroup())
16903         ){
16904             this.markValid();
16905             return true;
16906         }
16907         
16908         this.markInvalid();
16909         return false;
16910     },
16911     
16912     validateGroup : function()
16913     {
16914         if(!this.groupId){
16915             return (this.getValue() == this.inputValue) ? true : false;
16916         }
16917         
16918         var group = Roo.bootstrap.CheckBox.get(this.groupId);
16919         
16920         if(!group){
16921             return false;
16922         }
16923         
16924         var r = false;
16925         
16926         for(var i in group){
16927             if(r){
16928                 break;
16929             }
16930             
16931             r = (group[i].getValue() == group[i].inputValue) ? true : false;
16932         }
16933         
16934         return r;
16935     },
16936     
16937     /**
16938      * Mark this field as valid
16939      */
16940     markValid : function()
16941     {
16942         var _this = this;
16943         
16944         this.fireEvent('valid', this);
16945         
16946         if(this.inputType == 'radio'){
16947             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
16948                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
16949                 e.findParent('.form-group', false, true).addClass(_this.validClass);
16950             });
16951             
16952             return;
16953         }
16954         
16955         if(!this.groupId){
16956             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
16957             this.el.findParent('.form-group', false, true).addClass(this.validClass);
16958             return;
16959         }
16960         
16961         var group = Roo.bootstrap.CheckBox.get(this.groupId);
16962             
16963         if(!group){
16964             return;
16965         }
16966         
16967         for(var i in group){
16968             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
16969             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
16970         }
16971     },
16972     
16973      /**
16974      * Mark this field as invalid
16975      * @param {String} msg The validation message
16976      */
16977     markInvalid : function(msg)
16978     {
16979         var _this = this;
16980         
16981         this.fireEvent('invalid', this, msg);
16982         
16983         if(this.inputType == 'radio'){
16984             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
16985                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
16986                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
16987             });
16988             
16989             return;
16990         }
16991         
16992         if(!this.groupId){
16993             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
16994             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
16995             return;
16996         }
16997         
16998         var group = Roo.bootstrap.CheckBox.get(this.groupId);
16999             
17000         if(!group){
17001             return;
17002         }
17003         
17004         for(var i in group){
17005             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17006             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17007         }
17008         
17009     }
17010     
17011 });
17012
17013 Roo.apply(Roo.bootstrap.CheckBox, {
17014     
17015     groups: {},
17016     
17017      /**
17018     * register a CheckBox Group
17019     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17020     */
17021     register : function(checkbox)
17022     {
17023         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17024             this.groups[checkbox.groupId] = {};
17025         }
17026         
17027         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17028             return;
17029         }
17030         
17031         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17032         
17033     },
17034     /**
17035     * fetch a CheckBox Group based on the group ID
17036     * @param {string} the group ID
17037     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17038     */
17039     get: function(groupId) {
17040         if (typeof(this.groups[groupId]) == 'undefined') {
17041             return false;
17042         }
17043         
17044         return this.groups[groupId] ;
17045     }
17046     
17047     
17048 });
17049 /*
17050  * - LGPL
17051  *
17052  * Radio
17053  *
17054  *
17055  * not inline
17056  *<div class="radio">
17057   <label>
17058     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17059     Option one is this and that&mdash;be sure to include why it's great
17060   </label>
17061 </div>
17062  *
17063  *
17064  *inline
17065  *<span>
17066  *<label class="radio-inline">fieldLabel</label>
17067  *<label class="radio-inline">
17068   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17069 </label>
17070 <span>
17071  * 
17072  * 
17073  */
17074
17075 /**
17076  * @class Roo.bootstrap.Radio
17077  * @extends Roo.bootstrap.CheckBox
17078  * Bootstrap Radio class
17079
17080  * @constructor
17081  * Create a new Radio
17082  * @param {Object} config The config object
17083  */
17084
17085 Roo.bootstrap.Radio = function(config){
17086     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17087    
17088 };
17089
17090 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17091     
17092     inputType: 'radio',
17093     inputValue: '',
17094     valueOff: '',
17095     
17096     getAutoCreate : function()
17097     {
17098         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17099         align = align || 'left'; // default...
17100         
17101         
17102         
17103         var id = Roo.id();
17104         
17105         var cfg = {
17106                 tag : this.inline ? 'span' : 'div',
17107                 cls : '',
17108                 cn : []
17109         };
17110         
17111         var inline = this.inline ? ' radio-inline' : '';
17112         
17113         var lbl = {
17114                 tag: 'label' ,
17115                 // does not need for, as we wrap the input with it..
17116                 'for' : id,
17117                 cls : 'control-label box-label' + inline,
17118                 cn : []
17119         };
17120         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17121         
17122         var fieldLabel = {
17123             tag: 'label' ,
17124             //cls : 'control-label' + inline,
17125             html : this.fieldLabel,
17126             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17127         };
17128         
17129  
17130         
17131         
17132         var input =  {
17133             tag: 'input',
17134             id : id,
17135             type : this.inputType,
17136             //value : (!this.checked) ? this.valueOff : this.inputValue,
17137             value : this.inputValue,
17138             cls : 'roo-radio',
17139             placeholder : this.placeholder || '' // ?? needed????
17140             
17141         };
17142         if (this.weight) { // Validity check?
17143             input.cls += " radio-" + this.weight;
17144         }
17145         if (this.disabled) {
17146             input.disabled=true;
17147         }
17148         
17149         if(this.checked){
17150             input.checked = this.checked;
17151         }
17152         
17153         if (this.name) {
17154             input.name = this.name;
17155         }
17156         
17157         if (this.size) {
17158             input.cls += ' input-' + this.size;
17159         }
17160         
17161         //?? can span's inline have a width??
17162         
17163         var settings=this;
17164         ['xs','sm','md','lg'].map(function(size){
17165             if (settings[size]) {
17166                 cfg.cls += ' col-' + size + '-' + settings[size];
17167             }
17168         });
17169         
17170         var inputblock = input;
17171         
17172         if (this.before || this.after) {
17173             
17174             inputblock = {
17175                 cls : 'input-group',
17176                 tag : 'span',
17177                 cn :  [] 
17178             };
17179             if (this.before) {
17180                 inputblock.cn.push({
17181                     tag :'span',
17182                     cls : 'input-group-addon',
17183                     html : this.before
17184                 });
17185             }
17186             inputblock.cn.push(input);
17187             if (this.after) {
17188                 inputblock.cn.push({
17189                     tag :'span',
17190                     cls : 'input-group-addon',
17191                     html : this.after
17192                 });
17193             }
17194             
17195         };
17196         
17197         
17198         if (this.fieldLabel && this.fieldLabel.length) {
17199             cfg.cn.push(fieldLabel);
17200         }
17201        
17202         // normal bootstrap puts the input inside the label.
17203         // however with our styled version - it has to go after the input.
17204        
17205         //lbl.cn.push(inputblock);
17206         
17207         var lblwrap =  {
17208             tag: 'span',
17209             cls: 'radio' + inline,
17210             cn: [
17211                 inputblock,
17212                 lbl
17213             ]
17214         };
17215         
17216         cfg.cn.push( lblwrap);
17217         
17218         if(this.boxLabel){
17219             lbl.cn.push({
17220                 tag: 'span',
17221                 html: this.boxLabel
17222             })
17223         }
17224          
17225         
17226         return cfg;
17227         
17228     },
17229     
17230     initEvents : function()
17231     {
17232 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17233         
17234         this.inputEl().on('click', this.onClick,  this);
17235         if (this.boxLabel) {
17236             Roo.log('find label')
17237             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17238         }
17239         
17240     },
17241     
17242     inputEl: function ()
17243     {
17244         return this.el.select('input.roo-radio',true).first();
17245     },
17246     onClick : function()
17247     {   
17248         Roo.log("click");
17249         this.setChecked(true);
17250     },
17251     
17252     setChecked : function(state,suppressEvent)
17253     {
17254         if(state){
17255             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17256                 v.dom.checked = false;
17257             });
17258         }
17259         Roo.log(this.inputEl().dom);
17260         this.checked = state;
17261         this.inputEl().dom.checked = state;
17262         
17263         if(suppressEvent !== true){
17264             this.fireEvent('check', this, state);
17265         }
17266         
17267         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17268         
17269     },
17270     
17271     getGroupValue : function()
17272     {
17273         var value = '';
17274         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17275             if(v.dom.checked == true){
17276                 value = v.dom.value;
17277             }
17278         });
17279         
17280         return value;
17281     },
17282     
17283     /**
17284      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17285      * @return {Mixed} value The field value
17286      */
17287     getValue : function(){
17288         return this.getGroupValue();
17289     }
17290     
17291 });
17292
17293  
17294 //<script type="text/javascript">
17295
17296 /*
17297  * Based  Ext JS Library 1.1.1
17298  * Copyright(c) 2006-2007, Ext JS, LLC.
17299  * LGPL
17300  *
17301  */
17302  
17303 /**
17304  * @class Roo.HtmlEditorCore
17305  * @extends Roo.Component
17306  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17307  *
17308  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17309  */
17310
17311 Roo.HtmlEditorCore = function(config){
17312     
17313     
17314     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17315     
17316     
17317     this.addEvents({
17318         /**
17319          * @event initialize
17320          * Fires when the editor is fully initialized (including the iframe)
17321          * @param {Roo.HtmlEditorCore} this
17322          */
17323         initialize: true,
17324         /**
17325          * @event activate
17326          * Fires when the editor is first receives the focus. Any insertion must wait
17327          * until after this event.
17328          * @param {Roo.HtmlEditorCore} this
17329          */
17330         activate: true,
17331          /**
17332          * @event beforesync
17333          * Fires before the textarea is updated with content from the editor iframe. Return false
17334          * to cancel the sync.
17335          * @param {Roo.HtmlEditorCore} this
17336          * @param {String} html
17337          */
17338         beforesync: true,
17339          /**
17340          * @event beforepush
17341          * Fires before the iframe editor is updated with content from the textarea. Return false
17342          * to cancel the push.
17343          * @param {Roo.HtmlEditorCore} this
17344          * @param {String} html
17345          */
17346         beforepush: true,
17347          /**
17348          * @event sync
17349          * Fires when the textarea is updated with content from the editor iframe.
17350          * @param {Roo.HtmlEditorCore} this
17351          * @param {String} html
17352          */
17353         sync: true,
17354          /**
17355          * @event push
17356          * Fires when the iframe editor is updated with content from the textarea.
17357          * @param {Roo.HtmlEditorCore} this
17358          * @param {String} html
17359          */
17360         push: true,
17361         
17362         /**
17363          * @event editorevent
17364          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17365          * @param {Roo.HtmlEditorCore} this
17366          */
17367         editorevent: true
17368         
17369     });
17370     
17371     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
17372     
17373     // defaults : white / black...
17374     this.applyBlacklists();
17375     
17376     
17377     
17378 };
17379
17380
17381 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
17382
17383
17384      /**
17385      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
17386      */
17387     
17388     owner : false,
17389     
17390      /**
17391      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17392      *                        Roo.resizable.
17393      */
17394     resizable : false,
17395      /**
17396      * @cfg {Number} height (in pixels)
17397      */   
17398     height: 300,
17399    /**
17400      * @cfg {Number} width (in pixels)
17401      */   
17402     width: 500,
17403     
17404     /**
17405      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17406      * 
17407      */
17408     stylesheets: false,
17409     
17410     // id of frame..
17411     frameId: false,
17412     
17413     // private properties
17414     validationEvent : false,
17415     deferHeight: true,
17416     initialized : false,
17417     activated : false,
17418     sourceEditMode : false,
17419     onFocus : Roo.emptyFn,
17420     iframePad:3,
17421     hideMode:'offsets',
17422     
17423     clearUp: true,
17424     
17425     // blacklist + whitelisted elements..
17426     black: false,
17427     white: false,
17428      
17429     
17430
17431     /**
17432      * Protected method that will not generally be called directly. It
17433      * is called when the editor initializes the iframe with HTML contents. Override this method if you
17434      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
17435      */
17436     getDocMarkup : function(){
17437         // body styles..
17438         var st = '';
17439         
17440         // inherit styels from page...?? 
17441         if (this.stylesheets === false) {
17442             
17443             Roo.get(document.head).select('style').each(function(node) {
17444                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17445             });
17446             
17447             Roo.get(document.head).select('link').each(function(node) { 
17448                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17449             });
17450             
17451         } else if (!this.stylesheets.length) {
17452                 // simple..
17453                 st = '<style type="text/css">' +
17454                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17455                    '</style>';
17456         } else { 
17457             
17458         }
17459         
17460         st +=  '<style type="text/css">' +
17461             'IMG { cursor: pointer } ' +
17462         '</style>';
17463
17464         
17465         return '<html><head>' + st  +
17466             //<style type="text/css">' +
17467             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17468             //'</style>' +
17469             ' </head><body class="roo-htmleditor-body"></body></html>';
17470     },
17471
17472     // private
17473     onRender : function(ct, position)
17474     {
17475         var _t = this;
17476         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
17477         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
17478         
17479         
17480         this.el.dom.style.border = '0 none';
17481         this.el.dom.setAttribute('tabIndex', -1);
17482         this.el.addClass('x-hidden hide');
17483         
17484         
17485         
17486         if(Roo.isIE){ // fix IE 1px bogus margin
17487             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
17488         }
17489        
17490         
17491         this.frameId = Roo.id();
17492         
17493          
17494         
17495         var iframe = this.owner.wrap.createChild({
17496             tag: 'iframe',
17497             cls: 'form-control', // bootstrap..
17498             id: this.frameId,
17499             name: this.frameId,
17500             frameBorder : 'no',
17501             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
17502         }, this.el
17503         );
17504         
17505         
17506         this.iframe = iframe.dom;
17507
17508          this.assignDocWin();
17509         
17510         this.doc.designMode = 'on';
17511        
17512         this.doc.open();
17513         this.doc.write(this.getDocMarkup());
17514         this.doc.close();
17515
17516         
17517         var task = { // must defer to wait for browser to be ready
17518             run : function(){
17519                 //console.log("run task?" + this.doc.readyState);
17520                 this.assignDocWin();
17521                 if(this.doc.body || this.doc.readyState == 'complete'){
17522                     try {
17523                         this.doc.designMode="on";
17524                     } catch (e) {
17525                         return;
17526                     }
17527                     Roo.TaskMgr.stop(task);
17528                     this.initEditor.defer(10, this);
17529                 }
17530             },
17531             interval : 10,
17532             duration: 10000,
17533             scope: this
17534         };
17535         Roo.TaskMgr.start(task);
17536
17537     },
17538
17539     // private
17540     onResize : function(w, h)
17541     {
17542          Roo.log('resize: ' +w + ',' + h );
17543         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
17544         if(!this.iframe){
17545             return;
17546         }
17547         if(typeof w == 'number'){
17548             
17549             this.iframe.style.width = w + 'px';
17550         }
17551         if(typeof h == 'number'){
17552             
17553             this.iframe.style.height = h + 'px';
17554             if(this.doc){
17555                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
17556             }
17557         }
17558         
17559     },
17560
17561     /**
17562      * Toggles the editor between standard and source edit mode.
17563      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17564      */
17565     toggleSourceEdit : function(sourceEditMode){
17566         
17567         this.sourceEditMode = sourceEditMode === true;
17568         
17569         if(this.sourceEditMode){
17570  
17571             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
17572             
17573         }else{
17574             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
17575             //this.iframe.className = '';
17576             this.deferFocus();
17577         }
17578         //this.setSize(this.owner.wrap.getSize());
17579         //this.fireEvent('editmodechange', this, this.sourceEditMode);
17580     },
17581
17582     
17583   
17584
17585     /**
17586      * Protected method that will not generally be called directly. If you need/want
17587      * custom HTML cleanup, this is the method you should override.
17588      * @param {String} html The HTML to be cleaned
17589      * return {String} The cleaned HTML
17590      */
17591     cleanHtml : function(html){
17592         html = String(html);
17593         if(html.length > 5){
17594             if(Roo.isSafari){ // strip safari nonsense
17595                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
17596             }
17597         }
17598         if(html == '&nbsp;'){
17599             html = '';
17600         }
17601         return html;
17602     },
17603
17604     /**
17605      * HTML Editor -> Textarea
17606      * Protected method that will not generally be called directly. Syncs the contents
17607      * of the editor iframe with the textarea.
17608      */
17609     syncValue : function(){
17610         if(this.initialized){
17611             var bd = (this.doc.body || this.doc.documentElement);
17612             //this.cleanUpPaste(); -- this is done else where and causes havoc..
17613             var html = bd.innerHTML;
17614             if(Roo.isSafari){
17615                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
17616                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
17617                 if(m && m[1]){
17618                     html = '<div style="'+m[0]+'">' + html + '</div>';
17619                 }
17620             }
17621             html = this.cleanHtml(html);
17622             // fix up the special chars.. normaly like back quotes in word...
17623             // however we do not want to do this with chinese..
17624             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
17625                 var cc = b.charCodeAt();
17626                 if (
17627                     (cc >= 0x4E00 && cc < 0xA000 ) ||
17628                     (cc >= 0x3400 && cc < 0x4E00 ) ||
17629                     (cc >= 0xf900 && cc < 0xfb00 )
17630                 ) {
17631                         return b;
17632                 }
17633                 return "&#"+cc+";" 
17634             });
17635             if(this.owner.fireEvent('beforesync', this, html) !== false){
17636                 this.el.dom.value = html;
17637                 this.owner.fireEvent('sync', this, html);
17638             }
17639         }
17640     },
17641
17642     /**
17643      * Protected method that will not generally be called directly. Pushes the value of the textarea
17644      * into the iframe editor.
17645      */
17646     pushValue : function(){
17647         if(this.initialized){
17648             var v = this.el.dom.value.trim();
17649             
17650 //            if(v.length < 1){
17651 //                v = '&#160;';
17652 //            }
17653             
17654             if(this.owner.fireEvent('beforepush', this, v) !== false){
17655                 var d = (this.doc.body || this.doc.documentElement);
17656                 d.innerHTML = v;
17657                 this.cleanUpPaste();
17658                 this.el.dom.value = d.innerHTML;
17659                 this.owner.fireEvent('push', this, v);
17660             }
17661         }
17662     },
17663
17664     // private
17665     deferFocus : function(){
17666         this.focus.defer(10, this);
17667     },
17668
17669     // doc'ed in Field
17670     focus : function(){
17671         if(this.win && !this.sourceEditMode){
17672             this.win.focus();
17673         }else{
17674             this.el.focus();
17675         }
17676     },
17677     
17678     assignDocWin: function()
17679     {
17680         var iframe = this.iframe;
17681         
17682          if(Roo.isIE){
17683             this.doc = iframe.contentWindow.document;
17684             this.win = iframe.contentWindow;
17685         } else {
17686 //            if (!Roo.get(this.frameId)) {
17687 //                return;
17688 //            }
17689 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
17690 //            this.win = Roo.get(this.frameId).dom.contentWindow;
17691             
17692             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
17693                 return;
17694             }
17695             
17696             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
17697             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
17698         }
17699     },
17700     
17701     // private
17702     initEditor : function(){
17703         //console.log("INIT EDITOR");
17704         this.assignDocWin();
17705         
17706         
17707         
17708         this.doc.designMode="on";
17709         this.doc.open();
17710         this.doc.write(this.getDocMarkup());
17711         this.doc.close();
17712         
17713         var dbody = (this.doc.body || this.doc.documentElement);
17714         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
17715         // this copies styles from the containing element into thsi one..
17716         // not sure why we need all of this..
17717         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
17718         
17719         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
17720         //ss['background-attachment'] = 'fixed'; // w3c
17721         dbody.bgProperties = 'fixed'; // ie
17722         //Roo.DomHelper.applyStyles(dbody, ss);
17723         Roo.EventManager.on(this.doc, {
17724             //'mousedown': this.onEditorEvent,
17725             'mouseup': this.onEditorEvent,
17726             'dblclick': this.onEditorEvent,
17727             'click': this.onEditorEvent,
17728             'keyup': this.onEditorEvent,
17729             buffer:100,
17730             scope: this
17731         });
17732         if(Roo.isGecko){
17733             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
17734         }
17735         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
17736             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
17737         }
17738         this.initialized = true;
17739
17740         this.owner.fireEvent('initialize', this);
17741         this.pushValue();
17742     },
17743
17744     // private
17745     onDestroy : function(){
17746         
17747         
17748         
17749         if(this.rendered){
17750             
17751             //for (var i =0; i < this.toolbars.length;i++) {
17752             //    // fixme - ask toolbars for heights?
17753             //    this.toolbars[i].onDestroy();
17754            // }
17755             
17756             //this.wrap.dom.innerHTML = '';
17757             //this.wrap.remove();
17758         }
17759     },
17760
17761     // private
17762     onFirstFocus : function(){
17763         
17764         this.assignDocWin();
17765         
17766         
17767         this.activated = true;
17768          
17769     
17770         if(Roo.isGecko){ // prevent silly gecko errors
17771             this.win.focus();
17772             var s = this.win.getSelection();
17773             if(!s.focusNode || s.focusNode.nodeType != 3){
17774                 var r = s.getRangeAt(0);
17775                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
17776                 r.collapse(true);
17777                 this.deferFocus();
17778             }
17779             try{
17780                 this.execCmd('useCSS', true);
17781                 this.execCmd('styleWithCSS', false);
17782             }catch(e){}
17783         }
17784         this.owner.fireEvent('activate', this);
17785     },
17786
17787     // private
17788     adjustFont: function(btn){
17789         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
17790         //if(Roo.isSafari){ // safari
17791         //    adjust *= 2;
17792        // }
17793         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
17794         if(Roo.isSafari){ // safari
17795             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
17796             v =  (v < 10) ? 10 : v;
17797             v =  (v > 48) ? 48 : v;
17798             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
17799             
17800         }
17801         
17802         
17803         v = Math.max(1, v+adjust);
17804         
17805         this.execCmd('FontSize', v  );
17806     },
17807
17808     onEditorEvent : function(e){
17809         this.owner.fireEvent('editorevent', this, e);
17810       //  this.updateToolbar();
17811         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
17812     },
17813
17814     insertTag : function(tg)
17815     {
17816         // could be a bit smarter... -> wrap the current selected tRoo..
17817         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
17818             
17819             range = this.createRange(this.getSelection());
17820             var wrappingNode = this.doc.createElement(tg.toLowerCase());
17821             wrappingNode.appendChild(range.extractContents());
17822             range.insertNode(wrappingNode);
17823
17824             return;
17825             
17826             
17827             
17828         }
17829         this.execCmd("formatblock",   tg);
17830         
17831     },
17832     
17833     insertText : function(txt)
17834     {
17835         
17836         
17837         var range = this.createRange();
17838         range.deleteContents();
17839                //alert(Sender.getAttribute('label'));
17840                
17841         range.insertNode(this.doc.createTextNode(txt));
17842     } ,
17843     
17844      
17845
17846     /**
17847      * Executes a Midas editor command on the editor document and performs necessary focus and
17848      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
17849      * @param {String} cmd The Midas command
17850      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17851      */
17852     relayCmd : function(cmd, value){
17853         this.win.focus();
17854         this.execCmd(cmd, value);
17855         this.owner.fireEvent('editorevent', this);
17856         //this.updateToolbar();
17857         this.owner.deferFocus();
17858     },
17859
17860     /**
17861      * Executes a Midas editor command directly on the editor document.
17862      * For visual commands, you should use {@link #relayCmd} instead.
17863      * <b>This should only be called after the editor is initialized.</b>
17864      * @param {String} cmd The Midas command
17865      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17866      */
17867     execCmd : function(cmd, value){
17868         this.doc.execCommand(cmd, false, value === undefined ? null : value);
17869         this.syncValue();
17870     },
17871  
17872  
17873    
17874     /**
17875      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
17876      * to insert tRoo.
17877      * @param {String} text | dom node.. 
17878      */
17879     insertAtCursor : function(text)
17880     {
17881         
17882         
17883         
17884         if(!this.activated){
17885             return;
17886         }
17887         /*
17888         if(Roo.isIE){
17889             this.win.focus();
17890             var r = this.doc.selection.createRange();
17891             if(r){
17892                 r.collapse(true);
17893                 r.pasteHTML(text);
17894                 this.syncValue();
17895                 this.deferFocus();
17896             
17897             }
17898             return;
17899         }
17900         */
17901         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
17902             this.win.focus();
17903             
17904             
17905             // from jquery ui (MIT licenced)
17906             var range, node;
17907             var win = this.win;
17908             
17909             if (win.getSelection && win.getSelection().getRangeAt) {
17910                 range = win.getSelection().getRangeAt(0);
17911                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
17912                 range.insertNode(node);
17913             } else if (win.document.selection && win.document.selection.createRange) {
17914                 // no firefox support
17915                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17916                 win.document.selection.createRange().pasteHTML(txt);
17917             } else {
17918                 // no firefox support
17919                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17920                 this.execCmd('InsertHTML', txt);
17921             } 
17922             
17923             this.syncValue();
17924             
17925             this.deferFocus();
17926         }
17927     },
17928  // private
17929     mozKeyPress : function(e){
17930         if(e.ctrlKey){
17931             var c = e.getCharCode(), cmd;
17932           
17933             if(c > 0){
17934                 c = String.fromCharCode(c).toLowerCase();
17935                 switch(c){
17936                     case 'b':
17937                         cmd = 'bold';
17938                         break;
17939                     case 'i':
17940                         cmd = 'italic';
17941                         break;
17942                     
17943                     case 'u':
17944                         cmd = 'underline';
17945                         break;
17946                     
17947                     case 'v':
17948                         this.cleanUpPaste.defer(100, this);
17949                         return;
17950                         
17951                 }
17952                 if(cmd){
17953                     this.win.focus();
17954                     this.execCmd(cmd);
17955                     this.deferFocus();
17956                     e.preventDefault();
17957                 }
17958                 
17959             }
17960         }
17961     },
17962
17963     // private
17964     fixKeys : function(){ // load time branching for fastest keydown performance
17965         if(Roo.isIE){
17966             return function(e){
17967                 var k = e.getKey(), r;
17968                 if(k == e.TAB){
17969                     e.stopEvent();
17970                     r = this.doc.selection.createRange();
17971                     if(r){
17972                         r.collapse(true);
17973                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17974                         this.deferFocus();
17975                     }
17976                     return;
17977                 }
17978                 
17979                 if(k == e.ENTER){
17980                     r = this.doc.selection.createRange();
17981                     if(r){
17982                         var target = r.parentElement();
17983                         if(!target || target.tagName.toLowerCase() != 'li'){
17984                             e.stopEvent();
17985                             r.pasteHTML('<br />');
17986                             r.collapse(false);
17987                             r.select();
17988                         }
17989                     }
17990                 }
17991                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17992                     this.cleanUpPaste.defer(100, this);
17993                     return;
17994                 }
17995                 
17996                 
17997             };
17998         }else if(Roo.isOpera){
17999             return function(e){
18000                 var k = e.getKey();
18001                 if(k == e.TAB){
18002                     e.stopEvent();
18003                     this.win.focus();
18004                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18005                     this.deferFocus();
18006                 }
18007                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18008                     this.cleanUpPaste.defer(100, this);
18009                     return;
18010                 }
18011                 
18012             };
18013         }else if(Roo.isSafari){
18014             return function(e){
18015                 var k = e.getKey();
18016                 
18017                 if(k == e.TAB){
18018                     e.stopEvent();
18019                     this.execCmd('InsertText','\t');
18020                     this.deferFocus();
18021                     return;
18022                 }
18023                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18024                     this.cleanUpPaste.defer(100, this);
18025                     return;
18026                 }
18027                 
18028              };
18029         }
18030     }(),
18031     
18032     getAllAncestors: function()
18033     {
18034         var p = this.getSelectedNode();
18035         var a = [];
18036         if (!p) {
18037             a.push(p); // push blank onto stack..
18038             p = this.getParentElement();
18039         }
18040         
18041         
18042         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18043             a.push(p);
18044             p = p.parentNode;
18045         }
18046         a.push(this.doc.body);
18047         return a;
18048     },
18049     lastSel : false,
18050     lastSelNode : false,
18051     
18052     
18053     getSelection : function() 
18054     {
18055         this.assignDocWin();
18056         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18057     },
18058     
18059     getSelectedNode: function() 
18060     {
18061         // this may only work on Gecko!!!
18062         
18063         // should we cache this!!!!
18064         
18065         
18066         
18067          
18068         var range = this.createRange(this.getSelection()).cloneRange();
18069         
18070         if (Roo.isIE) {
18071             var parent = range.parentElement();
18072             while (true) {
18073                 var testRange = range.duplicate();
18074                 testRange.moveToElementText(parent);
18075                 if (testRange.inRange(range)) {
18076                     break;
18077                 }
18078                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18079                     break;
18080                 }
18081                 parent = parent.parentElement;
18082             }
18083             return parent;
18084         }
18085         
18086         // is ancestor a text element.
18087         var ac =  range.commonAncestorContainer;
18088         if (ac.nodeType == 3) {
18089             ac = ac.parentNode;
18090         }
18091         
18092         var ar = ac.childNodes;
18093          
18094         var nodes = [];
18095         var other_nodes = [];
18096         var has_other_nodes = false;
18097         for (var i=0;i<ar.length;i++) {
18098             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18099                 continue;
18100             }
18101             // fullly contained node.
18102             
18103             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18104                 nodes.push(ar[i]);
18105                 continue;
18106             }
18107             
18108             // probably selected..
18109             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18110                 other_nodes.push(ar[i]);
18111                 continue;
18112             }
18113             // outer..
18114             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18115                 continue;
18116             }
18117             
18118             
18119             has_other_nodes = true;
18120         }
18121         if (!nodes.length && other_nodes.length) {
18122             nodes= other_nodes;
18123         }
18124         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18125             return false;
18126         }
18127         
18128         return nodes[0];
18129     },
18130     createRange: function(sel)
18131     {
18132         // this has strange effects when using with 
18133         // top toolbar - not sure if it's a great idea.
18134         //this.editor.contentWindow.focus();
18135         if (typeof sel != "undefined") {
18136             try {
18137                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18138             } catch(e) {
18139                 return this.doc.createRange();
18140             }
18141         } else {
18142             return this.doc.createRange();
18143         }
18144     },
18145     getParentElement: function()
18146     {
18147         
18148         this.assignDocWin();
18149         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18150         
18151         var range = this.createRange(sel);
18152          
18153         try {
18154             var p = range.commonAncestorContainer;
18155             while (p.nodeType == 3) { // text node
18156                 p = p.parentNode;
18157             }
18158             return p;
18159         } catch (e) {
18160             return null;
18161         }
18162     
18163     },
18164     /***
18165      *
18166      * Range intersection.. the hard stuff...
18167      *  '-1' = before
18168      *  '0' = hits..
18169      *  '1' = after.
18170      *         [ -- selected range --- ]
18171      *   [fail]                        [fail]
18172      *
18173      *    basically..
18174      *      if end is before start or  hits it. fail.
18175      *      if start is after end or hits it fail.
18176      *
18177      *   if either hits (but other is outside. - then it's not 
18178      *   
18179      *    
18180      **/
18181     
18182     
18183     // @see http://www.thismuchiknow.co.uk/?p=64.
18184     rangeIntersectsNode : function(range, node)
18185     {
18186         var nodeRange = node.ownerDocument.createRange();
18187         try {
18188             nodeRange.selectNode(node);
18189         } catch (e) {
18190             nodeRange.selectNodeContents(node);
18191         }
18192     
18193         var rangeStartRange = range.cloneRange();
18194         rangeStartRange.collapse(true);
18195     
18196         var rangeEndRange = range.cloneRange();
18197         rangeEndRange.collapse(false);
18198     
18199         var nodeStartRange = nodeRange.cloneRange();
18200         nodeStartRange.collapse(true);
18201     
18202         var nodeEndRange = nodeRange.cloneRange();
18203         nodeEndRange.collapse(false);
18204     
18205         return rangeStartRange.compareBoundaryPoints(
18206                  Range.START_TO_START, nodeEndRange) == -1 &&
18207                rangeEndRange.compareBoundaryPoints(
18208                  Range.START_TO_START, nodeStartRange) == 1;
18209         
18210          
18211     },
18212     rangeCompareNode : function(range, node)
18213     {
18214         var nodeRange = node.ownerDocument.createRange();
18215         try {
18216             nodeRange.selectNode(node);
18217         } catch (e) {
18218             nodeRange.selectNodeContents(node);
18219         }
18220         
18221         
18222         range.collapse(true);
18223     
18224         nodeRange.collapse(true);
18225      
18226         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18227         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18228          
18229         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18230         
18231         var nodeIsBefore   =  ss == 1;
18232         var nodeIsAfter    = ee == -1;
18233         
18234         if (nodeIsBefore && nodeIsAfter)
18235             return 0; // outer
18236         if (!nodeIsBefore && nodeIsAfter)
18237             return 1; //right trailed.
18238         
18239         if (nodeIsBefore && !nodeIsAfter)
18240             return 2;  // left trailed.
18241         // fully contined.
18242         return 3;
18243     },
18244
18245     // private? - in a new class?
18246     cleanUpPaste :  function()
18247     {
18248         // cleans up the whole document..
18249         Roo.log('cleanuppaste');
18250         
18251         this.cleanUpChildren(this.doc.body);
18252         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18253         if (clean != this.doc.body.innerHTML) {
18254             this.doc.body.innerHTML = clean;
18255         }
18256         
18257     },
18258     
18259     cleanWordChars : function(input) {// change the chars to hex code
18260         var he = Roo.HtmlEditorCore;
18261         
18262         var output = input;
18263         Roo.each(he.swapCodes, function(sw) { 
18264             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18265             
18266             output = output.replace(swapper, sw[1]);
18267         });
18268         
18269         return output;
18270     },
18271     
18272     
18273     cleanUpChildren : function (n)
18274     {
18275         if (!n.childNodes.length) {
18276             return;
18277         }
18278         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18279            this.cleanUpChild(n.childNodes[i]);
18280         }
18281     },
18282     
18283     
18284         
18285     
18286     cleanUpChild : function (node)
18287     {
18288         var ed = this;
18289         //console.log(node);
18290         if (node.nodeName == "#text") {
18291             // clean up silly Windows -- stuff?
18292             return; 
18293         }
18294         if (node.nodeName == "#comment") {
18295             node.parentNode.removeChild(node);
18296             // clean up silly Windows -- stuff?
18297             return; 
18298         }
18299         var lcname = node.tagName.toLowerCase();
18300         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18301         // whitelist of tags..
18302         
18303         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18304             // remove node.
18305             node.parentNode.removeChild(node);
18306             return;
18307             
18308         }
18309         
18310         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18311         
18312         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18313         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18314         
18315         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18316         //    remove_keep_children = true;
18317         //}
18318         
18319         if (remove_keep_children) {
18320             this.cleanUpChildren(node);
18321             // inserts everything just before this node...
18322             while (node.childNodes.length) {
18323                 var cn = node.childNodes[0];
18324                 node.removeChild(cn);
18325                 node.parentNode.insertBefore(cn, node);
18326             }
18327             node.parentNode.removeChild(node);
18328             return;
18329         }
18330         
18331         if (!node.attributes || !node.attributes.length) {
18332             this.cleanUpChildren(node);
18333             return;
18334         }
18335         
18336         function cleanAttr(n,v)
18337         {
18338             
18339             if (v.match(/^\./) || v.match(/^\//)) {
18340                 return;
18341             }
18342             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18343                 return;
18344             }
18345             if (v.match(/^#/)) {
18346                 return;
18347             }
18348 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18349             node.removeAttribute(n);
18350             
18351         }
18352         
18353         var cwhite = this.cwhite;
18354         var cblack = this.cblack;
18355             
18356         function cleanStyle(n,v)
18357         {
18358             if (v.match(/expression/)) { //XSS?? should we even bother..
18359                 node.removeAttribute(n);
18360                 return;
18361             }
18362             
18363             var parts = v.split(/;/);
18364             var clean = [];
18365             
18366             Roo.each(parts, function(p) {
18367                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
18368                 if (!p.length) {
18369                     return true;
18370                 }
18371                 var l = p.split(':').shift().replace(/\s+/g,'');
18372                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
18373                 
18374                 if ( cwhite.length && cblack.indexOf(l) > -1) {
18375 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18376                     //node.removeAttribute(n);
18377                     return true;
18378                 }
18379                 //Roo.log()
18380                 // only allow 'c whitelisted system attributes'
18381                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
18382 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18383                     //node.removeAttribute(n);
18384                     return true;
18385                 }
18386                 
18387                 
18388                  
18389                 
18390                 clean.push(p);
18391                 return true;
18392             });
18393             if (clean.length) { 
18394                 node.setAttribute(n, clean.join(';'));
18395             } else {
18396                 node.removeAttribute(n);
18397             }
18398             
18399         }
18400         
18401         
18402         for (var i = node.attributes.length-1; i > -1 ; i--) {
18403             var a = node.attributes[i];
18404             //console.log(a);
18405             
18406             if (a.name.toLowerCase().substr(0,2)=='on')  {
18407                 node.removeAttribute(a.name);
18408                 continue;
18409             }
18410             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
18411                 node.removeAttribute(a.name);
18412                 continue;
18413             }
18414             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
18415                 cleanAttr(a.name,a.value); // fixme..
18416                 continue;
18417             }
18418             if (a.name == 'style') {
18419                 cleanStyle(a.name,a.value);
18420                 continue;
18421             }
18422             /// clean up MS crap..
18423             // tecnically this should be a list of valid class'es..
18424             
18425             
18426             if (a.name == 'class') {
18427                 if (a.value.match(/^Mso/)) {
18428                     node.className = '';
18429                 }
18430                 
18431                 if (a.value.match(/body/)) {
18432                     node.className = '';
18433                 }
18434                 continue;
18435             }
18436             
18437             // style cleanup!?
18438             // class cleanup?
18439             
18440         }
18441         
18442         
18443         this.cleanUpChildren(node);
18444         
18445         
18446     },
18447     /**
18448      * Clean up MS wordisms...
18449      */
18450     cleanWord : function(node)
18451     {
18452         var _t = this;
18453         var cleanWordChildren = function()
18454         {
18455             if (!node.childNodes.length) {
18456                 return;
18457             }
18458             for (var i = node.childNodes.length-1; i > -1 ; i--) {
18459                _t.cleanWord(node.childNodes[i]);
18460             }
18461         }
18462         
18463         
18464         if (!node) {
18465             this.cleanWord(this.doc.body);
18466             return;
18467         }
18468         if (node.nodeName == "#text") {
18469             // clean up silly Windows -- stuff?
18470             return; 
18471         }
18472         if (node.nodeName == "#comment") {
18473             node.parentNode.removeChild(node);
18474             // clean up silly Windows -- stuff?
18475             return; 
18476         }
18477         
18478         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
18479             node.parentNode.removeChild(node);
18480             return;
18481         }
18482         
18483         // remove - but keep children..
18484         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
18485             while (node.childNodes.length) {
18486                 var cn = node.childNodes[0];
18487                 node.removeChild(cn);
18488                 node.parentNode.insertBefore(cn, node);
18489             }
18490             node.parentNode.removeChild(node);
18491             cleanWordChildren();
18492             return;
18493         }
18494         // clean styles
18495         if (node.className.length) {
18496             
18497             var cn = node.className.split(/\W+/);
18498             var cna = [];
18499             Roo.each(cn, function(cls) {
18500                 if (cls.match(/Mso[a-zA-Z]+/)) {
18501                     return;
18502                 }
18503                 cna.push(cls);
18504             });
18505             node.className = cna.length ? cna.join(' ') : '';
18506             if (!cna.length) {
18507                 node.removeAttribute("class");
18508             }
18509         }
18510         
18511         if (node.hasAttribute("lang")) {
18512             node.removeAttribute("lang");
18513         }
18514         
18515         if (node.hasAttribute("style")) {
18516             
18517             var styles = node.getAttribute("style").split(";");
18518             var nstyle = [];
18519             Roo.each(styles, function(s) {
18520                 if (!s.match(/:/)) {
18521                     return;
18522                 }
18523                 var kv = s.split(":");
18524                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
18525                     return;
18526                 }
18527                 // what ever is left... we allow.
18528                 nstyle.push(s);
18529             });
18530             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
18531             if (!nstyle.length) {
18532                 node.removeAttribute('style');
18533             }
18534         }
18535         
18536         cleanWordChildren();
18537         
18538         
18539     },
18540     domToHTML : function(currentElement, depth, nopadtext) {
18541         
18542         depth = depth || 0;
18543         nopadtext = nopadtext || false;
18544     
18545         if (!currentElement) {
18546             return this.domToHTML(this.doc.body);
18547         }
18548         
18549         //Roo.log(currentElement);
18550         var j;
18551         var allText = false;
18552         var nodeName = currentElement.nodeName;
18553         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
18554         
18555         if  (nodeName == '#text') {
18556             
18557             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
18558         }
18559         
18560         
18561         var ret = '';
18562         if (nodeName != 'BODY') {
18563              
18564             var i = 0;
18565             // Prints the node tagName, such as <A>, <IMG>, etc
18566             if (tagName) {
18567                 var attr = [];
18568                 for(i = 0; i < currentElement.attributes.length;i++) {
18569                     // quoting?
18570                     var aname = currentElement.attributes.item(i).name;
18571                     if (!currentElement.attributes.item(i).value.length) {
18572                         continue;
18573                     }
18574                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
18575                 }
18576                 
18577                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
18578             } 
18579             else {
18580                 
18581                 // eack
18582             }
18583         } else {
18584             tagName = false;
18585         }
18586         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
18587             return ret;
18588         }
18589         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
18590             nopadtext = true;
18591         }
18592         
18593         
18594         // Traverse the tree
18595         i = 0;
18596         var currentElementChild = currentElement.childNodes.item(i);
18597         var allText = true;
18598         var innerHTML  = '';
18599         lastnode = '';
18600         while (currentElementChild) {
18601             // Formatting code (indent the tree so it looks nice on the screen)
18602             var nopad = nopadtext;
18603             if (lastnode == 'SPAN') {
18604                 nopad  = true;
18605             }
18606             // text
18607             if  (currentElementChild.nodeName == '#text') {
18608                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
18609                 toadd = nopadtext ? toadd : toadd.trim();
18610                 if (!nopad && toadd.length > 80) {
18611                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
18612                 }
18613                 innerHTML  += toadd;
18614                 
18615                 i++;
18616                 currentElementChild = currentElement.childNodes.item(i);
18617                 lastNode = '';
18618                 continue;
18619             }
18620             allText = false;
18621             
18622             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
18623                 
18624             // Recursively traverse the tree structure of the child node
18625             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
18626             lastnode = currentElementChild.nodeName;
18627             i++;
18628             currentElementChild=currentElement.childNodes.item(i);
18629         }
18630         
18631         ret += innerHTML;
18632         
18633         if (!allText) {
18634                 // The remaining code is mostly for formatting the tree
18635             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
18636         }
18637         
18638         
18639         if (tagName) {
18640             ret+= "</"+tagName+">";
18641         }
18642         return ret;
18643         
18644     },
18645         
18646     applyBlacklists : function()
18647     {
18648         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
18649         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
18650         
18651         this.white = [];
18652         this.black = [];
18653         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
18654             if (b.indexOf(tag) > -1) {
18655                 return;
18656             }
18657             this.white.push(tag);
18658             
18659         }, this);
18660         
18661         Roo.each(w, function(tag) {
18662             if (b.indexOf(tag) > -1) {
18663                 return;
18664             }
18665             if (this.white.indexOf(tag) > -1) {
18666                 return;
18667             }
18668             this.white.push(tag);
18669             
18670         }, this);
18671         
18672         
18673         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
18674             if (w.indexOf(tag) > -1) {
18675                 return;
18676             }
18677             this.black.push(tag);
18678             
18679         }, this);
18680         
18681         Roo.each(b, function(tag) {
18682             if (w.indexOf(tag) > -1) {
18683                 return;
18684             }
18685             if (this.black.indexOf(tag) > -1) {
18686                 return;
18687             }
18688             this.black.push(tag);
18689             
18690         }, this);
18691         
18692         
18693         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
18694         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
18695         
18696         this.cwhite = [];
18697         this.cblack = [];
18698         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
18699             if (b.indexOf(tag) > -1) {
18700                 return;
18701             }
18702             this.cwhite.push(tag);
18703             
18704         }, this);
18705         
18706         Roo.each(w, function(tag) {
18707             if (b.indexOf(tag) > -1) {
18708                 return;
18709             }
18710             if (this.cwhite.indexOf(tag) > -1) {
18711                 return;
18712             }
18713             this.cwhite.push(tag);
18714             
18715         }, this);
18716         
18717         
18718         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
18719             if (w.indexOf(tag) > -1) {
18720                 return;
18721             }
18722             this.cblack.push(tag);
18723             
18724         }, this);
18725         
18726         Roo.each(b, function(tag) {
18727             if (w.indexOf(tag) > -1) {
18728                 return;
18729             }
18730             if (this.cblack.indexOf(tag) > -1) {
18731                 return;
18732             }
18733             this.cblack.push(tag);
18734             
18735         }, this);
18736     },
18737     
18738     setStylesheets : function(stylesheets)
18739     {
18740         if(typeof(stylesheets) == 'string'){
18741             Roo.get(this.iframe.contentDocument.head).createChild({
18742                 tag : 'link',
18743                 rel : 'stylesheet',
18744                 type : 'text/css',
18745                 href : stylesheets
18746             });
18747             
18748             return;
18749         }
18750         var _this = this;
18751      
18752         Roo.each(stylesheets, function(s) {
18753             if(!s.length){
18754                 return;
18755             }
18756             
18757             Roo.get(_this.iframe.contentDocument.head).createChild({
18758                 tag : 'link',
18759                 rel : 'stylesheet',
18760                 type : 'text/css',
18761                 href : s
18762             });
18763         });
18764
18765         
18766     },
18767     
18768     removeStylesheets : function()
18769     {
18770         var _this = this;
18771         
18772         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
18773             s.remove();
18774         });
18775     }
18776     
18777     // hide stuff that is not compatible
18778     /**
18779      * @event blur
18780      * @hide
18781      */
18782     /**
18783      * @event change
18784      * @hide
18785      */
18786     /**
18787      * @event focus
18788      * @hide
18789      */
18790     /**
18791      * @event specialkey
18792      * @hide
18793      */
18794     /**
18795      * @cfg {String} fieldClass @hide
18796      */
18797     /**
18798      * @cfg {String} focusClass @hide
18799      */
18800     /**
18801      * @cfg {String} autoCreate @hide
18802      */
18803     /**
18804      * @cfg {String} inputType @hide
18805      */
18806     /**
18807      * @cfg {String} invalidClass @hide
18808      */
18809     /**
18810      * @cfg {String} invalidText @hide
18811      */
18812     /**
18813      * @cfg {String} msgFx @hide
18814      */
18815     /**
18816      * @cfg {String} validateOnBlur @hide
18817      */
18818 });
18819
18820 Roo.HtmlEditorCore.white = [
18821         'area', 'br', 'img', 'input', 'hr', 'wbr',
18822         
18823        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
18824        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
18825        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
18826        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
18827        'table',   'ul',         'xmp', 
18828        
18829        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
18830       'thead',   'tr', 
18831      
18832       'dir', 'menu', 'ol', 'ul', 'dl',
18833        
18834       'embed',  'object'
18835 ];
18836
18837
18838 Roo.HtmlEditorCore.black = [
18839     //    'embed',  'object', // enable - backend responsiblity to clean thiese
18840         'applet', // 
18841         'base',   'basefont', 'bgsound', 'blink',  'body', 
18842         'frame',  'frameset', 'head',    'html',   'ilayer', 
18843         'iframe', 'layer',  'link',     'meta',    'object',   
18844         'script', 'style' ,'title',  'xml' // clean later..
18845 ];
18846 Roo.HtmlEditorCore.clean = [
18847     'script', 'style', 'title', 'xml'
18848 ];
18849 Roo.HtmlEditorCore.remove = [
18850     'font'
18851 ];
18852 // attributes..
18853
18854 Roo.HtmlEditorCore.ablack = [
18855     'on'
18856 ];
18857     
18858 Roo.HtmlEditorCore.aclean = [ 
18859     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
18860 ];
18861
18862 // protocols..
18863 Roo.HtmlEditorCore.pwhite= [
18864         'http',  'https',  'mailto'
18865 ];
18866
18867 // white listed style attributes.
18868 Roo.HtmlEditorCore.cwhite= [
18869       //  'text-align', /// default is to allow most things..
18870       
18871          
18872 //        'font-size'//??
18873 ];
18874
18875 // black listed style attributes.
18876 Roo.HtmlEditorCore.cblack= [
18877       //  'font-size' -- this can be set by the project 
18878 ];
18879
18880
18881 Roo.HtmlEditorCore.swapCodes   =[ 
18882     [    8211, "--" ], 
18883     [    8212, "--" ], 
18884     [    8216,  "'" ],  
18885     [    8217, "'" ],  
18886     [    8220, '"' ],  
18887     [    8221, '"' ],  
18888     [    8226, "*" ],  
18889     [    8230, "..." ]
18890 ]; 
18891
18892     /*
18893  * - LGPL
18894  *
18895  * HtmlEditor
18896  * 
18897  */
18898
18899 /**
18900  * @class Roo.bootstrap.HtmlEditor
18901  * @extends Roo.bootstrap.TextArea
18902  * Bootstrap HtmlEditor class
18903
18904  * @constructor
18905  * Create a new HtmlEditor
18906  * @param {Object} config The config object
18907  */
18908
18909 Roo.bootstrap.HtmlEditor = function(config){
18910     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
18911     if (!this.toolbars) {
18912         this.toolbars = [];
18913     }
18914     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
18915     this.addEvents({
18916             /**
18917              * @event initialize
18918              * Fires when the editor is fully initialized (including the iframe)
18919              * @param {HtmlEditor} this
18920              */
18921             initialize: true,
18922             /**
18923              * @event activate
18924              * Fires when the editor is first receives the focus. Any insertion must wait
18925              * until after this event.
18926              * @param {HtmlEditor} this
18927              */
18928             activate: true,
18929              /**
18930              * @event beforesync
18931              * Fires before the textarea is updated with content from the editor iframe. Return false
18932              * to cancel the sync.
18933              * @param {HtmlEditor} this
18934              * @param {String} html
18935              */
18936             beforesync: true,
18937              /**
18938              * @event beforepush
18939              * Fires before the iframe editor is updated with content from the textarea. Return false
18940              * to cancel the push.
18941              * @param {HtmlEditor} this
18942              * @param {String} html
18943              */
18944             beforepush: true,
18945              /**
18946              * @event sync
18947              * Fires when the textarea is updated with content from the editor iframe.
18948              * @param {HtmlEditor} this
18949              * @param {String} html
18950              */
18951             sync: true,
18952              /**
18953              * @event push
18954              * Fires when the iframe editor is updated with content from the textarea.
18955              * @param {HtmlEditor} this
18956              * @param {String} html
18957              */
18958             push: true,
18959              /**
18960              * @event editmodechange
18961              * Fires when the editor switches edit modes
18962              * @param {HtmlEditor} this
18963              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
18964              */
18965             editmodechange: true,
18966             /**
18967              * @event editorevent
18968              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18969              * @param {HtmlEditor} this
18970              */
18971             editorevent: true,
18972             /**
18973              * @event firstfocus
18974              * Fires when on first focus - needed by toolbars..
18975              * @param {HtmlEditor} this
18976              */
18977             firstfocus: true,
18978             /**
18979              * @event autosave
18980              * Auto save the htmlEditor value as a file into Events
18981              * @param {HtmlEditor} this
18982              */
18983             autosave: true,
18984             /**
18985              * @event savedpreview
18986              * preview the saved version of htmlEditor
18987              * @param {HtmlEditor} this
18988              */
18989             savedpreview: true
18990         });
18991 };
18992
18993
18994 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
18995     
18996     
18997       /**
18998      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18999      */
19000     toolbars : false,
19001    
19002      /**
19003      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19004      *                        Roo.resizable.
19005      */
19006     resizable : false,
19007      /**
19008      * @cfg {Number} height (in pixels)
19009      */   
19010     height: 300,
19011    /**
19012      * @cfg {Number} width (in pixels)
19013      */   
19014     width: false,
19015     
19016     /**
19017      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19018      * 
19019      */
19020     stylesheets: false,
19021     
19022     // id of frame..
19023     frameId: false,
19024     
19025     // private properties
19026     validationEvent : false,
19027     deferHeight: true,
19028     initialized : false,
19029     activated : false,
19030     
19031     onFocus : Roo.emptyFn,
19032     iframePad:3,
19033     hideMode:'offsets',
19034     
19035     
19036     tbContainer : false,
19037     
19038     toolbarContainer :function() {
19039         return this.wrap.select('.x-html-editor-tb',true).first();
19040     },
19041
19042     /**
19043      * Protected method that will not generally be called directly. It
19044      * is called when the editor creates its toolbar. Override this method if you need to
19045      * add custom toolbar buttons.
19046      * @param {HtmlEditor} editor
19047      */
19048     createToolbar : function(){
19049         
19050         Roo.log("create toolbars");
19051         
19052         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19053         this.toolbars[0].render(this.toolbarContainer());
19054         
19055         return;
19056         
19057 //        if (!editor.toolbars || !editor.toolbars.length) {
19058 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19059 //        }
19060 //        
19061 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19062 //            editor.toolbars[i] = Roo.factory(
19063 //                    typeof(editor.toolbars[i]) == 'string' ?
19064 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19065 //                Roo.bootstrap.HtmlEditor);
19066 //            editor.toolbars[i].init(editor);
19067 //        }
19068     },
19069
19070      
19071     // private
19072     onRender : function(ct, position)
19073     {
19074        // Roo.log("Call onRender: " + this.xtype);
19075         var _t = this;
19076         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19077       
19078         this.wrap = this.inputEl().wrap({
19079             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19080         });
19081         
19082         this.editorcore.onRender(ct, position);
19083          
19084         if (this.resizable) {
19085             this.resizeEl = new Roo.Resizable(this.wrap, {
19086                 pinned : true,
19087                 wrap: true,
19088                 dynamic : true,
19089                 minHeight : this.height,
19090                 height: this.height,
19091                 handles : this.resizable,
19092                 width: this.width,
19093                 listeners : {
19094                     resize : function(r, w, h) {
19095                         _t.onResize(w,h); // -something
19096                     }
19097                 }
19098             });
19099             
19100         }
19101         this.createToolbar(this);
19102        
19103         
19104         if(!this.width && this.resizable){
19105             this.setSize(this.wrap.getSize());
19106         }
19107         if (this.resizeEl) {
19108             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19109             // should trigger onReize..
19110         }
19111         
19112     },
19113
19114     // private
19115     onResize : function(w, h)
19116     {
19117         Roo.log('resize: ' +w + ',' + h );
19118         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19119         var ew = false;
19120         var eh = false;
19121         
19122         if(this.inputEl() ){
19123             if(typeof w == 'number'){
19124                 var aw = w - this.wrap.getFrameWidth('lr');
19125                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19126                 ew = aw;
19127             }
19128             if(typeof h == 'number'){
19129                  var tbh = -11;  // fixme it needs to tool bar size!
19130                 for (var i =0; i < this.toolbars.length;i++) {
19131                     // fixme - ask toolbars for heights?
19132                     tbh += this.toolbars[i].el.getHeight();
19133                     //if (this.toolbars[i].footer) {
19134                     //    tbh += this.toolbars[i].footer.el.getHeight();
19135                     //}
19136                 }
19137               
19138                 
19139                 
19140                 
19141                 
19142                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19143                 ah -= 5; // knock a few pixes off for look..
19144                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19145                 var eh = ah;
19146             }
19147         }
19148         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19149         this.editorcore.onResize(ew,eh);
19150         
19151     },
19152
19153     /**
19154      * Toggles the editor between standard and source edit mode.
19155      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19156      */
19157     toggleSourceEdit : function(sourceEditMode)
19158     {
19159         this.editorcore.toggleSourceEdit(sourceEditMode);
19160         
19161         if(this.editorcore.sourceEditMode){
19162             Roo.log('editor - showing textarea');
19163             
19164 //            Roo.log('in');
19165 //            Roo.log(this.syncValue());
19166             this.syncValue();
19167             this.inputEl().removeClass(['hide', 'x-hidden']);
19168             this.inputEl().dom.removeAttribute('tabIndex');
19169             this.inputEl().focus();
19170         }else{
19171             Roo.log('editor - hiding textarea');
19172 //            Roo.log('out')
19173 //            Roo.log(this.pushValue()); 
19174             this.pushValue();
19175             
19176             this.inputEl().addClass(['hide', 'x-hidden']);
19177             this.inputEl().dom.setAttribute('tabIndex', -1);
19178             //this.deferFocus();
19179         }
19180          
19181         if(this.resizable){
19182             this.setSize(this.wrap.getSize());
19183         }
19184         
19185         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19186     },
19187  
19188     // private (for BoxComponent)
19189     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19190
19191     // private (for BoxComponent)
19192     getResizeEl : function(){
19193         return this.wrap;
19194     },
19195
19196     // private (for BoxComponent)
19197     getPositionEl : function(){
19198         return this.wrap;
19199     },
19200
19201     // private
19202     initEvents : function(){
19203         this.originalValue = this.getValue();
19204     },
19205
19206 //    /**
19207 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19208 //     * @method
19209 //     */
19210 //    markInvalid : Roo.emptyFn,
19211 //    /**
19212 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19213 //     * @method
19214 //     */
19215 //    clearInvalid : Roo.emptyFn,
19216
19217     setValue : function(v){
19218         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19219         this.editorcore.pushValue();
19220     },
19221
19222      
19223     // private
19224     deferFocus : function(){
19225         this.focus.defer(10, this);
19226     },
19227
19228     // doc'ed in Field
19229     focus : function(){
19230         this.editorcore.focus();
19231         
19232     },
19233       
19234
19235     // private
19236     onDestroy : function(){
19237         
19238         
19239         
19240         if(this.rendered){
19241             
19242             for (var i =0; i < this.toolbars.length;i++) {
19243                 // fixme - ask toolbars for heights?
19244                 this.toolbars[i].onDestroy();
19245             }
19246             
19247             this.wrap.dom.innerHTML = '';
19248             this.wrap.remove();
19249         }
19250     },
19251
19252     // private
19253     onFirstFocus : function(){
19254         //Roo.log("onFirstFocus");
19255         this.editorcore.onFirstFocus();
19256          for (var i =0; i < this.toolbars.length;i++) {
19257             this.toolbars[i].onFirstFocus();
19258         }
19259         
19260     },
19261     
19262     // private
19263     syncValue : function()
19264     {   
19265         this.editorcore.syncValue();
19266     },
19267     
19268     pushValue : function()
19269     {   
19270         this.editorcore.pushValue();
19271     }
19272      
19273     
19274     // hide stuff that is not compatible
19275     /**
19276      * @event blur
19277      * @hide
19278      */
19279     /**
19280      * @event change
19281      * @hide
19282      */
19283     /**
19284      * @event focus
19285      * @hide
19286      */
19287     /**
19288      * @event specialkey
19289      * @hide
19290      */
19291     /**
19292      * @cfg {String} fieldClass @hide
19293      */
19294     /**
19295      * @cfg {String} focusClass @hide
19296      */
19297     /**
19298      * @cfg {String} autoCreate @hide
19299      */
19300     /**
19301      * @cfg {String} inputType @hide
19302      */
19303     /**
19304      * @cfg {String} invalidClass @hide
19305      */
19306     /**
19307      * @cfg {String} invalidText @hide
19308      */
19309     /**
19310      * @cfg {String} msgFx @hide
19311      */
19312     /**
19313      * @cfg {String} validateOnBlur @hide
19314      */
19315 });
19316  
19317     
19318    
19319    
19320    
19321       
19322 Roo.namespace('Roo.bootstrap.htmleditor');
19323 /**
19324  * @class Roo.bootstrap.HtmlEditorToolbar1
19325  * Basic Toolbar
19326  * 
19327  * Usage:
19328  *
19329  new Roo.bootstrap.HtmlEditor({
19330     ....
19331     toolbars : [
19332         new Roo.bootstrap.HtmlEditorToolbar1({
19333             disable : { fonts: 1 , format: 1, ..., ... , ...],
19334             btns : [ .... ]
19335         })
19336     }
19337      
19338  * 
19339  * @cfg {Object} disable List of elements to disable..
19340  * @cfg {Array} btns List of additional buttons.
19341  * 
19342  * 
19343  * NEEDS Extra CSS? 
19344  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19345  */
19346  
19347 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19348 {
19349     
19350     Roo.apply(this, config);
19351     
19352     // default disabled, based on 'good practice'..
19353     this.disable = this.disable || {};
19354     Roo.applyIf(this.disable, {
19355         fontSize : true,
19356         colors : true,
19357         specialElements : true
19358     });
19359     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
19360     
19361     this.editor = config.editor;
19362     this.editorcore = config.editor.editorcore;
19363     
19364     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
19365     
19366     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
19367     // dont call parent... till later.
19368 }
19369 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
19370      
19371     bar : true,
19372     
19373     editor : false,
19374     editorcore : false,
19375     
19376     
19377     formats : [
19378         "p" ,  
19379         "h1","h2","h3","h4","h5","h6", 
19380         "pre", "code", 
19381         "abbr", "acronym", "address", "cite", "samp", "var",
19382         'div','span'
19383     ],
19384     
19385     onRender : function(ct, position)
19386     {
19387        // Roo.log("Call onRender: " + this.xtype);
19388         
19389        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
19390        Roo.log(this.el);
19391        this.el.dom.style.marginBottom = '0';
19392        var _this = this;
19393        var editorcore = this.editorcore;
19394        var editor= this.editor;
19395        
19396        var children = [];
19397        var btn = function(id,cmd , toggle, handler){
19398        
19399             var  event = toggle ? 'toggle' : 'click';
19400        
19401             var a = {
19402                 size : 'sm',
19403                 xtype: 'Button',
19404                 xns: Roo.bootstrap,
19405                 glyphicon : id,
19406                 cmd : id || cmd,
19407                 enableToggle:toggle !== false,
19408                 //html : 'submit'
19409                 pressed : toggle ? false : null,
19410                 listeners : {}
19411             }
19412             a.listeners[toggle ? 'toggle' : 'click'] = function() {
19413                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
19414             }
19415             children.push(a);
19416             return a;
19417        }
19418         
19419         var style = {
19420                 xtype: 'Button',
19421                 size : 'sm',
19422                 xns: Roo.bootstrap,
19423                 glyphicon : 'font',
19424                 //html : 'submit'
19425                 menu : {
19426                     xtype: 'Menu',
19427                     xns: Roo.bootstrap,
19428                     items:  []
19429                 }
19430         };
19431         Roo.each(this.formats, function(f) {
19432             style.menu.items.push({
19433                 xtype :'MenuItem',
19434                 xns: Roo.bootstrap,
19435                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
19436                 tagname : f,
19437                 listeners : {
19438                     click : function()
19439                     {
19440                         editorcore.insertTag(this.tagname);
19441                         editor.focus();
19442                     }
19443                 }
19444                 
19445             });
19446         });
19447          children.push(style);   
19448             
19449             
19450         btn('bold',false,true);
19451         btn('italic',false,true);
19452         btn('align-left', 'justifyleft',true);
19453         btn('align-center', 'justifycenter',true);
19454         btn('align-right' , 'justifyright',true);
19455         btn('link', false, false, function(btn) {
19456             //Roo.log("create link?");
19457             var url = prompt(this.createLinkText, this.defaultLinkValue);
19458             if(url && url != 'http:/'+'/'){
19459                 this.editorcore.relayCmd('createlink', url);
19460             }
19461         }),
19462         btn('list','insertunorderedlist',true);
19463         btn('pencil', false,true, function(btn){
19464                 Roo.log(this);
19465                 
19466                 this.toggleSourceEdit(btn.pressed);
19467         });
19468         /*
19469         var cog = {
19470                 xtype: 'Button',
19471                 size : 'sm',
19472                 xns: Roo.bootstrap,
19473                 glyphicon : 'cog',
19474                 //html : 'submit'
19475                 menu : {
19476                     xtype: 'Menu',
19477                     xns: Roo.bootstrap,
19478                     items:  []
19479                 }
19480         };
19481         
19482         cog.menu.items.push({
19483             xtype :'MenuItem',
19484             xns: Roo.bootstrap,
19485             html : Clean styles,
19486             tagname : f,
19487             listeners : {
19488                 click : function()
19489                 {
19490                     editorcore.insertTag(this.tagname);
19491                     editor.focus();
19492                 }
19493             }
19494             
19495         });
19496        */
19497         
19498          
19499        this.xtype = 'NavSimplebar';
19500         
19501         for(var i=0;i< children.length;i++) {
19502             
19503             this.buttons.add(this.addxtypeChild(children[i]));
19504             
19505         }
19506         
19507         editor.on('editorevent', this.updateToolbar, this);
19508     },
19509     onBtnClick : function(id)
19510     {
19511        this.editorcore.relayCmd(id);
19512        this.editorcore.focus();
19513     },
19514     
19515     /**
19516      * Protected method that will not generally be called directly. It triggers
19517      * a toolbar update by reading the markup state of the current selection in the editor.
19518      */
19519     updateToolbar: function(){
19520
19521         if(!this.editorcore.activated){
19522             this.editor.onFirstFocus(); // is this neeed?
19523             return;
19524         }
19525
19526         var btns = this.buttons; 
19527         var doc = this.editorcore.doc;
19528         btns.get('bold').setActive(doc.queryCommandState('bold'));
19529         btns.get('italic').setActive(doc.queryCommandState('italic'));
19530         //btns.get('underline').setActive(doc.queryCommandState('underline'));
19531         
19532         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
19533         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
19534         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
19535         
19536         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
19537         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
19538          /*
19539         
19540         var ans = this.editorcore.getAllAncestors();
19541         if (this.formatCombo) {
19542             
19543             
19544             var store = this.formatCombo.store;
19545             this.formatCombo.setValue("");
19546             for (var i =0; i < ans.length;i++) {
19547                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
19548                     // select it..
19549                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
19550                     break;
19551                 }
19552             }
19553         }
19554         
19555         
19556         
19557         // hides menus... - so this cant be on a menu...
19558         Roo.bootstrap.MenuMgr.hideAll();
19559         */
19560         Roo.bootstrap.MenuMgr.hideAll();
19561         //this.editorsyncValue();
19562     },
19563     onFirstFocus: function() {
19564         this.buttons.each(function(item){
19565            item.enable();
19566         });
19567     },
19568     toggleSourceEdit : function(sourceEditMode){
19569         
19570           
19571         if(sourceEditMode){
19572             Roo.log("disabling buttons");
19573            this.buttons.each( function(item){
19574                 if(item.cmd != 'pencil'){
19575                     item.disable();
19576                 }
19577             });
19578           
19579         }else{
19580             Roo.log("enabling buttons");
19581             if(this.editorcore.initialized){
19582                 this.buttons.each( function(item){
19583                     item.enable();
19584                 });
19585             }
19586             
19587         }
19588         Roo.log("calling toggole on editor");
19589         // tell the editor that it's been pressed..
19590         this.editor.toggleSourceEdit(sourceEditMode);
19591        
19592     }
19593 });
19594
19595
19596
19597
19598
19599 /**
19600  * @class Roo.bootstrap.Table.AbstractSelectionModel
19601  * @extends Roo.util.Observable
19602  * Abstract base class for grid SelectionModels.  It provides the interface that should be
19603  * implemented by descendant classes.  This class should not be directly instantiated.
19604  * @constructor
19605  */
19606 Roo.bootstrap.Table.AbstractSelectionModel = function(){
19607     this.locked = false;
19608     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
19609 };
19610
19611
19612 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
19613     /** @ignore Called by the grid automatically. Do not call directly. */
19614     init : function(grid){
19615         this.grid = grid;
19616         this.initEvents();
19617     },
19618
19619     /**
19620      * Locks the selections.
19621      */
19622     lock : function(){
19623         this.locked = true;
19624     },
19625
19626     /**
19627      * Unlocks the selections.
19628      */
19629     unlock : function(){
19630         this.locked = false;
19631     },
19632
19633     /**
19634      * Returns true if the selections are locked.
19635      * @return {Boolean}
19636      */
19637     isLocked : function(){
19638         return this.locked;
19639     }
19640 });
19641 /**
19642  * @extends Roo.bootstrap.Table.AbstractSelectionModel
19643  * @class Roo.bootstrap.Table.RowSelectionModel
19644  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
19645  * It supports multiple selections and keyboard selection/navigation. 
19646  * @constructor
19647  * @param {Object} config
19648  */
19649
19650 Roo.bootstrap.Table.RowSelectionModel = function(config){
19651     Roo.apply(this, config);
19652     this.selections = new Roo.util.MixedCollection(false, function(o){
19653         return o.id;
19654     });
19655
19656     this.last = false;
19657     this.lastActive = false;
19658
19659     this.addEvents({
19660         /**
19661              * @event selectionchange
19662              * Fires when the selection changes
19663              * @param {SelectionModel} this
19664              */
19665             "selectionchange" : true,
19666         /**
19667              * @event afterselectionchange
19668              * Fires after the selection changes (eg. by key press or clicking)
19669              * @param {SelectionModel} this
19670              */
19671             "afterselectionchange" : true,
19672         /**
19673              * @event beforerowselect
19674              * Fires when a row is selected being selected, return false to cancel.
19675              * @param {SelectionModel} this
19676              * @param {Number} rowIndex The selected index
19677              * @param {Boolean} keepExisting False if other selections will be cleared
19678              */
19679             "beforerowselect" : true,
19680         /**
19681              * @event rowselect
19682              * Fires when a row is selected.
19683              * @param {SelectionModel} this
19684              * @param {Number} rowIndex The selected index
19685              * @param {Roo.data.Record} r The record
19686              */
19687             "rowselect" : true,
19688         /**
19689              * @event rowdeselect
19690              * Fires when a row is deselected.
19691              * @param {SelectionModel} this
19692              * @param {Number} rowIndex The selected index
19693              */
19694         "rowdeselect" : true
19695     });
19696     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
19697     this.locked = false;
19698 };
19699
19700 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
19701     /**
19702      * @cfg {Boolean} singleSelect
19703      * True to allow selection of only one row at a time (defaults to false)
19704      */
19705     singleSelect : false,
19706
19707     // private
19708     initEvents : function(){
19709
19710         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
19711             this.grid.on("mousedown", this.handleMouseDown, this);
19712         }else{ // allow click to work like normal
19713             this.grid.on("rowclick", this.handleDragableRowClick, this);
19714         }
19715
19716         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
19717             "up" : function(e){
19718                 if(!e.shiftKey){
19719                     this.selectPrevious(e.shiftKey);
19720                 }else if(this.last !== false && this.lastActive !== false){
19721                     var last = this.last;
19722                     this.selectRange(this.last,  this.lastActive-1);
19723                     this.grid.getView().focusRow(this.lastActive);
19724                     if(last !== false){
19725                         this.last = last;
19726                     }
19727                 }else{
19728                     this.selectFirstRow();
19729                 }
19730                 this.fireEvent("afterselectionchange", this);
19731             },
19732             "down" : function(e){
19733                 if(!e.shiftKey){
19734                     this.selectNext(e.shiftKey);
19735                 }else if(this.last !== false && this.lastActive !== false){
19736                     var last = this.last;
19737                     this.selectRange(this.last,  this.lastActive+1);
19738                     this.grid.getView().focusRow(this.lastActive);
19739                     if(last !== false){
19740                         this.last = last;
19741                     }
19742                 }else{
19743                     this.selectFirstRow();
19744                 }
19745                 this.fireEvent("afterselectionchange", this);
19746             },
19747             scope: this
19748         });
19749
19750         var view = this.grid.view;
19751         view.on("refresh", this.onRefresh, this);
19752         view.on("rowupdated", this.onRowUpdated, this);
19753         view.on("rowremoved", this.onRemove, this);
19754     },
19755
19756     // private
19757     onRefresh : function(){
19758         var ds = this.grid.dataSource, i, v = this.grid.view;
19759         var s = this.selections;
19760         s.each(function(r){
19761             if((i = ds.indexOfId(r.id)) != -1){
19762                 v.onRowSelect(i);
19763             }else{
19764                 s.remove(r);
19765             }
19766         });
19767     },
19768
19769     // private
19770     onRemove : function(v, index, r){
19771         this.selections.remove(r);
19772     },
19773
19774     // private
19775     onRowUpdated : function(v, index, r){
19776         if(this.isSelected(r)){
19777             v.onRowSelect(index);
19778         }
19779     },
19780
19781     /**
19782      * Select records.
19783      * @param {Array} records The records to select
19784      * @param {Boolean} keepExisting (optional) True to keep existing selections
19785      */
19786     selectRecords : function(records, keepExisting){
19787         if(!keepExisting){
19788             this.clearSelections();
19789         }
19790         var ds = this.grid.dataSource;
19791         for(var i = 0, len = records.length; i < len; i++){
19792             this.selectRow(ds.indexOf(records[i]), true);
19793         }
19794     },
19795
19796     /**
19797      * Gets the number of selected rows.
19798      * @return {Number}
19799      */
19800     getCount : function(){
19801         return this.selections.length;
19802     },
19803
19804     /**
19805      * Selects the first row in the grid.
19806      */
19807     selectFirstRow : function(){
19808         this.selectRow(0);
19809     },
19810
19811     /**
19812      * Select the last row.
19813      * @param {Boolean} keepExisting (optional) True to keep existing selections
19814      */
19815     selectLastRow : function(keepExisting){
19816         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
19817     },
19818
19819     /**
19820      * Selects the row immediately following the last selected row.
19821      * @param {Boolean} keepExisting (optional) True to keep existing selections
19822      */
19823     selectNext : function(keepExisting){
19824         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
19825             this.selectRow(this.last+1, keepExisting);
19826             this.grid.getView().focusRow(this.last);
19827         }
19828     },
19829
19830     /**
19831      * Selects the row that precedes the last selected row.
19832      * @param {Boolean} keepExisting (optional) True to keep existing selections
19833      */
19834     selectPrevious : function(keepExisting){
19835         if(this.last){
19836             this.selectRow(this.last-1, keepExisting);
19837             this.grid.getView().focusRow(this.last);
19838         }
19839     },
19840
19841     /**
19842      * Returns the selected records
19843      * @return {Array} Array of selected records
19844      */
19845     getSelections : function(){
19846         return [].concat(this.selections.items);
19847     },
19848
19849     /**
19850      * Returns the first selected record.
19851      * @return {Record}
19852      */
19853     getSelected : function(){
19854         return this.selections.itemAt(0);
19855     },
19856
19857
19858     /**
19859      * Clears all selections.
19860      */
19861     clearSelections : function(fast){
19862         if(this.locked) return;
19863         if(fast !== true){
19864             var ds = this.grid.dataSource;
19865             var s = this.selections;
19866             s.each(function(r){
19867                 this.deselectRow(ds.indexOfId(r.id));
19868             }, this);
19869             s.clear();
19870         }else{
19871             this.selections.clear();
19872         }
19873         this.last = false;
19874     },
19875
19876
19877     /**
19878      * Selects all rows.
19879      */
19880     selectAll : function(){
19881         if(this.locked) return;
19882         this.selections.clear();
19883         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
19884             this.selectRow(i, true);
19885         }
19886     },
19887
19888     /**
19889      * Returns True if there is a selection.
19890      * @return {Boolean}
19891      */
19892     hasSelection : function(){
19893         return this.selections.length > 0;
19894     },
19895
19896     /**
19897      * Returns True if the specified row is selected.
19898      * @param {Number/Record} record The record or index of the record to check
19899      * @return {Boolean}
19900      */
19901     isSelected : function(index){
19902         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
19903         return (r && this.selections.key(r.id) ? true : false);
19904     },
19905
19906     /**
19907      * Returns True if the specified record id is selected.
19908      * @param {String} id The id of record to check
19909      * @return {Boolean}
19910      */
19911     isIdSelected : function(id){
19912         return (this.selections.key(id) ? true : false);
19913     },
19914
19915     // private
19916     handleMouseDown : function(e, t){
19917         var view = this.grid.getView(), rowIndex;
19918         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
19919             return;
19920         };
19921         if(e.shiftKey && this.last !== false){
19922             var last = this.last;
19923             this.selectRange(last, rowIndex, e.ctrlKey);
19924             this.last = last; // reset the last
19925             view.focusRow(rowIndex);
19926         }else{
19927             var isSelected = this.isSelected(rowIndex);
19928             if(e.button !== 0 && isSelected){
19929                 view.focusRow(rowIndex);
19930             }else if(e.ctrlKey && isSelected){
19931                 this.deselectRow(rowIndex);
19932             }else if(!isSelected){
19933                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
19934                 view.focusRow(rowIndex);
19935             }
19936         }
19937         this.fireEvent("afterselectionchange", this);
19938     },
19939     // private
19940     handleDragableRowClick :  function(grid, rowIndex, e) 
19941     {
19942         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
19943             this.selectRow(rowIndex, false);
19944             grid.view.focusRow(rowIndex);
19945              this.fireEvent("afterselectionchange", this);
19946         }
19947     },
19948     
19949     /**
19950      * Selects multiple rows.
19951      * @param {Array} rows Array of the indexes of the row to select
19952      * @param {Boolean} keepExisting (optional) True to keep existing selections
19953      */
19954     selectRows : function(rows, keepExisting){
19955         if(!keepExisting){
19956             this.clearSelections();
19957         }
19958         for(var i = 0, len = rows.length; i < len; i++){
19959             this.selectRow(rows[i], true);
19960         }
19961     },
19962
19963     /**
19964      * Selects a range of rows. All rows in between startRow and endRow are also selected.
19965      * @param {Number} startRow The index of the first row in the range
19966      * @param {Number} endRow The index of the last row in the range
19967      * @param {Boolean} keepExisting (optional) True to retain existing selections
19968      */
19969     selectRange : function(startRow, endRow, keepExisting){
19970         if(this.locked) return;
19971         if(!keepExisting){
19972             this.clearSelections();
19973         }
19974         if(startRow <= endRow){
19975             for(var i = startRow; i <= endRow; i++){
19976                 this.selectRow(i, true);
19977             }
19978         }else{
19979             for(var i = startRow; i >= endRow; i--){
19980                 this.selectRow(i, true);
19981             }
19982         }
19983     },
19984
19985     /**
19986      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19987      * @param {Number} startRow The index of the first row in the range
19988      * @param {Number} endRow The index of the last row in the range
19989      */
19990     deselectRange : function(startRow, endRow, preventViewNotify){
19991         if(this.locked) return;
19992         for(var i = startRow; i <= endRow; i++){
19993             this.deselectRow(i, preventViewNotify);
19994         }
19995     },
19996
19997     /**
19998      * Selects a row.
19999      * @param {Number} row The index of the row to select
20000      * @param {Boolean} keepExisting (optional) True to keep existing selections
20001      */
20002     selectRow : function(index, keepExisting, preventViewNotify){
20003         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20004         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20005             if(!keepExisting || this.singleSelect){
20006                 this.clearSelections();
20007             }
20008             var r = this.grid.dataSource.getAt(index);
20009             this.selections.add(r);
20010             this.last = this.lastActive = index;
20011             if(!preventViewNotify){
20012                 this.grid.getView().onRowSelect(index);
20013             }
20014             this.fireEvent("rowselect", this, index, r);
20015             this.fireEvent("selectionchange", this);
20016         }
20017     },
20018
20019     /**
20020      * Deselects a row.
20021      * @param {Number} row The index of the row to deselect
20022      */
20023     deselectRow : function(index, preventViewNotify){
20024         if(this.locked) return;
20025         if(this.last == index){
20026             this.last = false;
20027         }
20028         if(this.lastActive == index){
20029             this.lastActive = false;
20030         }
20031         var r = this.grid.dataSource.getAt(index);
20032         this.selections.remove(r);
20033         if(!preventViewNotify){
20034             this.grid.getView().onRowDeselect(index);
20035         }
20036         this.fireEvent("rowdeselect", this, index);
20037         this.fireEvent("selectionchange", this);
20038     },
20039
20040     // private
20041     restoreLast : function(){
20042         if(this._last){
20043             this.last = this._last;
20044         }
20045     },
20046
20047     // private
20048     acceptsNav : function(row, col, cm){
20049         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20050     },
20051
20052     // private
20053     onEditorKey : function(field, e){
20054         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20055         if(k == e.TAB){
20056             e.stopEvent();
20057             ed.completeEdit();
20058             if(e.shiftKey){
20059                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20060             }else{
20061                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20062             }
20063         }else if(k == e.ENTER && !e.ctrlKey){
20064             e.stopEvent();
20065             ed.completeEdit();
20066             if(e.shiftKey){
20067                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20068             }else{
20069                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20070             }
20071         }else if(k == e.ESC){
20072             ed.cancelEdit();
20073         }
20074         if(newCell){
20075             g.startEditing(newCell[0], newCell[1]);
20076         }
20077     }
20078 });/*
20079  * Based on:
20080  * Ext JS Library 1.1.1
20081  * Copyright(c) 2006-2007, Ext JS, LLC.
20082  *
20083  * Originally Released Under LGPL - original licence link has changed is not relivant.
20084  *
20085  * Fork - LGPL
20086  * <script type="text/javascript">
20087  */
20088  
20089 /**
20090  * @class Roo.bootstrap.PagingToolbar
20091  * @extends Roo.Row
20092  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20093  * @constructor
20094  * Create a new PagingToolbar
20095  * @param {Object} config The config object
20096  */
20097 Roo.bootstrap.PagingToolbar = function(config)
20098 {
20099     // old args format still supported... - xtype is prefered..
20100         // created from xtype...
20101     var ds = config.dataSource;
20102     this.toolbarItems = [];
20103     if (config.items) {
20104         this.toolbarItems = config.items;
20105 //        config.items = [];
20106     }
20107     
20108     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20109     this.ds = ds;
20110     this.cursor = 0;
20111     if (ds) { 
20112         this.bind(ds);
20113     }
20114     
20115     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20116     
20117 };
20118
20119 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20120     /**
20121      * @cfg {Roo.data.Store} dataSource
20122      * The underlying data store providing the paged data
20123      */
20124     /**
20125      * @cfg {String/HTMLElement/Element} container
20126      * container The id or element that will contain the toolbar
20127      */
20128     /**
20129      * @cfg {Boolean} displayInfo
20130      * True to display the displayMsg (defaults to false)
20131      */
20132     /**
20133      * @cfg {Number} pageSize
20134      * The number of records to display per page (defaults to 20)
20135      */
20136     pageSize: 20,
20137     /**
20138      * @cfg {String} displayMsg
20139      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20140      */
20141     displayMsg : 'Displaying {0} - {1} of {2}',
20142     /**
20143      * @cfg {String} emptyMsg
20144      * The message to display when no records are found (defaults to "No data to display")
20145      */
20146     emptyMsg : 'No data to display',
20147     /**
20148      * Customizable piece of the default paging text (defaults to "Page")
20149      * @type String
20150      */
20151     beforePageText : "Page",
20152     /**
20153      * Customizable piece of the default paging text (defaults to "of %0")
20154      * @type String
20155      */
20156     afterPageText : "of {0}",
20157     /**
20158      * Customizable piece of the default paging text (defaults to "First Page")
20159      * @type String
20160      */
20161     firstText : "First Page",
20162     /**
20163      * Customizable piece of the default paging text (defaults to "Previous Page")
20164      * @type String
20165      */
20166     prevText : "Previous Page",
20167     /**
20168      * Customizable piece of the default paging text (defaults to "Next Page")
20169      * @type String
20170      */
20171     nextText : "Next Page",
20172     /**
20173      * Customizable piece of the default paging text (defaults to "Last Page")
20174      * @type String
20175      */
20176     lastText : "Last Page",
20177     /**
20178      * Customizable piece of the default paging text (defaults to "Refresh")
20179      * @type String
20180      */
20181     refreshText : "Refresh",
20182
20183     buttons : false,
20184     // private
20185     onRender : function(ct, position) 
20186     {
20187         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20188         this.navgroup.parentId = this.id;
20189         this.navgroup.onRender(this.el, null);
20190         // add the buttons to the navgroup
20191         
20192         if(this.displayInfo){
20193             Roo.log(this.el.select('ul.navbar-nav',true).first());
20194             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20195             this.displayEl = this.el.select('.x-paging-info', true).first();
20196 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20197 //            this.displayEl = navel.el.select('span',true).first();
20198         }
20199         
20200         var _this = this;
20201         
20202         if(this.buttons){
20203             Roo.each(_this.buttons, function(e){
20204                Roo.factory(e).onRender(_this.el, null);
20205             });
20206         }
20207             
20208         Roo.each(_this.toolbarItems, function(e) {
20209             _this.navgroup.addItem(e);
20210         });
20211         
20212         
20213         this.first = this.navgroup.addItem({
20214             tooltip: this.firstText,
20215             cls: "prev",
20216             icon : 'fa fa-backward',
20217             disabled: true,
20218             preventDefault: true,
20219             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20220         });
20221         
20222         this.prev =  this.navgroup.addItem({
20223             tooltip: this.prevText,
20224             cls: "prev",
20225             icon : 'fa fa-step-backward',
20226             disabled: true,
20227             preventDefault: true,
20228             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20229         });
20230     //this.addSeparator();
20231         
20232         
20233         var field = this.navgroup.addItem( {
20234             tagtype : 'span',
20235             cls : 'x-paging-position',
20236             
20237             html : this.beforePageText  +
20238                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20239                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20240          } ); //?? escaped?
20241         
20242         this.field = field.el.select('input', true).first();
20243         this.field.on("keydown", this.onPagingKeydown, this);
20244         this.field.on("focus", function(){this.dom.select();});
20245     
20246     
20247         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20248         //this.field.setHeight(18);
20249         //this.addSeparator();
20250         this.next = this.navgroup.addItem({
20251             tooltip: this.nextText,
20252             cls: "next",
20253             html : ' <i class="fa fa-step-forward">',
20254             disabled: true,
20255             preventDefault: true,
20256             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20257         });
20258         this.last = this.navgroup.addItem({
20259             tooltip: this.lastText,
20260             icon : 'fa fa-forward',
20261             cls: "next",
20262             disabled: true,
20263             preventDefault: true,
20264             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20265         });
20266     //this.addSeparator();
20267         this.loading = this.navgroup.addItem({
20268             tooltip: this.refreshText,
20269             icon: 'fa fa-refresh',
20270             preventDefault: true,
20271             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20272         });
20273
20274     },
20275
20276     // private
20277     updateInfo : function(){
20278         if(this.displayEl){
20279             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20280             var msg = count == 0 ?
20281                 this.emptyMsg :
20282                 String.format(
20283                     this.displayMsg,
20284                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20285                 );
20286             this.displayEl.update(msg);
20287         }
20288     },
20289
20290     // private
20291     onLoad : function(ds, r, o){
20292        this.cursor = o.params ? o.params.start : 0;
20293        var d = this.getPageData(),
20294             ap = d.activePage,
20295             ps = d.pages;
20296         
20297        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20298        this.field.dom.value = ap;
20299        this.first.setDisabled(ap == 1);
20300        this.prev.setDisabled(ap == 1);
20301        this.next.setDisabled(ap == ps);
20302        this.last.setDisabled(ap == ps);
20303        this.loading.enable();
20304        this.updateInfo();
20305     },
20306
20307     // private
20308     getPageData : function(){
20309         var total = this.ds.getTotalCount();
20310         return {
20311             total : total,
20312             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20313             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20314         };
20315     },
20316
20317     // private
20318     onLoadError : function(){
20319         this.loading.enable();
20320     },
20321
20322     // private
20323     onPagingKeydown : function(e){
20324         var k = e.getKey();
20325         var d = this.getPageData();
20326         if(k == e.RETURN){
20327             var v = this.field.dom.value, pageNum;
20328             if(!v || isNaN(pageNum = parseInt(v, 10))){
20329                 this.field.dom.value = d.activePage;
20330                 return;
20331             }
20332             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20333             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20334             e.stopEvent();
20335         }
20336         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))
20337         {
20338           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20339           this.field.dom.value = pageNum;
20340           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20341           e.stopEvent();
20342         }
20343         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20344         {
20345           var v = this.field.dom.value, pageNum; 
20346           var increment = (e.shiftKey) ? 10 : 1;
20347           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20348             increment *= -1;
20349           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20350             this.field.dom.value = d.activePage;
20351             return;
20352           }
20353           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
20354           {
20355             this.field.dom.value = parseInt(v, 10) + increment;
20356             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
20357             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20358           }
20359           e.stopEvent();
20360         }
20361     },
20362
20363     // private
20364     beforeLoad : function(){
20365         if(this.loading){
20366             this.loading.disable();
20367         }
20368     },
20369
20370     // private
20371     onClick : function(which){
20372         
20373         var ds = this.ds;
20374         if (!ds) {
20375             return;
20376         }
20377         
20378         switch(which){
20379             case "first":
20380                 ds.load({params:{start: 0, limit: this.pageSize}});
20381             break;
20382             case "prev":
20383                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
20384             break;
20385             case "next":
20386                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
20387             break;
20388             case "last":
20389                 var total = ds.getTotalCount();
20390                 var extra = total % this.pageSize;
20391                 var lastStart = extra ? (total - extra) : total-this.pageSize;
20392                 ds.load({params:{start: lastStart, limit: this.pageSize}});
20393             break;
20394             case "refresh":
20395                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
20396             break;
20397         }
20398     },
20399
20400     /**
20401      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
20402      * @param {Roo.data.Store} store The data store to unbind
20403      */
20404     unbind : function(ds){
20405         ds.un("beforeload", this.beforeLoad, this);
20406         ds.un("load", this.onLoad, this);
20407         ds.un("loadexception", this.onLoadError, this);
20408         ds.un("remove", this.updateInfo, this);
20409         ds.un("add", this.updateInfo, this);
20410         this.ds = undefined;
20411     },
20412
20413     /**
20414      * Binds the paging toolbar to the specified {@link Roo.data.Store}
20415      * @param {Roo.data.Store} store The data store to bind
20416      */
20417     bind : function(ds){
20418         ds.on("beforeload", this.beforeLoad, this);
20419         ds.on("load", this.onLoad, this);
20420         ds.on("loadexception", this.onLoadError, this);
20421         ds.on("remove", this.updateInfo, this);
20422         ds.on("add", this.updateInfo, this);
20423         this.ds = ds;
20424     }
20425 });/*
20426  * - LGPL
20427  *
20428  * element
20429  * 
20430  */
20431
20432 /**
20433  * @class Roo.bootstrap.MessageBar
20434  * @extends Roo.bootstrap.Component
20435  * Bootstrap MessageBar class
20436  * @cfg {String} html contents of the MessageBar
20437  * @cfg {String} weight (info | success | warning | danger) default info
20438  * @cfg {String} beforeClass insert the bar before the given class
20439  * @cfg {Boolean} closable (true | false) default false
20440  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
20441  * 
20442  * @constructor
20443  * Create a new Element
20444  * @param {Object} config The config object
20445  */
20446
20447 Roo.bootstrap.MessageBar = function(config){
20448     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
20449 };
20450
20451 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
20452     
20453     html: '',
20454     weight: 'info',
20455     closable: false,
20456     fixed: false,
20457     beforeClass: 'bootstrap-sticky-wrap',
20458     
20459     getAutoCreate : function(){
20460         
20461         var cfg = {
20462             tag: 'div',
20463             cls: 'alert alert-dismissable alert-' + this.weight,
20464             cn: [
20465                 {
20466                     tag: 'span',
20467                     cls: 'message',
20468                     html: this.html || ''
20469                 }
20470             ]
20471         }
20472         
20473         if(this.fixed){
20474             cfg.cls += ' alert-messages-fixed';
20475         }
20476         
20477         if(this.closable){
20478             cfg.cn.push({
20479                 tag: 'button',
20480                 cls: 'close',
20481                 html: 'x'
20482             });
20483         }
20484         
20485         return cfg;
20486     },
20487     
20488     onRender : function(ct, position)
20489     {
20490         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20491         
20492         if(!this.el){
20493             var cfg = Roo.apply({},  this.getAutoCreate());
20494             cfg.id = Roo.id();
20495             
20496             if (this.cls) {
20497                 cfg.cls += ' ' + this.cls;
20498             }
20499             if (this.style) {
20500                 cfg.style = this.style;
20501             }
20502             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
20503             
20504             this.el.setVisibilityMode(Roo.Element.DISPLAY);
20505         }
20506         
20507         this.el.select('>button.close').on('click', this.hide, this);
20508         
20509     },
20510     
20511     show : function()
20512     {
20513         if (!this.rendered) {
20514             this.render();
20515         }
20516         
20517         this.el.show();
20518         
20519         this.fireEvent('show', this);
20520         
20521     },
20522     
20523     hide : function()
20524     {
20525         if (!this.rendered) {
20526             this.render();
20527         }
20528         
20529         this.el.hide();
20530         
20531         this.fireEvent('hide', this);
20532     },
20533     
20534     update : function()
20535     {
20536 //        var e = this.el.dom.firstChild;
20537 //        
20538 //        if(this.closable){
20539 //            e = e.nextSibling;
20540 //        }
20541 //        
20542 //        e.data = this.html || '';
20543
20544         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
20545     }
20546    
20547 });
20548
20549  
20550
20551      /*
20552  * - LGPL
20553  *
20554  * Graph
20555  * 
20556  */
20557
20558
20559 /**
20560  * @class Roo.bootstrap.Graph
20561  * @extends Roo.bootstrap.Component
20562  * Bootstrap Graph class
20563 > Prameters
20564  -sm {number} sm 4
20565  -md {number} md 5
20566  @cfg {String} graphtype  bar | vbar | pie
20567  @cfg {number} g_x coodinator | centre x (pie)
20568  @cfg {number} g_y coodinator | centre y (pie)
20569  @cfg {number} g_r radius (pie)
20570  @cfg {number} g_height height of the chart (respected by all elements in the set)
20571  @cfg {number} g_width width of the chart (respected by all elements in the set)
20572  @cfg {Object} title The title of the chart
20573     
20574  -{Array}  values
20575  -opts (object) options for the chart 
20576      o {
20577      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
20578      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
20579      o vgutter (number)
20580      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.
20581      o stacked (boolean) whether or not to tread values as in a stacked bar chart
20582      o to
20583      o stretch (boolean)
20584      o }
20585  -opts (object) options for the pie
20586      o{
20587      o cut
20588      o startAngle (number)
20589      o endAngle (number)
20590      } 
20591  *
20592  * @constructor
20593  * Create a new Input
20594  * @param {Object} config The config object
20595  */
20596
20597 Roo.bootstrap.Graph = function(config){
20598     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
20599     
20600     this.addEvents({
20601         // img events
20602         /**
20603          * @event click
20604          * The img click event for the img.
20605          * @param {Roo.EventObject} e
20606          */
20607         "click" : true
20608     });
20609 };
20610
20611 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
20612     
20613     sm: 4,
20614     md: 5,
20615     graphtype: 'bar',
20616     g_height: 250,
20617     g_width: 400,
20618     g_x: 50,
20619     g_y: 50,
20620     g_r: 30,
20621     opts:{
20622         //g_colors: this.colors,
20623         g_type: 'soft',
20624         g_gutter: '20%'
20625
20626     },
20627     title : false,
20628
20629     getAutoCreate : function(){
20630         
20631         var cfg = {
20632             tag: 'div',
20633             html : null
20634         }
20635         
20636         
20637         return  cfg;
20638     },
20639
20640     onRender : function(ct,position){
20641         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
20642         this.raphael = Raphael(this.el.dom);
20643         
20644                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
20645                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
20646                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
20647                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
20648                 /*
20649                 r.text(160, 10, "Single Series Chart").attr(txtattr);
20650                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
20651                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
20652                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
20653                 
20654                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
20655                 r.barchart(330, 10, 300, 220, data1);
20656                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
20657                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
20658                 */
20659                 
20660                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
20661                 // r.barchart(30, 30, 560, 250,  xdata, {
20662                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
20663                 //     axis : "0 0 1 1",
20664                 //     axisxlabels :  xdata
20665                 //     //yvalues : cols,
20666                    
20667                 // });
20668 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
20669 //        
20670 //        this.load(null,xdata,{
20671 //                axis : "0 0 1 1",
20672 //                axisxlabels :  xdata
20673 //                });
20674
20675     },
20676
20677     load : function(graphtype,xdata,opts){
20678         this.raphael.clear();
20679         if(!graphtype) {
20680             graphtype = this.graphtype;
20681         }
20682         if(!opts){
20683             opts = this.opts;
20684         }
20685         var r = this.raphael,
20686             fin = function () {
20687                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
20688             },
20689             fout = function () {
20690                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
20691             },
20692             pfin = function() {
20693                 this.sector.stop();
20694                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
20695
20696                 if (this.label) {
20697                     this.label[0].stop();
20698                     this.label[0].attr({ r: 7.5 });
20699                     this.label[1].attr({ "font-weight": 800 });
20700                 }
20701             },
20702             pfout = function() {
20703                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
20704
20705                 if (this.label) {
20706                     this.label[0].animate({ r: 5 }, 500, "bounce");
20707                     this.label[1].attr({ "font-weight": 400 });
20708                 }
20709             };
20710
20711         switch(graphtype){
20712             case 'bar':
20713                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
20714                 break;
20715             case 'hbar':
20716                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
20717                 break;
20718             case 'pie':
20719 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
20720 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
20721 //            
20722                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
20723                 
20724                 break;
20725
20726         }
20727         
20728         if(this.title){
20729             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
20730         }
20731         
20732     },
20733     
20734     setTitle: function(o)
20735     {
20736         this.title = o;
20737     },
20738     
20739     initEvents: function() {
20740         
20741         if(!this.href){
20742             this.el.on('click', this.onClick, this);
20743         }
20744     },
20745     
20746     onClick : function(e)
20747     {
20748         Roo.log('img onclick');
20749         this.fireEvent('click', this, e);
20750     }
20751    
20752 });
20753
20754  
20755 /*
20756  * - LGPL
20757  *
20758  * numberBox
20759  * 
20760  */
20761 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20762
20763 /**
20764  * @class Roo.bootstrap.dash.NumberBox
20765  * @extends Roo.bootstrap.Component
20766  * Bootstrap NumberBox class
20767  * @cfg {String} headline Box headline
20768  * @cfg {String} content Box content
20769  * @cfg {String} icon Box icon
20770  * @cfg {String} footer Footer text
20771  * @cfg {String} fhref Footer href
20772  * 
20773  * @constructor
20774  * Create a new NumberBox
20775  * @param {Object} config The config object
20776  */
20777
20778
20779 Roo.bootstrap.dash.NumberBox = function(config){
20780     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
20781     
20782 };
20783
20784 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
20785     
20786     headline : '',
20787     content : '',
20788     icon : '',
20789     footer : '',
20790     fhref : '',
20791     ficon : '',
20792     
20793     getAutoCreate : function(){
20794         
20795         var cfg = {
20796             tag : 'div',
20797             cls : 'small-box ',
20798             cn : [
20799                 {
20800                     tag : 'div',
20801                     cls : 'inner',
20802                     cn :[
20803                         {
20804                             tag : 'h3',
20805                             cls : 'roo-headline',
20806                             html : this.headline
20807                         },
20808                         {
20809                             tag : 'p',
20810                             cls : 'roo-content',
20811                             html : this.content
20812                         }
20813                     ]
20814                 }
20815             ]
20816         }
20817         
20818         if(this.icon){
20819             cfg.cn.push({
20820                 tag : 'div',
20821                 cls : 'icon',
20822                 cn :[
20823                     {
20824                         tag : 'i',
20825                         cls : 'ion ' + this.icon
20826                     }
20827                 ]
20828             });
20829         }
20830         
20831         if(this.footer){
20832             var footer = {
20833                 tag : 'a',
20834                 cls : 'small-box-footer',
20835                 href : this.fhref || '#',
20836                 html : this.footer
20837             };
20838             
20839             cfg.cn.push(footer);
20840             
20841         }
20842         
20843         return  cfg;
20844     },
20845
20846     onRender : function(ct,position){
20847         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
20848
20849
20850        
20851                 
20852     },
20853
20854     setHeadline: function (value)
20855     {
20856         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
20857     },
20858     
20859     setFooter: function (value, href)
20860     {
20861         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
20862         
20863         if(href){
20864             this.el.select('a.small-box-footer',true).first().attr('href', href);
20865         }
20866         
20867     },
20868
20869     setContent: function (value)
20870     {
20871         this.el.select('.roo-content',true).first().dom.innerHTML = value;
20872     },
20873
20874     initEvents: function() 
20875     {   
20876         
20877     }
20878     
20879 });
20880
20881  
20882 /*
20883  * - LGPL
20884  *
20885  * TabBox
20886  * 
20887  */
20888 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20889
20890 /**
20891  * @class Roo.bootstrap.dash.TabBox
20892  * @extends Roo.bootstrap.Component
20893  * Bootstrap TabBox class
20894  * @cfg {String} title Title of the TabBox
20895  * @cfg {String} icon Icon of the TabBox
20896  * @cfg {Boolean} showtabs (true|false) show the tabs default true
20897  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
20898  * 
20899  * @constructor
20900  * Create a new TabBox
20901  * @param {Object} config The config object
20902  */
20903
20904
20905 Roo.bootstrap.dash.TabBox = function(config){
20906     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
20907     this.addEvents({
20908         // raw events
20909         /**
20910          * @event addpane
20911          * When a pane is added
20912          * @param {Roo.bootstrap.dash.TabPane} pane
20913          */
20914         "addpane" : true,
20915         /**
20916          * @event activatepane
20917          * When a pane is activated
20918          * @param {Roo.bootstrap.dash.TabPane} pane
20919          */
20920         "activatepane" : true
20921         
20922          
20923     });
20924     
20925     this.panes = [];
20926 };
20927
20928 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
20929
20930     title : '',
20931     icon : false,
20932     showtabs : true,
20933     tabScrollable : false,
20934     
20935     getChildContainer : function()
20936     {
20937         return this.el.select('.tab-content', true).first();
20938     },
20939     
20940     getAutoCreate : function(){
20941         
20942         var header = {
20943             tag: 'li',
20944             cls: 'pull-left header',
20945             html: this.title,
20946             cn : []
20947         };
20948         
20949         if(this.icon){
20950             header.cn.push({
20951                 tag: 'i',
20952                 cls: 'fa ' + this.icon
20953             });
20954         }
20955         
20956         var h = {
20957             tag: 'ul',
20958             cls: 'nav nav-tabs pull-right',
20959             cn: [
20960                 header
20961             ]
20962         };
20963         
20964         if(this.tabScrollable){
20965             h = {
20966                 tag: 'div',
20967                 cls: 'tab-header',
20968                 cn: [
20969                     {
20970                         tag: 'ul',
20971                         cls: 'nav nav-tabs pull-right',
20972                         cn: [
20973                             header
20974                         ]
20975                     }
20976                 ]
20977             }
20978         }
20979         
20980         var cfg = {
20981             tag: 'div',
20982             cls: 'nav-tabs-custom',
20983             cn: [
20984                 h,
20985                 {
20986                     tag: 'div',
20987                     cls: 'tab-content no-padding',
20988                     cn: []
20989                 }
20990             ]
20991         }
20992
20993         return  cfg;
20994     },
20995     initEvents : function()
20996     {
20997         //Roo.log('add add pane handler');
20998         this.on('addpane', this.onAddPane, this);
20999     },
21000      /**
21001      * Updates the box title
21002      * @param {String} html to set the title to.
21003      */
21004     setTitle : function(value)
21005     {
21006         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21007     },
21008     onAddPane : function(pane)
21009     {
21010         this.panes.push(pane);
21011         //Roo.log('addpane');
21012         //Roo.log(pane);
21013         // tabs are rendere left to right..
21014         if(!this.showtabs){
21015             return;
21016         }
21017         
21018         var ctr = this.el.select('.nav-tabs', true).first();
21019          
21020          
21021         var existing = ctr.select('.nav-tab',true);
21022         var qty = existing.getCount();;
21023         
21024         
21025         var tab = ctr.createChild({
21026             tag : 'li',
21027             cls : 'nav-tab' + (qty ? '' : ' active'),
21028             cn : [
21029                 {
21030                     tag : 'a',
21031                     href:'#',
21032                     html : pane.title
21033                 }
21034             ]
21035         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21036         pane.tab = tab;
21037         
21038         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21039         if (!qty) {
21040             pane.el.addClass('active');
21041         }
21042         
21043                 
21044     },
21045     onTabClick : function(ev,un,ob,pane)
21046     {
21047         //Roo.log('tab - prev default');
21048         ev.preventDefault();
21049         
21050         
21051         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21052         pane.tab.addClass('active');
21053         //Roo.log(pane.title);
21054         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21055         // technically we should have a deactivate event.. but maybe add later.
21056         // and it should not de-activate the selected tab...
21057         this.fireEvent('activatepane', pane);
21058         pane.el.addClass('active');
21059         pane.fireEvent('activate');
21060         
21061         
21062     },
21063     
21064     getActivePane : function()
21065     {
21066         var r = false;
21067         Roo.each(this.panes, function(p) {
21068             if(p.el.hasClass('active')){
21069                 r = p;
21070                 return false;
21071             }
21072             
21073             return;
21074         });
21075         
21076         return r;
21077     }
21078     
21079     
21080 });
21081
21082  
21083 /*
21084  * - LGPL
21085  *
21086  * Tab pane
21087  * 
21088  */
21089 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21090 /**
21091  * @class Roo.bootstrap.TabPane
21092  * @extends Roo.bootstrap.Component
21093  * Bootstrap TabPane class
21094  * @cfg {Boolean} active (false | true) Default false
21095  * @cfg {String} title title of panel
21096
21097  * 
21098  * @constructor
21099  * Create a new TabPane
21100  * @param {Object} config The config object
21101  */
21102
21103 Roo.bootstrap.dash.TabPane = function(config){
21104     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21105     
21106     this.addEvents({
21107         // raw events
21108         /**
21109          * @event activate
21110          * When a pane is activated
21111          * @param {Roo.bootstrap.dash.TabPane} pane
21112          */
21113         "activate" : true
21114          
21115     });
21116 };
21117
21118 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21119     
21120     active : false,
21121     title : '',
21122     
21123     // the tabBox that this is attached to.
21124     tab : false,
21125      
21126     getAutoCreate : function() 
21127     {
21128         var cfg = {
21129             tag: 'div',
21130             cls: 'tab-pane'
21131         }
21132         
21133         if(this.active){
21134             cfg.cls += ' active';
21135         }
21136         
21137         return cfg;
21138     },
21139     initEvents  : function()
21140     {
21141         //Roo.log('trigger add pane handler');
21142         this.parent().fireEvent('addpane', this)
21143     },
21144     
21145      /**
21146      * Updates the tab title 
21147      * @param {String} html to set the title to.
21148      */
21149     setTitle: function(str)
21150     {
21151         if (!this.tab) {
21152             return;
21153         }
21154         this.title = str;
21155         this.tab.select('a', true).first().dom.innerHTML = str;
21156         
21157     }
21158     
21159     
21160     
21161 });
21162
21163  
21164
21165
21166  /*
21167  * - LGPL
21168  *
21169  * menu
21170  * 
21171  */
21172 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21173
21174 /**
21175  * @class Roo.bootstrap.menu.Menu
21176  * @extends Roo.bootstrap.Component
21177  * Bootstrap Menu class - container for Menu
21178  * @cfg {String} html Text of the menu
21179  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21180  * @cfg {String} icon Font awesome icon
21181  * @cfg {String} pos Menu align to (top | bottom) default bottom
21182  * 
21183  * 
21184  * @constructor
21185  * Create a new Menu
21186  * @param {Object} config The config object
21187  */
21188
21189
21190 Roo.bootstrap.menu.Menu = function(config){
21191     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21192     
21193     this.addEvents({
21194         /**
21195          * @event beforeshow
21196          * Fires before this menu is displayed
21197          * @param {Roo.bootstrap.menu.Menu} this
21198          */
21199         beforeshow : true,
21200         /**
21201          * @event beforehide
21202          * Fires before this menu is hidden
21203          * @param {Roo.bootstrap.menu.Menu} this
21204          */
21205         beforehide : true,
21206         /**
21207          * @event show
21208          * Fires after this menu is displayed
21209          * @param {Roo.bootstrap.menu.Menu} this
21210          */
21211         show : true,
21212         /**
21213          * @event hide
21214          * Fires after this menu is hidden
21215          * @param {Roo.bootstrap.menu.Menu} this
21216          */
21217         hide : true,
21218         /**
21219          * @event click
21220          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21221          * @param {Roo.bootstrap.menu.Menu} this
21222          * @param {Roo.EventObject} e
21223          */
21224         click : true
21225     });
21226     
21227 };
21228
21229 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21230     
21231     submenu : false,
21232     html : '',
21233     weight : 'default',
21234     icon : false,
21235     pos : 'bottom',
21236     
21237     
21238     getChildContainer : function() {
21239         if(this.isSubMenu){
21240             return this.el;
21241         }
21242         
21243         return this.el.select('ul.dropdown-menu', true).first();  
21244     },
21245     
21246     getAutoCreate : function()
21247     {
21248         var text = [
21249             {
21250                 tag : 'span',
21251                 cls : 'roo-menu-text',
21252                 html : this.html
21253             }
21254         ];
21255         
21256         if(this.icon){
21257             text.unshift({
21258                 tag : 'i',
21259                 cls : 'fa ' + this.icon
21260             })
21261         }
21262         
21263         
21264         var cfg = {
21265             tag : 'div',
21266             cls : 'btn-group',
21267             cn : [
21268                 {
21269                     tag : 'button',
21270                     cls : 'dropdown-button btn btn-' + this.weight,
21271                     cn : text
21272                 },
21273                 {
21274                     tag : 'button',
21275                     cls : 'dropdown-toggle btn btn-' + this.weight,
21276                     cn : [
21277                         {
21278                             tag : 'span',
21279                             cls : 'caret'
21280                         }
21281                     ]
21282                 },
21283                 {
21284                     tag : 'ul',
21285                     cls : 'dropdown-menu'
21286                 }
21287             ]
21288             
21289         };
21290         
21291         if(this.pos == 'top'){
21292             cfg.cls += ' dropup';
21293         }
21294         
21295         if(this.isSubMenu){
21296             cfg = {
21297                 tag : 'ul',
21298                 cls : 'dropdown-menu'
21299             }
21300         }
21301         
21302         return cfg;
21303     },
21304     
21305     onRender : function(ct, position)
21306     {
21307         this.isSubMenu = ct.hasClass('dropdown-submenu');
21308         
21309         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21310     },
21311     
21312     initEvents : function() 
21313     {
21314         if(this.isSubMenu){
21315             return;
21316         }
21317         
21318         this.hidden = true;
21319         
21320         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21321         this.triggerEl.on('click', this.onTriggerPress, this);
21322         
21323         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21324         this.buttonEl.on('click', this.onClick, this);
21325         
21326     },
21327     
21328     list : function()
21329     {
21330         if(this.isSubMenu){
21331             return this.el;
21332         }
21333         
21334         return this.el.select('ul.dropdown-menu', true).first();
21335     },
21336     
21337     onClick : function(e)
21338     {
21339         this.fireEvent("click", this, e);
21340     },
21341     
21342     onTriggerPress  : function(e)
21343     {   
21344         if (this.isVisible()) {
21345             this.hide();
21346         } else {
21347             this.show();
21348         }
21349     },
21350     
21351     isVisible : function(){
21352         return !this.hidden;
21353     },
21354     
21355     show : function()
21356     {
21357         this.fireEvent("beforeshow", this);
21358         
21359         this.hidden = false;
21360         this.el.addClass('open');
21361         
21362         Roo.get(document).on("mouseup", this.onMouseUp, this);
21363         
21364         this.fireEvent("show", this);
21365         
21366         
21367     },
21368     
21369     hide : function()
21370     {
21371         this.fireEvent("beforehide", this);
21372         
21373         this.hidden = true;
21374         this.el.removeClass('open');
21375         
21376         Roo.get(document).un("mouseup", this.onMouseUp);
21377         
21378         this.fireEvent("hide", this);
21379     },
21380     
21381     onMouseUp : function()
21382     {
21383         this.hide();
21384     }
21385     
21386 });
21387
21388  
21389  /*
21390  * - LGPL
21391  *
21392  * menu item
21393  * 
21394  */
21395 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21396
21397 /**
21398  * @class Roo.bootstrap.menu.Item
21399  * @extends Roo.bootstrap.Component
21400  * Bootstrap MenuItem class
21401  * @cfg {Boolean} submenu (true | false) default false
21402  * @cfg {String} html text of the item
21403  * @cfg {String} href the link
21404  * @cfg {Boolean} disable (true | false) default false
21405  * @cfg {Boolean} preventDefault (true | false) default true
21406  * @cfg {String} icon Font awesome icon
21407  * @cfg {String} pos Submenu align to (left | right) default right 
21408  * 
21409  * 
21410  * @constructor
21411  * Create a new Item
21412  * @param {Object} config The config object
21413  */
21414
21415
21416 Roo.bootstrap.menu.Item = function(config){
21417     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
21418     this.addEvents({
21419         /**
21420          * @event mouseover
21421          * Fires when the mouse is hovering over this menu
21422          * @param {Roo.bootstrap.menu.Item} this
21423          * @param {Roo.EventObject} e
21424          */
21425         mouseover : true,
21426         /**
21427          * @event mouseout
21428          * Fires when the mouse exits this menu
21429          * @param {Roo.bootstrap.menu.Item} this
21430          * @param {Roo.EventObject} e
21431          */
21432         mouseout : true,
21433         // raw events
21434         /**
21435          * @event click
21436          * The raw click event for the entire grid.
21437          * @param {Roo.EventObject} e
21438          */
21439         click : true
21440     });
21441 };
21442
21443 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
21444     
21445     submenu : false,
21446     href : '',
21447     html : '',
21448     preventDefault: true,
21449     disable : false,
21450     icon : false,
21451     pos : 'right',
21452     
21453     getAutoCreate : function()
21454     {
21455         var text = [
21456             {
21457                 tag : 'span',
21458                 cls : 'roo-menu-item-text',
21459                 html : this.html
21460             }
21461         ];
21462         
21463         if(this.icon){
21464             text.unshift({
21465                 tag : 'i',
21466                 cls : 'fa ' + this.icon
21467             })
21468         }
21469         
21470         var cfg = {
21471             tag : 'li',
21472             cn : [
21473                 {
21474                     tag : 'a',
21475                     href : this.href || '#',
21476                     cn : text
21477                 }
21478             ]
21479         };
21480         
21481         if(this.disable){
21482             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
21483         }
21484         
21485         if(this.submenu){
21486             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
21487             
21488             if(this.pos == 'left'){
21489                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
21490             }
21491         }
21492         
21493         return cfg;
21494     },
21495     
21496     initEvents : function() 
21497     {
21498         this.el.on('mouseover', this.onMouseOver, this);
21499         this.el.on('mouseout', this.onMouseOut, this);
21500         
21501         this.el.select('a', true).first().on('click', this.onClick, this);
21502         
21503     },
21504     
21505     onClick : function(e)
21506     {
21507         if(this.preventDefault){
21508             e.preventDefault();
21509         }
21510         
21511         this.fireEvent("click", this, e);
21512     },
21513     
21514     onMouseOver : function(e)
21515     {
21516         if(this.submenu && this.pos == 'left'){
21517             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
21518         }
21519         
21520         this.fireEvent("mouseover", this, e);
21521     },
21522     
21523     onMouseOut : function(e)
21524     {
21525         this.fireEvent("mouseout", this, e);
21526     }
21527 });
21528
21529  
21530
21531  /*
21532  * - LGPL
21533  *
21534  * menu separator
21535  * 
21536  */
21537 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21538
21539 /**
21540  * @class Roo.bootstrap.menu.Separator
21541  * @extends Roo.bootstrap.Component
21542  * Bootstrap Separator class
21543  * 
21544  * @constructor
21545  * Create a new Separator
21546  * @param {Object} config The config object
21547  */
21548
21549
21550 Roo.bootstrap.menu.Separator = function(config){
21551     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
21552 };
21553
21554 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
21555     
21556     getAutoCreate : function(){
21557         var cfg = {
21558             tag : 'li',
21559             cls: 'divider'
21560         };
21561         
21562         return cfg;
21563     }
21564    
21565 });
21566
21567  
21568
21569  /*
21570  * - LGPL
21571  *
21572  * Tooltip
21573  * 
21574  */
21575
21576 /**
21577  * @class Roo.bootstrap.Tooltip
21578  * Bootstrap Tooltip class
21579  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
21580  * to determine which dom element triggers the tooltip.
21581  * 
21582  * It needs to add support for additional attributes like tooltip-position
21583  * 
21584  * @constructor
21585  * Create a new Toolti
21586  * @param {Object} config The config object
21587  */
21588
21589 Roo.bootstrap.Tooltip = function(config){
21590     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
21591 };
21592
21593 Roo.apply(Roo.bootstrap.Tooltip, {
21594     /**
21595      * @function init initialize tooltip monitoring.
21596      * @static
21597      */
21598     currentEl : false,
21599     currentTip : false,
21600     currentRegion : false,
21601     
21602     //  init : delay?
21603     
21604     init : function()
21605     {
21606         Roo.get(document).on('mouseover', this.enter ,this);
21607         Roo.get(document).on('mouseout', this.leave, this);
21608          
21609         
21610         this.currentTip = new Roo.bootstrap.Tooltip();
21611     },
21612     
21613     enter : function(ev)
21614     {
21615         var dom = ev.getTarget();
21616         //Roo.log(['enter',dom]);
21617         var el = Roo.fly(dom);
21618         if (this.currentEl) {
21619             //Roo.log(dom);
21620             //Roo.log(this.currentEl);
21621             //Roo.log(this.currentEl.contains(dom));
21622             if (this.currentEl == el) {
21623                 return;
21624             }
21625             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
21626                 return;
21627             }
21628
21629         }
21630         
21631         
21632         
21633         if (this.currentTip.el) {
21634             this.currentTip.el.hide(); // force hiding...
21635         }    
21636         //Roo.log(el);
21637         if (!el.attr('tooltip')) { // parents who have tip?
21638             return;
21639         }
21640         this.currentEl = el;
21641         this.currentTip.bind(el);
21642         this.currentRegion = Roo.lib.Region.getRegion(dom);
21643         this.currentTip.enter();
21644         
21645     },
21646     leave : function(ev)
21647     {
21648         var dom = ev.getTarget();
21649         //Roo.log(['leave',dom]);
21650         if (!this.currentEl) {
21651             return;
21652         }
21653         
21654         
21655         if (dom != this.currentEl.dom) {
21656             return;
21657         }
21658         var xy = ev.getXY();
21659         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
21660             return;
21661         }
21662         // only activate leave if mouse cursor is outside... bounding box..
21663         
21664         
21665         
21666         
21667         if (this.currentTip) {
21668             this.currentTip.leave();
21669         }
21670         //Roo.log('clear currentEl');
21671         this.currentEl = false;
21672         
21673         
21674     },
21675     alignment : {
21676         'left' : ['r-l', [-2,0], 'right'],
21677         'right' : ['l-r', [2,0], 'left'],
21678         'bottom' : ['t-b', [0,2], 'top'],
21679         'top' : [ 'b-t', [0,-2], 'bottom']
21680     }
21681     
21682 });
21683
21684
21685 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
21686     
21687     
21688     bindEl : false,
21689     
21690     delay : null, // can be { show : 300 , hide: 500}
21691     
21692     timeout : null,
21693     
21694     hoverState : null, //???
21695     
21696     placement : 'bottom', 
21697     
21698     getAutoCreate : function(){
21699     
21700         var cfg = {
21701            cls : 'tooltip',
21702            role : 'tooltip',
21703            cn : [
21704                 {
21705                     cls : 'tooltip-arrow'
21706                 },
21707                 {
21708                     cls : 'tooltip-inner'
21709                 }
21710            ]
21711         };
21712         
21713         return cfg;
21714     },
21715     bind : function(el)
21716     {
21717         this.bindEl = el;
21718     },
21719       
21720     
21721     enter : function () {
21722        
21723         if (this.timeout != null) {
21724             clearTimeout(this.timeout);
21725         }
21726         
21727         this.hoverState = 'in';
21728          //Roo.log("enter - show");
21729         if (!this.delay || !this.delay.show) {
21730             this.show();
21731             return;
21732         }
21733         var _t = this;
21734         this.timeout = setTimeout(function () {
21735             if (_t.hoverState == 'in') {
21736                 _t.show();
21737             }
21738         }, this.delay.show);
21739     },
21740     leave : function()
21741     {
21742         clearTimeout(this.timeout);
21743     
21744         this.hoverState = 'out';
21745          if (!this.delay || !this.delay.hide) {
21746             this.hide();
21747             return 
21748         }
21749        
21750         var _t = this;
21751         this.timeout = setTimeout(function () {
21752             //Roo.log("leave - timeout");
21753             
21754             if (_t.hoverState == 'out') {
21755                 _t.hide();
21756                 Roo.bootstrap.Tooltip.currentEl = false;
21757             }
21758         }, delay)
21759     },
21760     
21761     show : function ()
21762     {
21763         if (!this.el) {
21764             this.render(document.body);
21765         }
21766         // set content.
21767         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
21768         this.el.select('.tooltip-inner',true).first().dom.innerHTML = this.bindEl.attr('tooltip');
21769         
21770         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
21771         
21772         var placement = typeof this.placement == 'function' ?
21773             this.placement.call(this, this.el, on_el) :
21774             this.placement;
21775             
21776         var autoToken = /\s?auto?\s?/i;
21777         var autoPlace = autoToken.test(placement);
21778         if (autoPlace) {
21779             placement = placement.replace(autoToken, '') || 'top';
21780         }
21781         
21782         //this.el.detach()
21783         //this.el.setXY([0,0]);
21784         this.el.show();
21785         //this.el.dom.style.display='block';
21786         this.el.addClass(placement);
21787         
21788         //this.el.appendTo(on_el);
21789         
21790         var p = this.getPosition();
21791         var box = this.el.getBox();
21792         
21793         if (autoPlace) {
21794             // fixme..
21795         }
21796         var align = Roo.bootstrap.Tooltip.alignment[placement];
21797         this.el.alignTo(this.bindEl, align[0],align[1]);
21798         //var arrow = this.el.select('.arrow',true).first();
21799         //arrow.set(align[2], 
21800         
21801         this.el.addClass('in fade');
21802         this.hoverState = null;
21803         
21804         if (this.el.hasClass('fade')) {
21805             // fade it?
21806         }
21807         
21808     },
21809     hide : function()
21810     {
21811          
21812         if (!this.el) {
21813             return;
21814         }
21815         //this.el.setXY([0,0]);
21816         this.el.removeClass('in');
21817         //this.el.hide();
21818         
21819     }
21820     
21821 });
21822  
21823
21824  /*
21825  * - LGPL
21826  *
21827  * Location Picker
21828  * 
21829  */
21830
21831 /**
21832  * @class Roo.bootstrap.LocationPicker
21833  * @extends Roo.bootstrap.Component
21834  * Bootstrap LocationPicker class
21835  * @cfg {Number} latitude Position when init default 0
21836  * @cfg {Number} longitude Position when init default 0
21837  * @cfg {Number} zoom default 15
21838  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
21839  * @cfg {Boolean} mapTypeControl default false
21840  * @cfg {Boolean} disableDoubleClickZoom default false
21841  * @cfg {Boolean} scrollwheel default true
21842  * @cfg {Boolean} streetViewControl default false
21843  * @cfg {Number} radius default 0
21844  * @cfg {String} locationName
21845  * @cfg {Boolean} draggable default true
21846  * @cfg {Boolean} enableAutocomplete default false
21847  * @cfg {Boolean} enableReverseGeocode default true
21848  * @cfg {String} markerTitle
21849  * 
21850  * @constructor
21851  * Create a new LocationPicker
21852  * @param {Object} config The config object
21853  */
21854
21855
21856 Roo.bootstrap.LocationPicker = function(config){
21857     
21858     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
21859     
21860     this.addEvents({
21861         /**
21862          * @event initial
21863          * Fires when the picker initialized.
21864          * @param {Roo.bootstrap.LocationPicker} this
21865          * @param {Google Location} location
21866          */
21867         initial : true,
21868         /**
21869          * @event positionchanged
21870          * Fires when the picker position changed.
21871          * @param {Roo.bootstrap.LocationPicker} this
21872          * @param {Google Location} location
21873          */
21874         positionchanged : true,
21875         /**
21876          * @event resize
21877          * Fires when the map resize.
21878          * @param {Roo.bootstrap.LocationPicker} this
21879          */
21880         resize : true,
21881         /**
21882          * @event show
21883          * Fires when the map show.
21884          * @param {Roo.bootstrap.LocationPicker} this
21885          */
21886         show : true,
21887         /**
21888          * @event hide
21889          * Fires when the map hide.
21890          * @param {Roo.bootstrap.LocationPicker} this
21891          */
21892         hide : true,
21893         /**
21894          * @event mapClick
21895          * Fires when click the map.
21896          * @param {Roo.bootstrap.LocationPicker} this
21897          * @param {Map event} e
21898          */
21899         mapClick : true,
21900         /**
21901          * @event mapRightClick
21902          * Fires when right click the map.
21903          * @param {Roo.bootstrap.LocationPicker} this
21904          * @param {Map event} e
21905          */
21906         mapRightClick : true,
21907         /**
21908          * @event markerClick
21909          * Fires when click the marker.
21910          * @param {Roo.bootstrap.LocationPicker} this
21911          * @param {Map event} e
21912          */
21913         markerClick : true,
21914         /**
21915          * @event markerRightClick
21916          * Fires when right click the marker.
21917          * @param {Roo.bootstrap.LocationPicker} this
21918          * @param {Map event} e
21919          */
21920         markerRightClick : true,
21921         /**
21922          * @event OverlayViewDraw
21923          * Fires when OverlayView Draw
21924          * @param {Roo.bootstrap.LocationPicker} this
21925          */
21926         OverlayViewDraw : true,
21927         /**
21928          * @event OverlayViewOnAdd
21929          * Fires when OverlayView Draw
21930          * @param {Roo.bootstrap.LocationPicker} this
21931          */
21932         OverlayViewOnAdd : true,
21933         /**
21934          * @event OverlayViewOnRemove
21935          * Fires when OverlayView Draw
21936          * @param {Roo.bootstrap.LocationPicker} this
21937          */
21938         OverlayViewOnRemove : true,
21939         /**
21940          * @event OverlayViewShow
21941          * Fires when OverlayView Draw
21942          * @param {Roo.bootstrap.LocationPicker} this
21943          * @param {Pixel} cpx
21944          */
21945         OverlayViewShow : true,
21946         /**
21947          * @event OverlayViewHide
21948          * Fires when OverlayView Draw
21949          * @param {Roo.bootstrap.LocationPicker} this
21950          */
21951         OverlayViewHide : true
21952     });
21953         
21954 };
21955
21956 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
21957     
21958     gMapContext: false,
21959     
21960     latitude: 0,
21961     longitude: 0,
21962     zoom: 15,
21963     mapTypeId: false,
21964     mapTypeControl: false,
21965     disableDoubleClickZoom: false,
21966     scrollwheel: true,
21967     streetViewControl: false,
21968     radius: 0,
21969     locationName: '',
21970     draggable: true,
21971     enableAutocomplete: false,
21972     enableReverseGeocode: true,
21973     markerTitle: '',
21974     
21975     getAutoCreate: function()
21976     {
21977
21978         var cfg = {
21979             tag: 'div',
21980             cls: 'roo-location-picker'
21981         };
21982         
21983         return cfg
21984     },
21985     
21986     initEvents: function(ct, position)
21987     {       
21988         if(!this.el.getWidth() || this.isApplied()){
21989             return;
21990         }
21991         
21992         this.el.setVisibilityMode(Roo.Element.DISPLAY);
21993         
21994         this.initial();
21995     },
21996     
21997     initial: function()
21998     {
21999         if(!this.mapTypeId){
22000             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22001         }
22002         
22003         this.gMapContext = this.GMapContext();
22004         
22005         this.initOverlayView();
22006         
22007         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22008         
22009         var _this = this;
22010                 
22011         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22012             _this.setPosition(_this.gMapContext.marker.position);
22013         });
22014         
22015         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22016             _this.fireEvent('mapClick', this, event);
22017             
22018         });
22019
22020         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22021             _this.fireEvent('mapRightClick', this, event);
22022             
22023         });
22024         
22025         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22026             _this.fireEvent('markerClick', this, event);
22027             
22028         });
22029
22030         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22031             _this.fireEvent('markerRightClick', this, event);
22032             
22033         });
22034         
22035         this.setPosition(this.gMapContext.location);
22036         
22037         this.fireEvent('initial', this, this.gMapContext.location);
22038     },
22039     
22040     initOverlayView: function()
22041     {
22042         var _this = this;
22043         
22044         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22045             
22046             draw: function()
22047             {
22048                 _this.fireEvent('OverlayViewDraw', _this);
22049             },
22050             
22051             onAdd: function()
22052             {
22053                 _this.fireEvent('OverlayViewOnAdd', _this);
22054             },
22055             
22056             onRemove: function()
22057             {
22058                 _this.fireEvent('OverlayViewOnRemove', _this);
22059             },
22060             
22061             show: function(cpx)
22062             {
22063                 _this.fireEvent('OverlayViewShow', _this, cpx);
22064             },
22065             
22066             hide: function()
22067             {
22068                 _this.fireEvent('OverlayViewHide', _this);
22069             }
22070             
22071         });
22072     },
22073     
22074     fromLatLngToContainerPixel: function(event)
22075     {
22076         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22077     },
22078     
22079     isApplied: function() 
22080     {
22081         return this.getGmapContext() == false ? false : true;
22082     },
22083     
22084     getGmapContext: function() 
22085     {
22086         return this.gMapContext
22087     },
22088     
22089     GMapContext: function() 
22090     {
22091         var _map = new google.maps.Map(this.el.dom, this);
22092         var _marker = new google.maps.Marker({
22093             position: new google.maps.LatLng(this.latitude, this.longitude),
22094             map: _map,
22095             title: this.markerTitle,
22096             draggable: this.draggable
22097         });
22098         
22099         return {
22100             map: _map,
22101             marker: _marker,
22102             circle: null,
22103             location: _marker.position,
22104             radius: this.radius,
22105             locationName: this.locationName,
22106             addressComponents: {
22107                 formatted_address: null,
22108                 addressLine1: null,
22109                 addressLine2: null,
22110                 streetName: null,
22111                 streetNumber: null,
22112                 city: null,
22113                 district: null,
22114                 state: null,
22115                 stateOrProvince: null
22116             },
22117             settings: this,
22118             domContainer: this.el.dom,
22119             geodecoder: new google.maps.Geocoder()
22120         };
22121     },
22122     
22123     drawCircle: function(center, radius, options) 
22124     {
22125         if (this.gMapContext.circle != null) {
22126             this.gMapContext.circle.setMap(null);
22127         }
22128         if (radius > 0) {
22129             radius *= 1;
22130             options = Roo.apply({}, options, {
22131                 strokeColor: "#0000FF",
22132                 strokeOpacity: .35,
22133                 strokeWeight: 2,
22134                 fillColor: "#0000FF",
22135                 fillOpacity: .2
22136             });
22137             
22138             options.map = this.gMapContext.map;
22139             options.radius = radius;
22140             options.center = center;
22141             this.gMapContext.circle = new google.maps.Circle(options);
22142             return this.gMapContext.circle;
22143         }
22144         
22145         return null;
22146     },
22147     
22148     setPosition: function(location) 
22149     {
22150         this.gMapContext.location = location;
22151         this.gMapContext.marker.setPosition(location);
22152         this.gMapContext.map.panTo(location);
22153         this.drawCircle(location, this.gMapContext.radius, {});
22154         
22155         var _this = this;
22156         
22157         if (this.gMapContext.settings.enableReverseGeocode) {
22158             this.gMapContext.geodecoder.geocode({
22159                 latLng: this.gMapContext.location
22160             }, function(results, status) {
22161                 
22162                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22163                     _this.gMapContext.locationName = results[0].formatted_address;
22164                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22165                     
22166                     _this.fireEvent('positionchanged', this, location);
22167                 }
22168             });
22169             
22170             return;
22171         }
22172         
22173         this.fireEvent('positionchanged', this, location);
22174     },
22175     
22176     resize: function()
22177     {
22178         google.maps.event.trigger(this.gMapContext.map, "resize");
22179         
22180         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22181         
22182         this.fireEvent('resize', this);
22183     },
22184     
22185     setPositionByLatLng: function(latitude, longitude)
22186     {
22187         this.setPosition(new google.maps.LatLng(latitude, longitude));
22188     },
22189     
22190     getCurrentPosition: function() 
22191     {
22192         return {
22193             latitude: this.gMapContext.location.lat(),
22194             longitude: this.gMapContext.location.lng()
22195         };
22196     },
22197     
22198     getAddressName: function() 
22199     {
22200         return this.gMapContext.locationName;
22201     },
22202     
22203     getAddressComponents: function() 
22204     {
22205         return this.gMapContext.addressComponents;
22206     },
22207     
22208     address_component_from_google_geocode: function(address_components) 
22209     {
22210         var result = {};
22211         
22212         for (var i = 0; i < address_components.length; i++) {
22213             var component = address_components[i];
22214             if (component.types.indexOf("postal_code") >= 0) {
22215                 result.postalCode = component.short_name;
22216             } else if (component.types.indexOf("street_number") >= 0) {
22217                 result.streetNumber = component.short_name;
22218             } else if (component.types.indexOf("route") >= 0) {
22219                 result.streetName = component.short_name;
22220             } else if (component.types.indexOf("neighborhood") >= 0) {
22221                 result.city = component.short_name;
22222             } else if (component.types.indexOf("locality") >= 0) {
22223                 result.city = component.short_name;
22224             } else if (component.types.indexOf("sublocality") >= 0) {
22225                 result.district = component.short_name;
22226             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22227                 result.stateOrProvince = component.short_name;
22228             } else if (component.types.indexOf("country") >= 0) {
22229                 result.country = component.short_name;
22230             }
22231         }
22232         
22233         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22234         result.addressLine2 = "";
22235         return result;
22236     },
22237     
22238     setZoomLevel: function(zoom)
22239     {
22240         this.gMapContext.map.setZoom(zoom);
22241     },
22242     
22243     show: function()
22244     {
22245         if(!this.el){
22246             return;
22247         }
22248         
22249         this.el.show();
22250         
22251         this.resize();
22252         
22253         this.fireEvent('show', this);
22254     },
22255     
22256     hide: function()
22257     {
22258         if(!this.el){
22259             return;
22260         }
22261         
22262         this.el.hide();
22263         
22264         this.fireEvent('hide', this);
22265     }
22266     
22267 });
22268
22269 Roo.apply(Roo.bootstrap.LocationPicker, {
22270     
22271     OverlayView : function(map, options)
22272     {
22273         options = options || {};
22274         
22275         this.setMap(map);
22276     }
22277     
22278     
22279 });/*
22280  * - LGPL
22281  *
22282  * Alert
22283  * 
22284  */
22285
22286 /**
22287  * @class Roo.bootstrap.Alert
22288  * @extends Roo.bootstrap.Component
22289  * Bootstrap Alert class
22290  * @cfg {String} title The title of alert
22291  * @cfg {String} html The content of alert
22292  * @cfg {String} weight (  success | info | warning | danger )
22293  * @cfg {String} faicon font-awesomeicon
22294  * 
22295  * @constructor
22296  * Create a new alert
22297  * @param {Object} config The config object
22298  */
22299
22300
22301 Roo.bootstrap.Alert = function(config){
22302     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22303     
22304 };
22305
22306 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22307     
22308     title: '',
22309     html: '',
22310     weight: false,
22311     faicon: false,
22312     
22313     getAutoCreate : function()
22314     {
22315         
22316         var cfg = {
22317             tag : 'div',
22318             cls : 'alert',
22319             cn : [
22320                 {
22321                     tag : 'i',
22322                     cls : 'roo-alert-icon'
22323                     
22324                 },
22325                 {
22326                     tag : 'b',
22327                     cls : 'roo-alert-title',
22328                     html : this.title
22329                 },
22330                 {
22331                     tag : 'span',
22332                     cls : 'roo-alert-text',
22333                     html : this.html
22334                 }
22335             ]
22336         };
22337         
22338         if(this.faicon){
22339             cfg.cn[0].cls += ' fa ' + this.faicon;
22340         }
22341         
22342         if(this.weight){
22343             cfg.cls += ' alert-' + this.weight;
22344         }
22345         
22346         return cfg;
22347     },
22348     
22349     initEvents: function() 
22350     {
22351         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22352     },
22353     
22354     setTitle : function(str)
22355     {
22356         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
22357     },
22358     
22359     setText : function(str)
22360     {
22361         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
22362     },
22363     
22364     setWeight : function(weight)
22365     {
22366         if(this.weight){
22367             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
22368         }
22369         
22370         this.weight = weight;
22371         
22372         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
22373     },
22374     
22375     setIcon : function(icon)
22376     {
22377         if(this.faicon){
22378             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
22379         }
22380         
22381         this.faicon = icon
22382         
22383         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
22384     },
22385     
22386     hide: function() 
22387     {
22388         this.el.hide();   
22389     },
22390     
22391     show: function() 
22392     {  
22393         this.el.show();   
22394     }
22395     
22396 });
22397
22398