Roo/bootstrap/Radio.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 });
1129
1130  /*
1131  * - LGPL
1132  *
1133  * image
1134  * 
1135  */
1136
1137
1138 /**
1139  * @class Roo.bootstrap.Img
1140  * @extends Roo.bootstrap.Component
1141  * Bootstrap Img class
1142  * @cfg {Boolean} imgResponsive false | true
1143  * @cfg {String} border rounded | circle | thumbnail
1144  * @cfg {String} src image source
1145  * @cfg {String} alt image alternative text
1146  * @cfg {String} href a tag href
1147  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1148  * 
1149  * @constructor
1150  * Create a new Input
1151  * @param {Object} config The config object
1152  */
1153
1154 Roo.bootstrap.Img = function(config){
1155     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1156     
1157     this.addEvents({
1158         // img events
1159         /**
1160          * @event click
1161          * The img click event for the img.
1162          * @param {Roo.EventObject} e
1163          */
1164         "click" : true
1165     });
1166 };
1167
1168 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1169     
1170     imgResponsive: true,
1171     border: '',
1172     src: '',
1173     href: false,
1174     target: false,
1175
1176     getAutoCreate : function(){
1177         
1178         var cfg = {
1179             tag: 'img',
1180             cls: (this.imgResponsive) ? 'img-responsive' : '',
1181             html : null
1182         }
1183         
1184         cfg.html = this.html || cfg.html;
1185         
1186         cfg.src = this.src || cfg.src;
1187         
1188         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1189             cfg.cls += ' img-' + this.border;
1190         }
1191         
1192         if(this.alt){
1193             cfg.alt = this.alt;
1194         }
1195         
1196         if(this.href){
1197             var a = {
1198                 tag: 'a',
1199                 href: this.href,
1200                 cn: [
1201                     cfg
1202                 ]
1203             }
1204             
1205             if(this.target){
1206                 a.target = this.target;
1207             }
1208             
1209         }
1210         
1211         
1212         return (this.href) ? a : cfg;
1213     },
1214     
1215     initEvents: function() {
1216         
1217         if(!this.href){
1218             this.el.on('click', this.onClick, this);
1219         }
1220     },
1221     
1222     onClick : function(e)
1223     {
1224         Roo.log('img onclick');
1225         this.fireEvent('click', this, e);
1226     }
1227    
1228 });
1229
1230  /*
1231  * - LGPL
1232  *
1233  * image
1234  * 
1235  */
1236
1237
1238 /**
1239  * @class Roo.bootstrap.Link
1240  * @extends Roo.bootstrap.Component
1241  * Bootstrap Link Class
1242  * @cfg {String} alt image alternative text
1243  * @cfg {String} href a tag href
1244  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1245  * @cfg {String} html the content of the link.
1246  * @cfg {String} anchor name for the anchor link
1247
1248  * @cfg {Boolean} preventDefault (true | false) default false
1249
1250  * 
1251  * @constructor
1252  * Create a new Input
1253  * @param {Object} config The config object
1254  */
1255
1256 Roo.bootstrap.Link = function(config){
1257     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1258     
1259     this.addEvents({
1260         // img events
1261         /**
1262          * @event click
1263          * The img click event for the img.
1264          * @param {Roo.EventObject} e
1265          */
1266         "click" : true
1267     });
1268 };
1269
1270 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1271     
1272     href: false,
1273     target: false,
1274     preventDefault: false,
1275     anchor : false,
1276     alt : false,
1277
1278     getAutoCreate : function()
1279     {
1280         
1281         var cfg = {
1282             tag: 'a'
1283         };
1284         // anchor's do not require html/href...
1285         if (this.anchor === false) {
1286             cfg.html = this.html || 'html-missing';
1287             cfg.href = this.href || '#';
1288         } else {
1289             cfg.name = this.anchor;
1290             if (this.html !== false) {
1291                 cfg.html = this.html;
1292             }
1293             if (this.href !== false) {
1294                 cfg.href = this.href;
1295             }
1296         }
1297         
1298         if(this.alt !== false){
1299             cfg.alt = this.alt;
1300         }
1301         
1302         
1303         if(this.target !== false) {
1304             cfg.target = this.target;
1305         }
1306         
1307         return cfg;
1308     },
1309     
1310     initEvents: function() {
1311         
1312         if(!this.href || this.preventDefault){
1313             this.el.on('click', this.onClick, this);
1314         }
1315     },
1316     
1317     onClick : function(e)
1318     {
1319         if(this.preventDefault){
1320             e.preventDefault();
1321         }
1322         //Roo.log('img onclick');
1323         this.fireEvent('click', this, e);
1324     }
1325    
1326 });
1327
1328  /*
1329  * - LGPL
1330  *
1331  * header
1332  * 
1333  */
1334
1335 /**
1336  * @class Roo.bootstrap.Header
1337  * @extends Roo.bootstrap.Component
1338  * Bootstrap Header class
1339  * @cfg {String} html content of header
1340  * @cfg {Number} level (1|2|3|4|5|6) default 1
1341  * 
1342  * @constructor
1343  * Create a new Header
1344  * @param {Object} config The config object
1345  */
1346
1347
1348 Roo.bootstrap.Header  = function(config){
1349     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1350 };
1351
1352 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1353     
1354     //href : false,
1355     html : false,
1356     level : 1,
1357     
1358     
1359     
1360     getAutoCreate : function(){
1361         
1362         var cfg = {
1363             tag: 'h' + (1 *this.level),
1364             html: this.html || 'fill in html'
1365         } ;
1366         
1367         return cfg;
1368     }
1369    
1370 });
1371
1372  
1373
1374  /*
1375  * Based on:
1376  * Ext JS Library 1.1.1
1377  * Copyright(c) 2006-2007, Ext JS, LLC.
1378  *
1379  * Originally Released Under LGPL - original licence link has changed is not relivant.
1380  *
1381  * Fork - LGPL
1382  * <script type="text/javascript">
1383  */
1384  
1385 /**
1386  * @class Roo.bootstrap.MenuMgr
1387  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1388  * @singleton
1389  */
1390 Roo.bootstrap.MenuMgr = function(){
1391    var menus, active, groups = {}, attached = false, lastShow = new Date();
1392
1393    // private - called when first menu is created
1394    function init(){
1395        menus = {};
1396        active = new Roo.util.MixedCollection();
1397        Roo.get(document).addKeyListener(27, function(){
1398            if(active.length > 0){
1399                hideAll();
1400            }
1401        });
1402    }
1403
1404    // private
1405    function hideAll(){
1406        if(active && active.length > 0){
1407            var c = active.clone();
1408            c.each(function(m){
1409                m.hide();
1410            });
1411        }
1412    }
1413
1414    // private
1415    function onHide(m){
1416        active.remove(m);
1417        if(active.length < 1){
1418            Roo.get(document).un("mouseup", onMouseDown);
1419             
1420            attached = false;
1421        }
1422    }
1423
1424    // private
1425    function onShow(m){
1426        var last = active.last();
1427        lastShow = new Date();
1428        active.add(m);
1429        if(!attached){
1430           Roo.get(document).on("mouseup", onMouseDown);
1431            
1432            attached = true;
1433        }
1434        if(m.parentMenu){
1435           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1436           m.parentMenu.activeChild = m;
1437        }else if(last && last.isVisible()){
1438           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1439        }
1440    }
1441
1442    // private
1443    function onBeforeHide(m){
1444        if(m.activeChild){
1445            m.activeChild.hide();
1446        }
1447        if(m.autoHideTimer){
1448            clearTimeout(m.autoHideTimer);
1449            delete m.autoHideTimer;
1450        }
1451    }
1452
1453    // private
1454    function onBeforeShow(m){
1455        var pm = m.parentMenu;
1456        if(!pm && !m.allowOtherMenus){
1457            hideAll();
1458        }else if(pm && pm.activeChild && active != m){
1459            pm.activeChild.hide();
1460        }
1461    }
1462
1463    // private
1464    function onMouseDown(e){
1465         Roo.log("on MouseDown");
1466         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1467            hideAll();
1468         }
1469         
1470         
1471    }
1472
1473    // private
1474    function onBeforeCheck(mi, state){
1475        if(state){
1476            var g = groups[mi.group];
1477            for(var i = 0, l = g.length; i < l; i++){
1478                if(g[i] != mi){
1479                    g[i].setChecked(false);
1480                }
1481            }
1482        }
1483    }
1484
1485    return {
1486
1487        /**
1488         * Hides all menus that are currently visible
1489         */
1490        hideAll : function(){
1491             hideAll();  
1492        },
1493
1494        // private
1495        register : function(menu){
1496            if(!menus){
1497                init();
1498            }
1499            menus[menu.id] = menu;
1500            menu.on("beforehide", onBeforeHide);
1501            menu.on("hide", onHide);
1502            menu.on("beforeshow", onBeforeShow);
1503            menu.on("show", onShow);
1504            var g = menu.group;
1505            if(g && menu.events["checkchange"]){
1506                if(!groups[g]){
1507                    groups[g] = [];
1508                }
1509                groups[g].push(menu);
1510                menu.on("checkchange", onCheck);
1511            }
1512        },
1513
1514         /**
1515          * Returns a {@link Roo.menu.Menu} object
1516          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1517          * be used to generate and return a new Menu instance.
1518          */
1519        get : function(menu){
1520            if(typeof menu == "string"){ // menu id
1521                return menus[menu];
1522            }else if(menu.events){  // menu instance
1523                return menu;
1524            }
1525            /*else if(typeof menu.length == 'number'){ // array of menu items?
1526                return new Roo.bootstrap.Menu({items:menu});
1527            }else{ // otherwise, must be a config
1528                return new Roo.bootstrap.Menu(menu);
1529            }
1530            */
1531            return false;
1532        },
1533
1534        // private
1535        unregister : function(menu){
1536            delete menus[menu.id];
1537            menu.un("beforehide", onBeforeHide);
1538            menu.un("hide", onHide);
1539            menu.un("beforeshow", onBeforeShow);
1540            menu.un("show", onShow);
1541            var g = menu.group;
1542            if(g && menu.events["checkchange"]){
1543                groups[g].remove(menu);
1544                menu.un("checkchange", onCheck);
1545            }
1546        },
1547
1548        // private
1549        registerCheckable : function(menuItem){
1550            var g = menuItem.group;
1551            if(g){
1552                if(!groups[g]){
1553                    groups[g] = [];
1554                }
1555                groups[g].push(menuItem);
1556                menuItem.on("beforecheckchange", onBeforeCheck);
1557            }
1558        },
1559
1560        // private
1561        unregisterCheckable : function(menuItem){
1562            var g = menuItem.group;
1563            if(g){
1564                groups[g].remove(menuItem);
1565                menuItem.un("beforecheckchange", onBeforeCheck);
1566            }
1567        }
1568    };
1569 }();/*
1570  * - LGPL
1571  *
1572  * menu
1573  * 
1574  */
1575
1576 /**
1577  * @class Roo.bootstrap.Menu
1578  * @extends Roo.bootstrap.Component
1579  * Bootstrap Menu class - container for MenuItems
1580  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1581  * 
1582  * @constructor
1583  * Create a new Menu
1584  * @param {Object} config The config object
1585  */
1586
1587
1588 Roo.bootstrap.Menu = function(config){
1589     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1590     if (this.registerMenu) {
1591         Roo.bootstrap.MenuMgr.register(this);
1592     }
1593     this.addEvents({
1594         /**
1595          * @event beforeshow
1596          * Fires before this menu is displayed
1597          * @param {Roo.menu.Menu} this
1598          */
1599         beforeshow : true,
1600         /**
1601          * @event beforehide
1602          * Fires before this menu is hidden
1603          * @param {Roo.menu.Menu} this
1604          */
1605         beforehide : true,
1606         /**
1607          * @event show
1608          * Fires after this menu is displayed
1609          * @param {Roo.menu.Menu} this
1610          */
1611         show : true,
1612         /**
1613          * @event hide
1614          * Fires after this menu is hidden
1615          * @param {Roo.menu.Menu} this
1616          */
1617         hide : true,
1618         /**
1619          * @event click
1620          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1621          * @param {Roo.menu.Menu} this
1622          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1623          * @param {Roo.EventObject} e
1624          */
1625         click : true,
1626         /**
1627          * @event mouseover
1628          * Fires when the mouse is hovering over this menu
1629          * @param {Roo.menu.Menu} this
1630          * @param {Roo.EventObject} e
1631          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1632          */
1633         mouseover : true,
1634         /**
1635          * @event mouseout
1636          * Fires when the mouse exits this menu
1637          * @param {Roo.menu.Menu} this
1638          * @param {Roo.EventObject} e
1639          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1640          */
1641         mouseout : true,
1642         /**
1643          * @event itemclick
1644          * Fires when a menu item contained in this menu is clicked
1645          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1646          * @param {Roo.EventObject} e
1647          */
1648         itemclick: true
1649     });
1650     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1651 };
1652
1653 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1654     
1655    /// html : false,
1656     //align : '',
1657     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1658     type: false,
1659     /**
1660      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1661      */
1662     registerMenu : true,
1663     
1664     menuItems :false, // stores the menu items..
1665     
1666     hidden:true,
1667     
1668     parentMenu : false,
1669     
1670     getChildContainer : function() {
1671         return this.el;  
1672     },
1673     
1674     getAutoCreate : function(){
1675          
1676         //if (['right'].indexOf(this.align)!==-1) {
1677         //    cfg.cn[1].cls += ' pull-right'
1678         //}
1679         
1680         
1681         var cfg = {
1682             tag : 'ul',
1683             cls : 'dropdown-menu' ,
1684             style : 'z-index:1000'
1685             
1686         }
1687         
1688         if (this.type === 'submenu') {
1689             cfg.cls = 'submenu active';
1690         }
1691         if (this.type === 'treeview') {
1692             cfg.cls = 'treeview-menu';
1693         }
1694         
1695         return cfg;
1696     },
1697     initEvents : function() {
1698         
1699        // Roo.log("ADD event");
1700        // Roo.log(this.triggerEl.dom);
1701         this.triggerEl.on('click', this.onTriggerPress, this);
1702         this.triggerEl.addClass('dropdown-toggle');
1703         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1704
1705         this.el.on("mouseover", this.onMouseOver, this);
1706         this.el.on("mouseout", this.onMouseOut, this);
1707         
1708         
1709     },
1710     findTargetItem : function(e){
1711         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1712         if(!t){
1713             return false;
1714         }
1715         //Roo.log(t);         Roo.log(t.id);
1716         if(t && t.id){
1717             //Roo.log(this.menuitems);
1718             return this.menuitems.get(t.id);
1719             
1720             //return this.items.get(t.menuItemId);
1721         }
1722         
1723         return false;
1724     },
1725     onClick : function(e){
1726         Roo.log("menu.onClick");
1727         var t = this.findTargetItem(e);
1728         if(!t || t.isContainer){
1729             return;
1730         }
1731         Roo.log(e);
1732         /*
1733         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1734             if(t == this.activeItem && t.shouldDeactivate(e)){
1735                 this.activeItem.deactivate();
1736                 delete this.activeItem;
1737                 return;
1738             }
1739             if(t.canActivate){
1740                 this.setActiveItem(t, true);
1741             }
1742             return;
1743             
1744             
1745         }
1746         */
1747        
1748         Roo.log('pass click event');
1749         
1750         t.onClick(e);
1751         
1752         this.fireEvent("click", this, t, e);
1753         
1754         this.hide();
1755     },
1756      onMouseOver : function(e){
1757         var t  = this.findTargetItem(e);
1758         //Roo.log(t);
1759         //if(t){
1760         //    if(t.canActivate && !t.disabled){
1761         //        this.setActiveItem(t, true);
1762         //    }
1763         //}
1764         
1765         this.fireEvent("mouseover", this, e, t);
1766     },
1767     isVisible : function(){
1768         return !this.hidden;
1769     },
1770      onMouseOut : function(e){
1771         var t  = this.findTargetItem(e);
1772         
1773         //if(t ){
1774         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1775         //        this.activeItem.deactivate();
1776         //        delete this.activeItem;
1777         //    }
1778         //}
1779         this.fireEvent("mouseout", this, e, t);
1780     },
1781     
1782     
1783     /**
1784      * Displays this menu relative to another element
1785      * @param {String/HTMLElement/Roo.Element} element The element to align to
1786      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1787      * the element (defaults to this.defaultAlign)
1788      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1789      */
1790     show : function(el, pos, parentMenu){
1791         this.parentMenu = parentMenu;
1792         if(!this.el){
1793             this.render();
1794         }
1795         this.fireEvent("beforeshow", this);
1796         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1797     },
1798      /**
1799      * Displays this menu at a specific xy position
1800      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1801      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1802      */
1803     showAt : function(xy, parentMenu, /* private: */_e){
1804         this.parentMenu = parentMenu;
1805         if(!this.el){
1806             this.render();
1807         }
1808         if(_e !== false){
1809             this.fireEvent("beforeshow", this);
1810             
1811             //xy = this.el.adjustForConstraints(xy);
1812         }
1813         //this.el.setXY(xy);
1814         //this.el.show();
1815         this.hideMenuItems();
1816         this.hidden = false;
1817         this.triggerEl.addClass('open');
1818         this.focus();
1819         this.fireEvent("show", this);
1820     },
1821     
1822     focus : function(){
1823         return;
1824         if(!this.hidden){
1825             this.doFocus.defer(50, this);
1826         }
1827     },
1828
1829     doFocus : function(){
1830         if(!this.hidden){
1831             this.focusEl.focus();
1832         }
1833     },
1834
1835     /**
1836      * Hides this menu and optionally all parent menus
1837      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1838      */
1839     hide : function(deep){
1840         
1841         this.hideMenuItems();
1842         if(this.el && this.isVisible()){
1843             this.fireEvent("beforehide", this);
1844             if(this.activeItem){
1845                 this.activeItem.deactivate();
1846                 this.activeItem = null;
1847             }
1848             this.triggerEl.removeClass('open');;
1849             this.hidden = true;
1850             this.fireEvent("hide", this);
1851         }
1852         if(deep === true && this.parentMenu){
1853             this.parentMenu.hide(true);
1854         }
1855     },
1856     
1857     onTriggerPress  : function(e)
1858     {
1859         
1860         Roo.log('trigger press');
1861         //Roo.log(e.getTarget());
1862        // Roo.log(this.triggerEl.dom);
1863         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1864             return;
1865         }
1866         if (this.isVisible()) {
1867             Roo.log('hide');
1868             this.hide();
1869         } else {
1870             this.show(this.triggerEl, false, false);
1871         }
1872         
1873         
1874     },
1875     
1876          
1877        
1878     
1879     hideMenuItems : function()
1880     {
1881         //$(backdrop).remove()
1882         Roo.select('.open',true).each(function(aa) {
1883             
1884             aa.removeClass('open');
1885           //var parent = getParent($(this))
1886           //var relatedTarget = { relatedTarget: this }
1887           
1888            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1889           //if (e.isDefaultPrevented()) return
1890            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1891         })
1892     },
1893     addxtypeChild : function (tree, cntr) {
1894         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1895           
1896         this.menuitems.add(comp);
1897         return comp;
1898
1899     },
1900     getEl : function()
1901     {
1902         Roo.log(this.el);
1903         return this.el;
1904     }
1905 });
1906
1907  
1908  /*
1909  * - LGPL
1910  *
1911  * menu item
1912  * 
1913  */
1914
1915
1916 /**
1917  * @class Roo.bootstrap.MenuItem
1918  * @extends Roo.bootstrap.Component
1919  * Bootstrap MenuItem class
1920  * @cfg {String} html the menu label
1921  * @cfg {String} href the link
1922  * @cfg {Boolean} preventDefault (true | false) default true
1923  * @cfg {Boolean} isContainer (true | false) default false
1924  * 
1925  * 
1926  * @constructor
1927  * Create a new MenuItem
1928  * @param {Object} config The config object
1929  */
1930
1931
1932 Roo.bootstrap.MenuItem = function(config){
1933     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1934     this.addEvents({
1935         // raw events
1936         /**
1937          * @event click
1938          * The raw click event for the entire grid.
1939          * @param {Roo.EventObject} e
1940          */
1941         "click" : true
1942     });
1943 };
1944
1945 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1946     
1947     href : false,
1948     html : false,
1949     preventDefault: true,
1950     isContainer : false,
1951     
1952     getAutoCreate : function(){
1953         
1954         if(this.isContainer){
1955             return {
1956                 tag: 'li',
1957                 cls: 'dropdown-menu-item'
1958             };
1959         }
1960         
1961         var cfg= {
1962             tag: 'li',
1963             cls: 'dropdown-menu-item',
1964             cn: [
1965                     {
1966                         tag : 'a',
1967                         href : '#',
1968                         html : 'Link'
1969                     }
1970                 ]
1971         };
1972         if (this.parent().type == 'treeview') {
1973             cfg.cls = 'treeview-menu';
1974         }
1975         
1976         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1977         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1978         return cfg;
1979     },
1980     
1981     initEvents: function() {
1982         
1983         //this.el.select('a').on('click', this.onClick, this);
1984         
1985     },
1986     onClick : function(e)
1987     {
1988         Roo.log('item on click ');
1989         //if(this.preventDefault){
1990         //    e.preventDefault();
1991         //}
1992         //this.parent().hideMenuItems();
1993         
1994         this.fireEvent('click', this, e);
1995     },
1996     getEl : function()
1997     {
1998         return this.el;
1999     }
2000 });
2001
2002  
2003
2004  /*
2005  * - LGPL
2006  *
2007  * menu separator
2008  * 
2009  */
2010
2011
2012 /**
2013  * @class Roo.bootstrap.MenuSeparator
2014  * @extends Roo.bootstrap.Component
2015  * Bootstrap MenuSeparator class
2016  * 
2017  * @constructor
2018  * Create a new MenuItem
2019  * @param {Object} config The config object
2020  */
2021
2022
2023 Roo.bootstrap.MenuSeparator = function(config){
2024     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2025 };
2026
2027 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2028     
2029     getAutoCreate : function(){
2030         var cfg = {
2031             cls: 'divider',
2032             tag : 'li'
2033         };
2034         
2035         return cfg;
2036     }
2037    
2038 });
2039
2040  
2041
2042  
2043 /*
2044 <div class="modal fade">
2045   <div class="modal-dialog">
2046     <div class="modal-content">
2047       <div class="modal-header">
2048         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2049         <h4 class="modal-title">Modal title</h4>
2050       </div>
2051       <div class="modal-body">
2052         <p>One fine body&hellip;</p>
2053       </div>
2054       <div class="modal-footer">
2055         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2056         <button type="button" class="btn btn-primary">Save changes</button>
2057       </div>
2058     </div><!-- /.modal-content -->
2059   </div><!-- /.modal-dialog -->
2060 </div><!-- /.modal -->
2061 */
2062 /*
2063  * - LGPL
2064  *
2065  * page contgainer.
2066  * 
2067  */
2068
2069 /**
2070  * @class Roo.bootstrap.Modal
2071  * @extends Roo.bootstrap.Component
2072  * Bootstrap Modal class
2073  * @cfg {String} title Title of dialog
2074  * @cfg {Boolean} specificTitle default false
2075  * @cfg {Array} buttons Array of buttons or standard button set..
2076  * @cfg {String} buttonPosition (left|right|center) default right
2077  * @cfg {Boolean} animate default true
2078  * @cfg {Boolean} allow_close default true
2079  * 
2080  * @constructor
2081  * Create a new Modal Dialog
2082  * @param {Object} config The config object
2083  */
2084
2085 Roo.bootstrap.Modal = function(config){
2086     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2087     this.addEvents({
2088         // raw events
2089         /**
2090          * @event btnclick
2091          * The raw btnclick event for the button
2092          * @param {Roo.EventObject} e
2093          */
2094         "btnclick" : true
2095     });
2096     this.buttons = this.buttons || [];
2097 };
2098
2099 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2100     
2101     title : 'test dialog',
2102    
2103     buttons : false,
2104     
2105     // set on load...
2106     body:  false,
2107     
2108     specificTitle: false,
2109     
2110     buttonPosition: 'right',
2111     
2112     allow_close : true,
2113     
2114     animate : true,
2115     
2116     onRender : function(ct, position)
2117     {
2118         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2119      
2120         if(!this.el){
2121             var cfg = Roo.apply({},  this.getAutoCreate());
2122             cfg.id = Roo.id();
2123             //if(!cfg.name){
2124             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2125             //}
2126             //if (!cfg.name.length) {
2127             //    delete cfg.name;
2128            // }
2129             if (this.cls) {
2130                 cfg.cls += ' ' + this.cls;
2131             }
2132             if (this.style) {
2133                 cfg.style = this.style;
2134             }
2135             this.el = Roo.get(document.body).createChild(cfg, position);
2136         }
2137         //var type = this.el.dom.type;
2138         
2139         if(this.tabIndex !== undefined){
2140             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2141         }
2142         
2143         
2144         
2145         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2146         this.maskEl.enableDisplayMode("block");
2147         this.maskEl.hide();
2148         //this.el.addClass("x-dlg-modal");
2149     
2150         if (this.buttons.length) {
2151             Roo.each(this.buttons, function(bb) {
2152                 b = Roo.apply({}, bb);
2153                 b.xns = b.xns || Roo.bootstrap;
2154                 b.xtype = b.xtype || 'Button';
2155                 if (typeof(b.listeners) == 'undefined') {
2156                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2157                 }
2158                 
2159                 var btn = Roo.factory(b);
2160                 
2161                 btn.onRender(this.el.select('.modal-footer div').first());
2162                 
2163             },this);
2164         }
2165         // render the children.
2166         var nitems = [];
2167         
2168         if(typeof(this.items) != 'undefined'){
2169             var items = this.items;
2170             delete this.items;
2171
2172             for(var i =0;i < items.length;i++) {
2173                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2174             }
2175         }
2176         
2177         this.items = nitems;
2178         
2179         this.body = this.el.select('.modal-body',true).first();
2180         this.close = this.el.select('.modal-header .close', true).first();
2181         this.footer = this.el.select('.modal-footer',true).first();
2182         this.initEvents();
2183         //this.el.addClass([this.fieldClass, this.cls]);
2184         
2185     },
2186     getAutoCreate : function(){
2187         
2188         
2189         var bdy = {
2190                 cls : 'modal-body',
2191                 html : this.html || ''
2192         };
2193         
2194         var title = {
2195             tag: 'h4',
2196             cls : 'modal-title',
2197             html : this.title
2198         };
2199         
2200         if(this.specificTitle){
2201             title = this.title;
2202             
2203         };
2204         
2205         var header = [];
2206         if (this.allow_close) {
2207             header.push({
2208                 tag: 'button',
2209                 cls : 'close',
2210                 html : '&times'
2211             });
2212         }
2213         header.push(title);
2214         
2215         var modal = {
2216             cls: "modal",
2217             style : 'display: none',
2218             cn : [
2219                 {
2220                     cls: "modal-dialog",
2221                     cn : [
2222                         {
2223                             cls : "modal-content",
2224                             cn : [
2225                                 {
2226                                     cls : 'modal-header',
2227                                     cn : header
2228                                 },
2229                                 bdy,
2230                                 {
2231                                     cls : 'modal-footer',
2232                                     cn : [
2233                                         {
2234                                             tag: 'div',
2235                                             cls: 'btn-' + this.buttonPosition
2236                                         }
2237                                     ]
2238                                     
2239                                 }
2240                                 
2241                                 
2242                             ]
2243                             
2244                         }
2245                     ]
2246                         
2247                 }
2248             ]
2249         };
2250         
2251         if(this.animate){
2252             modal.cls += ' fade';
2253         }
2254         
2255         return modal;
2256           
2257     },
2258     getChildContainer : function() {
2259          
2260          return this.el.select('.modal-body',true).first();
2261         
2262     },
2263     getButtonContainer : function() {
2264          return this.el.select('.modal-footer div',true).first();
2265         
2266     },
2267     initEvents : function()
2268     {
2269         this.el.select('.modal-header .close').on('click', this.hide, this);
2270 //        
2271 //        this.addxtype(this);
2272     },
2273     show : function() {
2274         
2275         if (!this.rendered) {
2276             this.render();
2277         }
2278         
2279         this.el.setStyle('display', 'block');
2280         
2281         if(this.animate){
2282             var _this = this;
2283             (function(){ _this.el.addClass('in'); }).defer(50);
2284         }else{
2285             this.el.addClass('in');
2286         }
2287         
2288         Roo.get(document.body).addClass("x-body-masked");
2289         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2290         this.maskEl.show();
2291         this.el.setStyle('zIndex', '10001');
2292        
2293         this.fireEvent('show', this);
2294         
2295         
2296     },
2297     hide : function()
2298     {
2299         this.maskEl.hide();
2300         Roo.get(document.body).removeClass("x-body-masked");
2301         this.el.removeClass('in');
2302         
2303         if(this.animate){
2304             var _this = this;
2305             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2306         }else{
2307             this.el.setStyle('display', 'none');
2308         }
2309         
2310         this.fireEvent('hide', this);
2311     },
2312     
2313     addButton : function(str, cb)
2314     {
2315          
2316         
2317         var b = Roo.apply({}, { html : str } );
2318         b.xns = b.xns || Roo.bootstrap;
2319         b.xtype = b.xtype || 'Button';
2320         if (typeof(b.listeners) == 'undefined') {
2321             b.listeners = { click : cb.createDelegate(this)  };
2322         }
2323         
2324         var btn = Roo.factory(b);
2325            
2326         btn.onRender(this.el.select('.modal-footer div').first());
2327         
2328         return btn;   
2329        
2330     },
2331     
2332     setDefaultButton : function(btn)
2333     {
2334         //this.el.select('.modal-footer').()
2335     },
2336     resizeTo: function(w,h)
2337     {
2338         // skip..
2339     },
2340     setContentSize  : function(w, h)
2341     {
2342         
2343     },
2344     onButtonClick: function(btn,e)
2345     {
2346         //Roo.log([a,b,c]);
2347         this.fireEvent('btnclick', btn.name, e);
2348     },
2349     setTitle: function(str) {
2350         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2351         
2352     }
2353 });
2354
2355
2356 Roo.apply(Roo.bootstrap.Modal,  {
2357     /**
2358          * Button config that displays a single OK button
2359          * @type Object
2360          */
2361         OK :  [{
2362             name : 'ok',
2363             weight : 'primary',
2364             html : 'OK'
2365         }], 
2366         /**
2367          * Button config that displays Yes and No buttons
2368          * @type Object
2369          */
2370         YESNO : [
2371             {
2372                 name  : 'no',
2373                 html : 'No'
2374             },
2375             {
2376                 name  :'yes',
2377                 weight : 'primary',
2378                 html : 'Yes'
2379             }
2380         ],
2381         
2382         /**
2383          * Button config that displays OK and Cancel buttons
2384          * @type Object
2385          */
2386         OKCANCEL : [
2387             {
2388                name : 'cancel',
2389                 html : 'Cancel'
2390             },
2391             {
2392                 name : 'ok',
2393                 weight : 'primary',
2394                 html : 'OK'
2395             }
2396         ],
2397         /**
2398          * Button config that displays Yes, No and Cancel buttons
2399          * @type Object
2400          */
2401         YESNOCANCEL : [
2402             {
2403                 name : 'yes',
2404                 weight : 'primary',
2405                 html : 'Yes'
2406             },
2407             {
2408                 name : 'no',
2409                 html : 'No'
2410             },
2411             {
2412                 name : 'cancel',
2413                 html : 'Cancel'
2414             }
2415         ]
2416 });
2417  /*
2418  * - LGPL
2419  *
2420  * messagebox - can be used as a replace
2421  * 
2422  */
2423 /**
2424  * @class Roo.MessageBox
2425  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2426  * Example usage:
2427  *<pre><code>
2428 // Basic alert:
2429 Roo.Msg.alert('Status', 'Changes saved successfully.');
2430
2431 // Prompt for user data:
2432 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2433     if (btn == 'ok'){
2434         // process text value...
2435     }
2436 });
2437
2438 // Show a dialog using config options:
2439 Roo.Msg.show({
2440    title:'Save Changes?',
2441    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2442    buttons: Roo.Msg.YESNOCANCEL,
2443    fn: processResult,
2444    animEl: 'elId'
2445 });
2446 </code></pre>
2447  * @singleton
2448  */
2449 Roo.bootstrap.MessageBox = function(){
2450     var dlg, opt, mask, waitTimer;
2451     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2452     var buttons, activeTextEl, bwidth;
2453
2454     
2455     // private
2456     var handleButton = function(button){
2457         dlg.hide();
2458         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2459     };
2460
2461     // private
2462     var handleHide = function(){
2463         if(opt && opt.cls){
2464             dlg.el.removeClass(opt.cls);
2465         }
2466         //if(waitTimer){
2467         //    Roo.TaskMgr.stop(waitTimer);
2468         //    waitTimer = null;
2469         //}
2470     };
2471
2472     // private
2473     var updateButtons = function(b){
2474         var width = 0;
2475         if(!b){
2476             buttons["ok"].hide();
2477             buttons["cancel"].hide();
2478             buttons["yes"].hide();
2479             buttons["no"].hide();
2480             //dlg.footer.dom.style.display = 'none';
2481             return width;
2482         }
2483         dlg.footer.dom.style.display = '';
2484         for(var k in buttons){
2485             if(typeof buttons[k] != "function"){
2486                 if(b[k]){
2487                     buttons[k].show();
2488                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2489                     width += buttons[k].el.getWidth()+15;
2490                 }else{
2491                     buttons[k].hide();
2492                 }
2493             }
2494         }
2495         return width;
2496     };
2497
2498     // private
2499     var handleEsc = function(d, k, e){
2500         if(opt && opt.closable !== false){
2501             dlg.hide();
2502         }
2503         if(e){
2504             e.stopEvent();
2505         }
2506     };
2507
2508     return {
2509         /**
2510          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2511          * @return {Roo.BasicDialog} The BasicDialog element
2512          */
2513         getDialog : function(){
2514            if(!dlg){
2515                 dlg = new Roo.bootstrap.Modal( {
2516                     //draggable: true,
2517                     //resizable:false,
2518                     //constraintoviewport:false,
2519                     //fixedcenter:true,
2520                     //collapsible : false,
2521                     //shim:true,
2522                     //modal: true,
2523                   //  width:400,
2524                   //  height:100,
2525                     //buttonAlign:"center",
2526                     closeClick : function(){
2527                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2528                             handleButton("no");
2529                         }else{
2530                             handleButton("cancel");
2531                         }
2532                     }
2533                 });
2534                 dlg.render();
2535                 dlg.on("hide", handleHide);
2536                 mask = dlg.mask;
2537                 //dlg.addKeyListener(27, handleEsc);
2538                 buttons = {};
2539                 this.buttons = buttons;
2540                 var bt = this.buttonText;
2541                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2542                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2543                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2544                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2545                 Roo.log(buttons)
2546                 bodyEl = dlg.body.createChild({
2547
2548                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2549                         '<textarea class="roo-mb-textarea"></textarea>' +
2550                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2551                 });
2552                 msgEl = bodyEl.dom.firstChild;
2553                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2554                 textboxEl.enableDisplayMode();
2555                 textboxEl.addKeyListener([10,13], function(){
2556                     if(dlg.isVisible() && opt && opt.buttons){
2557                         if(opt.buttons.ok){
2558                             handleButton("ok");
2559                         }else if(opt.buttons.yes){
2560                             handleButton("yes");
2561                         }
2562                     }
2563                 });
2564                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2565                 textareaEl.enableDisplayMode();
2566                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2567                 progressEl.enableDisplayMode();
2568                 var pf = progressEl.dom.firstChild;
2569                 if (pf) {
2570                     pp = Roo.get(pf.firstChild);
2571                     pp.setHeight(pf.offsetHeight);
2572                 }
2573                 
2574             }
2575             return dlg;
2576         },
2577
2578         /**
2579          * Updates the message box body text
2580          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2581          * the XHTML-compliant non-breaking space character '&amp;#160;')
2582          * @return {Roo.MessageBox} This message box
2583          */
2584         updateText : function(text){
2585             if(!dlg.isVisible() && !opt.width){
2586                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2587             }
2588             msgEl.innerHTML = text || '&#160;';
2589       
2590             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2591             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2592             var w = Math.max(
2593                     Math.min(opt.width || cw , this.maxWidth), 
2594                     Math.max(opt.minWidth || this.minWidth, bwidth)
2595             );
2596             if(opt.prompt){
2597                 activeTextEl.setWidth(w);
2598             }
2599             if(dlg.isVisible()){
2600                 dlg.fixedcenter = false;
2601             }
2602             // to big, make it scroll. = But as usual stupid IE does not support
2603             // !important..
2604             
2605             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2606                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2607                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2608             } else {
2609                 bodyEl.dom.style.height = '';
2610                 bodyEl.dom.style.overflowY = '';
2611             }
2612             if (cw > w) {
2613                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2614             } else {
2615                 bodyEl.dom.style.overflowX = '';
2616             }
2617             
2618             dlg.setContentSize(w, bodyEl.getHeight());
2619             if(dlg.isVisible()){
2620                 dlg.fixedcenter = true;
2621             }
2622             return this;
2623         },
2624
2625         /**
2626          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2627          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2628          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2629          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2630          * @return {Roo.MessageBox} This message box
2631          */
2632         updateProgress : function(value, text){
2633             if(text){
2634                 this.updateText(text);
2635             }
2636             if (pp) { // weird bug on my firefox - for some reason this is not defined
2637                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2638             }
2639             return this;
2640         },        
2641
2642         /**
2643          * Returns true if the message box is currently displayed
2644          * @return {Boolean} True if the message box is visible, else false
2645          */
2646         isVisible : function(){
2647             return dlg && dlg.isVisible();  
2648         },
2649
2650         /**
2651          * Hides the message box if it is displayed
2652          */
2653         hide : function(){
2654             if(this.isVisible()){
2655                 dlg.hide();
2656             }  
2657         },
2658
2659         /**
2660          * Displays a new message box, or reinitializes an existing message box, based on the config options
2661          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2662          * The following config object properties are supported:
2663          * <pre>
2664 Property    Type             Description
2665 ----------  ---------------  ------------------------------------------------------------------------------------
2666 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2667                                    closes (defaults to undefined)
2668 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2669                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2670 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2671                                    progress and wait dialogs will ignore this property and always hide the
2672                                    close button as they can only be closed programmatically.
2673 cls               String           A custom CSS class to apply to the message box element
2674 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2675                                    displayed (defaults to 75)
2676 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2677                                    function will be btn (the name of the button that was clicked, if applicable,
2678                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2679                                    Progress and wait dialogs will ignore this option since they do not respond to
2680                                    user actions and can only be closed programmatically, so any required function
2681                                    should be called by the same code after it closes the dialog.
2682 icon              String           A CSS class that provides a background image to be used as an icon for
2683                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2684 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2685 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2686 modal             Boolean          False to allow user interaction with the page while the message box is
2687                                    displayed (defaults to true)
2688 msg               String           A string that will replace the existing message box body text (defaults
2689                                    to the XHTML-compliant non-breaking space character '&#160;')
2690 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2691 progress          Boolean          True to display a progress bar (defaults to false)
2692 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2693 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2694 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2695 title             String           The title text
2696 value             String           The string value to set into the active textbox element if displayed
2697 wait              Boolean          True to display a progress bar (defaults to false)
2698 width             Number           The width of the dialog in pixels
2699 </pre>
2700          *
2701          * Example usage:
2702          * <pre><code>
2703 Roo.Msg.show({
2704    title: 'Address',
2705    msg: 'Please enter your address:',
2706    width: 300,
2707    buttons: Roo.MessageBox.OKCANCEL,
2708    multiline: true,
2709    fn: saveAddress,
2710    animEl: 'addAddressBtn'
2711 });
2712 </code></pre>
2713          * @param {Object} config Configuration options
2714          * @return {Roo.MessageBox} This message box
2715          */
2716         show : function(options)
2717         {
2718             
2719             // this causes nightmares if you show one dialog after another
2720             // especially on callbacks..
2721              
2722             if(this.isVisible()){
2723                 
2724                 this.hide();
2725                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2726                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2727                 Roo.log("New Dialog Message:" +  options.msg )
2728                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2729                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2730                 
2731             }
2732             var d = this.getDialog();
2733             opt = options;
2734             d.setTitle(opt.title || "&#160;");
2735             d.close.setDisplayed(opt.closable !== false);
2736             activeTextEl = textboxEl;
2737             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2738             if(opt.prompt){
2739                 if(opt.multiline){
2740                     textboxEl.hide();
2741                     textareaEl.show();
2742                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2743                         opt.multiline : this.defaultTextHeight);
2744                     activeTextEl = textareaEl;
2745                 }else{
2746                     textboxEl.show();
2747                     textareaEl.hide();
2748                 }
2749             }else{
2750                 textboxEl.hide();
2751                 textareaEl.hide();
2752             }
2753             progressEl.setDisplayed(opt.progress === true);
2754             this.updateProgress(0);
2755             activeTextEl.dom.value = opt.value || "";
2756             if(opt.prompt){
2757                 dlg.setDefaultButton(activeTextEl);
2758             }else{
2759                 var bs = opt.buttons;
2760                 var db = null;
2761                 if(bs && bs.ok){
2762                     db = buttons["ok"];
2763                 }else if(bs && bs.yes){
2764                     db = buttons["yes"];
2765                 }
2766                 dlg.setDefaultButton(db);
2767             }
2768             bwidth = updateButtons(opt.buttons);
2769             this.updateText(opt.msg);
2770             if(opt.cls){
2771                 d.el.addClass(opt.cls);
2772             }
2773             d.proxyDrag = opt.proxyDrag === true;
2774             d.modal = opt.modal !== false;
2775             d.mask = opt.modal !== false ? mask : false;
2776             if(!d.isVisible()){
2777                 // force it to the end of the z-index stack so it gets a cursor in FF
2778                 document.body.appendChild(dlg.el.dom);
2779                 d.animateTarget = null;
2780                 d.show(options.animEl);
2781             }
2782             return this;
2783         },
2784
2785         /**
2786          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2787          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2788          * and closing the message box when the process is complete.
2789          * @param {String} title The title bar text
2790          * @param {String} msg The message box body text
2791          * @return {Roo.MessageBox} This message box
2792          */
2793         progress : function(title, msg){
2794             this.show({
2795                 title : title,
2796                 msg : msg,
2797                 buttons: false,
2798                 progress:true,
2799                 closable:false,
2800                 minWidth: this.minProgressWidth,
2801                 modal : true
2802             });
2803             return this;
2804         },
2805
2806         /**
2807          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2808          * If a callback function is passed it will be called after the user clicks the button, and the
2809          * id of the button that was clicked will be passed as the only parameter to the callback
2810          * (could also be the top-right close button).
2811          * @param {String} title The title bar text
2812          * @param {String} msg The message box body text
2813          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2814          * @param {Object} scope (optional) The scope of the callback function
2815          * @return {Roo.MessageBox} This message box
2816          */
2817         alert : function(title, msg, fn, scope){
2818             this.show({
2819                 title : title,
2820                 msg : msg,
2821                 buttons: this.OK,
2822                 fn: fn,
2823                 scope : scope,
2824                 modal : true
2825             });
2826             return this;
2827         },
2828
2829         /**
2830          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2831          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2832          * You are responsible for closing the message box when the process is complete.
2833          * @param {String} msg The message box body text
2834          * @param {String} title (optional) The title bar text
2835          * @return {Roo.MessageBox} This message box
2836          */
2837         wait : function(msg, title){
2838             this.show({
2839                 title : title,
2840                 msg : msg,
2841                 buttons: false,
2842                 closable:false,
2843                 progress:true,
2844                 modal:true,
2845                 width:300,
2846                 wait:true
2847             });
2848             waitTimer = Roo.TaskMgr.start({
2849                 run: function(i){
2850                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2851                 },
2852                 interval: 1000
2853             });
2854             return this;
2855         },
2856
2857         /**
2858          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2859          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2860          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2861          * @param {String} title The title bar text
2862          * @param {String} msg The message box body text
2863          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2864          * @param {Object} scope (optional) The scope of the callback function
2865          * @return {Roo.MessageBox} This message box
2866          */
2867         confirm : function(title, msg, fn, scope){
2868             this.show({
2869                 title : title,
2870                 msg : msg,
2871                 buttons: this.YESNO,
2872                 fn: fn,
2873                 scope : scope,
2874                 modal : true
2875             });
2876             return this;
2877         },
2878
2879         /**
2880          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2881          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2882          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2883          * (could also be the top-right close button) and the text that was entered will be passed as the two
2884          * parameters to the callback.
2885          * @param {String} title The title bar text
2886          * @param {String} msg The message box body text
2887          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2888          * @param {Object} scope (optional) The scope of the callback function
2889          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2890          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2891          * @return {Roo.MessageBox} This message box
2892          */
2893         prompt : function(title, msg, fn, scope, multiline){
2894             this.show({
2895                 title : title,
2896                 msg : msg,
2897                 buttons: this.OKCANCEL,
2898                 fn: fn,
2899                 minWidth:250,
2900                 scope : scope,
2901                 prompt:true,
2902                 multiline: multiline,
2903                 modal : true
2904             });
2905             return this;
2906         },
2907
2908         /**
2909          * Button config that displays a single OK button
2910          * @type Object
2911          */
2912         OK : {ok:true},
2913         /**
2914          * Button config that displays Yes and No buttons
2915          * @type Object
2916          */
2917         YESNO : {yes:true, no:true},
2918         /**
2919          * Button config that displays OK and Cancel buttons
2920          * @type Object
2921          */
2922         OKCANCEL : {ok:true, cancel:true},
2923         /**
2924          * Button config that displays Yes, No and Cancel buttons
2925          * @type Object
2926          */
2927         YESNOCANCEL : {yes:true, no:true, cancel:true},
2928
2929         /**
2930          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2931          * @type Number
2932          */
2933         defaultTextHeight : 75,
2934         /**
2935          * The maximum width in pixels of the message box (defaults to 600)
2936          * @type Number
2937          */
2938         maxWidth : 600,
2939         /**
2940          * The minimum width in pixels of the message box (defaults to 100)
2941          * @type Number
2942          */
2943         minWidth : 100,
2944         /**
2945          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2946          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2947          * @type Number
2948          */
2949         minProgressWidth : 250,
2950         /**
2951          * An object containing the default button text strings that can be overriden for localized language support.
2952          * Supported properties are: ok, cancel, yes and no.
2953          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2954          * @type Object
2955          */
2956         buttonText : {
2957             ok : "OK",
2958             cancel : "Cancel",
2959             yes : "Yes",
2960             no : "No"
2961         }
2962     };
2963 }();
2964
2965 /**
2966  * Shorthand for {@link Roo.MessageBox}
2967  */
2968 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2969 Roo.Msg = Roo.Msg || Roo.MessageBox;
2970 /*
2971  * - LGPL
2972  *
2973  * navbar
2974  * 
2975  */
2976
2977 /**
2978  * @class Roo.bootstrap.Navbar
2979  * @extends Roo.bootstrap.Component
2980  * Bootstrap Navbar class
2981
2982  * @constructor
2983  * Create a new Navbar
2984  * @param {Object} config The config object
2985  */
2986
2987
2988 Roo.bootstrap.Navbar = function(config){
2989     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2990     
2991 };
2992
2993 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2994     
2995     
2996    
2997     // private
2998     navItems : false,
2999     loadMask : false,
3000     
3001     
3002     getAutoCreate : function(){
3003         
3004         
3005         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3006         
3007     },
3008     
3009     initEvents :function ()
3010     {
3011         //Roo.log(this.el.select('.navbar-toggle',true));
3012         this.el.select('.navbar-toggle',true).on('click', function() {
3013            // Roo.log('click');
3014             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3015         }, this);
3016         
3017         var mark = {
3018             tag: "div",
3019             cls:"x-dlg-mask"
3020         }
3021         
3022         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3023         
3024         var size = this.el.getSize();
3025         this.maskEl.setSize(size.width, size.height);
3026         this.maskEl.enableDisplayMode("block");
3027         this.maskEl.hide();
3028         
3029         if(this.loadMask){
3030             this.maskEl.show();
3031         }
3032     },
3033     
3034     
3035     getChildContainer : function()
3036     {
3037         if (this.el.select('.collapse').getCount()) {
3038             return this.el.select('.collapse',true).first();
3039         }
3040         
3041         return this.el;
3042     },
3043     
3044     mask : function()
3045     {
3046         this.maskEl.show();
3047     },
3048     
3049     unmask : function()
3050     {
3051         this.maskEl.hide();
3052     } 
3053     
3054     
3055     
3056     
3057 });
3058
3059
3060
3061  
3062
3063  /*
3064  * - LGPL
3065  *
3066  * navbar
3067  * 
3068  */
3069
3070 /**
3071  * @class Roo.bootstrap.NavSimplebar
3072  * @extends Roo.bootstrap.Navbar
3073  * Bootstrap Sidebar class
3074  *
3075  * @cfg {Boolean} inverse is inverted color
3076  * 
3077  * @cfg {String} type (nav | pills | tabs)
3078  * @cfg {Boolean} arrangement stacked | justified
3079  * @cfg {String} align (left | right) alignment
3080  * 
3081  * @cfg {Boolean} main (true|false) main nav bar? default false
3082  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3083  * 
3084  * @cfg {String} tag (header|footer|nav|div) default is nav 
3085
3086  * 
3087  * 
3088  * 
3089  * @constructor
3090  * Create a new Sidebar
3091  * @param {Object} config The config object
3092  */
3093
3094
3095 Roo.bootstrap.NavSimplebar = function(config){
3096     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3097 };
3098
3099 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3100     
3101     inverse: false,
3102     
3103     type: false,
3104     arrangement: '',
3105     align : false,
3106     
3107     
3108     
3109     main : false,
3110     
3111     
3112     tag : false,
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         var cfg = {
3119             tag : this.tag || 'div',
3120             cls : 'navbar'
3121         };
3122           
3123         
3124         cfg.cn = [
3125             {
3126                 cls: 'nav',
3127                 tag : 'ul'
3128             }
3129         ];
3130         
3131          
3132         this.type = this.type || 'nav';
3133         if (['tabs','pills'].indexOf(this.type)!==-1) {
3134             cfg.cn[0].cls += ' nav-' + this.type
3135         
3136         
3137         } else {
3138             if (this.type!=='nav') {
3139                 Roo.log('nav type must be nav/tabs/pills')
3140             }
3141             cfg.cn[0].cls += ' navbar-nav'
3142         }
3143         
3144         
3145         
3146         
3147         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3148             cfg.cn[0].cls += ' nav-' + this.arrangement;
3149         }
3150         
3151         
3152         if (this.align === 'right') {
3153             cfg.cn[0].cls += ' navbar-right';
3154         }
3155         
3156         if (this.inverse) {
3157             cfg.cls += ' navbar-inverse';
3158             
3159         }
3160         
3161         
3162         return cfg;
3163     
3164         
3165     }
3166     
3167     
3168     
3169 });
3170
3171
3172
3173  
3174
3175  
3176        /*
3177  * - LGPL
3178  *
3179  * navbar
3180  * 
3181  */
3182
3183 /**
3184  * @class Roo.bootstrap.NavHeaderbar
3185  * @extends Roo.bootstrap.NavSimplebar
3186  * Bootstrap Sidebar class
3187  *
3188  * @cfg {String} brand what is brand
3189  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3190  * @cfg {String} brand_href href of the brand
3191  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3192  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3193  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3194  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3195  * 
3196  * @constructor
3197  * Create a new Sidebar
3198  * @param {Object} config The config object
3199  */
3200
3201
3202 Roo.bootstrap.NavHeaderbar = function(config){
3203     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3204       
3205 };
3206
3207 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3208     
3209     position: '',
3210     brand: '',
3211     brand_href: false,
3212     srButton : true,
3213     autohide : false,
3214     desktopCenter : false,
3215    
3216     
3217     getAutoCreate : function(){
3218         
3219         var   cfg = {
3220             tag: this.nav || 'nav',
3221             cls: 'navbar',
3222             role: 'navigation',
3223             cn: []
3224         };
3225         
3226         var cn = cfg.cn;
3227         if (this.desktopCenter) {
3228             cn.push({cls : 'container', cn : []});
3229             cn = cn[0].cn;
3230         }
3231         
3232         if(this.srButton){
3233             cn.push({
3234                 tag: 'div',
3235                 cls: 'navbar-header',
3236                 cn: [
3237                     {
3238                         tag: 'button',
3239                         type: 'button',
3240                         cls: 'navbar-toggle',
3241                         'data-toggle': 'collapse',
3242                         cn: [
3243                             {
3244                                 tag: 'span',
3245                                 cls: 'sr-only',
3246                                 html: 'Toggle navigation'
3247                             },
3248                             {
3249                                 tag: 'span',
3250                                 cls: 'icon-bar'
3251                             },
3252                             {
3253                                 tag: 'span',
3254                                 cls: 'icon-bar'
3255                             },
3256                             {
3257                                 tag: 'span',
3258                                 cls: 'icon-bar'
3259                             }
3260                         ]
3261                     }
3262                 ]
3263             });
3264         }
3265         
3266         cn.push({
3267             tag: 'div',
3268             cls: 'collapse navbar-collapse',
3269             cn : []
3270         });
3271         
3272         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3273         
3274         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3275             cfg.cls += ' navbar-' + this.position;
3276             
3277             // tag can override this..
3278             
3279             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3280         }
3281         
3282         if (this.brand !== '') {
3283             cn[0].cn.push({
3284                 tag: 'a',
3285                 href: this.brand_href ? this.brand_href : '#',
3286                 cls: 'navbar-brand',
3287                 cn: [
3288                 this.brand
3289                 ]
3290             });
3291         }
3292         
3293         if(this.main){
3294             cfg.cls += ' main-nav';
3295         }
3296         
3297         
3298         return cfg;
3299
3300         
3301     },
3302     getHeaderChildContainer : function()
3303     {
3304         if (this.el.select('.navbar-header').getCount()) {
3305             return this.el.select('.navbar-header',true).first();
3306         }
3307         
3308         return this.getChildContainer();
3309     },
3310     
3311     
3312     initEvents : function()
3313     {
3314         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3315         
3316         if (this.autohide) {
3317             
3318             var prevScroll = 0;
3319             var ft = this.el;
3320             
3321             Roo.get(document).on('scroll',function(e) {
3322                 var ns = Roo.get(document).getScroll().top;
3323                 var os = prevScroll;
3324                 prevScroll = ns;
3325                 
3326                 if(ns > os){
3327                     ft.removeClass('slideDown');
3328                     ft.addClass('slideUp');
3329                     return;
3330                 }
3331                 ft.removeClass('slideUp');
3332                 ft.addClass('slideDown');
3333                  
3334               
3335           },this);
3336         }
3337     }    
3338           
3339       
3340     
3341     
3342 });
3343
3344
3345
3346  
3347
3348  /*
3349  * - LGPL
3350  *
3351  * navbar
3352  * 
3353  */
3354
3355 /**
3356  * @class Roo.bootstrap.NavSidebar
3357  * @extends Roo.bootstrap.Navbar
3358  * Bootstrap Sidebar class
3359  * 
3360  * @constructor
3361  * Create a new Sidebar
3362  * @param {Object} config The config object
3363  */
3364
3365
3366 Roo.bootstrap.NavSidebar = function(config){
3367     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3368 };
3369
3370 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3371     
3372     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3373     
3374     getAutoCreate : function(){
3375         
3376         
3377         return  {
3378             tag: 'div',
3379             cls: 'sidebar sidebar-nav'
3380         };
3381     
3382         
3383     }
3384     
3385     
3386     
3387 });
3388
3389
3390
3391  
3392
3393  /*
3394  * - LGPL
3395  *
3396  * nav group
3397  * 
3398  */
3399
3400 /**
3401  * @class Roo.bootstrap.NavGroup
3402  * @extends Roo.bootstrap.Component
3403  * Bootstrap NavGroup class
3404  * @cfg {String} align left | right
3405  * @cfg {Boolean} inverse false | true
3406  * @cfg {String} type (nav|pills|tab) default nav
3407  * @cfg {String} navId - reference Id for navbar.
3408
3409  * 
3410  * @constructor
3411  * Create a new nav group
3412  * @param {Object} config The config object
3413  */
3414
3415 Roo.bootstrap.NavGroup = function(config){
3416     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3417     this.navItems = [];
3418    
3419     Roo.bootstrap.NavGroup.register(this);
3420      this.addEvents({
3421         /**
3422              * @event changed
3423              * Fires when the active item changes
3424              * @param {Roo.bootstrap.NavGroup} this
3425              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3426              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3427          */
3428         'changed': true
3429      });
3430     
3431 };
3432
3433 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3434     
3435     align: '',
3436     inverse: false,
3437     form: false,
3438     type: 'nav',
3439     navId : '',
3440     // private
3441     
3442     navItems : false, 
3443     
3444     getAutoCreate : function()
3445     {
3446         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3447         
3448         cfg = {
3449             tag : 'ul',
3450             cls: 'nav' 
3451         }
3452         
3453         if (['tabs','pills'].indexOf(this.type)!==-1) {
3454             cfg.cls += ' nav-' + this.type
3455         } else {
3456             if (this.type!=='nav') {
3457                 Roo.log('nav type must be nav/tabs/pills')
3458             }
3459             cfg.cls += ' navbar-nav'
3460         }
3461         
3462         if (this.parent().sidebar) {
3463             cfg = {
3464                 tag: 'ul',
3465                 cls: 'dashboard-menu sidebar-menu'
3466             }
3467             
3468             return cfg;
3469         }
3470         
3471         if (this.form === true) {
3472             cfg = {
3473                 tag: 'form',
3474                 cls: 'navbar-form'
3475             }
3476             
3477             if (this.align === 'right') {
3478                 cfg.cls += ' navbar-right';
3479             } else {
3480                 cfg.cls += ' navbar-left';
3481             }
3482         }
3483         
3484         if (this.align === 'right') {
3485             cfg.cls += ' navbar-right';
3486         }
3487         
3488         if (this.inverse) {
3489             cfg.cls += ' navbar-inverse';
3490             
3491         }
3492         
3493         
3494         return cfg;
3495     },
3496     /**
3497     * sets the active Navigation item
3498     * @param {Roo.bootstrap.NavItem} the new current navitem
3499     */
3500     setActiveItem : function(item)
3501     {
3502         var prev = false;
3503         Roo.each(this.navItems, function(v){
3504             if (v == item) {
3505                 return ;
3506             }
3507             if (v.isActive()) {
3508                 v.setActive(false, true);
3509                 prev = v;
3510                 
3511             }
3512             
3513         });
3514
3515         item.setActive(true, true);
3516         this.fireEvent('changed', this, item, prev);
3517         
3518         
3519     },
3520     /**
3521     * gets the active Navigation item
3522     * @return {Roo.bootstrap.NavItem} the current navitem
3523     */
3524     getActive : function()
3525     {
3526         
3527         var prev = false;
3528         Roo.each(this.navItems, function(v){
3529             
3530             if (v.isActive()) {
3531                 prev = v;
3532                 
3533             }
3534             
3535         });
3536         return prev;
3537     },
3538     
3539     indexOfNav : function()
3540     {
3541         
3542         var prev = false;
3543         Roo.each(this.navItems, function(v,i){
3544             
3545             if (v.isActive()) {
3546                 prev = i;
3547                 
3548             }
3549             
3550         });
3551         return prev;
3552     },
3553     /**
3554     * adds a Navigation item
3555     * @param {Roo.bootstrap.NavItem} the navitem to add
3556     */
3557     addItem : function(cfg)
3558     {
3559         var cn = new Roo.bootstrap.NavItem(cfg);
3560         this.register(cn);
3561         cn.parentId = this.id;
3562         cn.onRender(this.el, null);
3563         return cn;
3564     },
3565     /**
3566     * register a Navigation item
3567     * @param {Roo.bootstrap.NavItem} the navitem to add
3568     */
3569     register : function(item)
3570     {
3571         this.navItems.push( item);
3572         item.navId = this.navId;
3573     
3574     },
3575     
3576     /**
3577     * clear all the Navigation item
3578     */
3579    
3580     clearAll : function()
3581     {
3582         this.navItems = [];
3583         this.el.dom.innerHTML = '';
3584     },
3585     
3586     getNavItem: function(tabId)
3587     {
3588         var ret = false;
3589         Roo.each(this.navItems, function(e) {
3590             if (e.tabId == tabId) {
3591                ret =  e;
3592                return false;
3593             }
3594             return true;
3595             
3596         });
3597         return ret;
3598     },
3599     
3600     setActiveNext : function()
3601     {
3602         var i = this.indexOfNav(this.getActive());
3603         if (i > this.navItems.length) {
3604             return;
3605         }
3606         this.setActiveItem(this.navItems[i+1]);
3607     },
3608     setActivePrev : function()
3609     {
3610         var i = this.indexOfNav(this.getActive());
3611         if (i  < 1) {
3612             return;
3613         }
3614         this.setActiveItem(this.navItems[i-1]);
3615     },
3616     clearWasActive : function(except) {
3617         Roo.each(this.navItems, function(e) {
3618             if (e.tabId != except.tabId && e.was_active) {
3619                e.was_active = false;
3620                return false;
3621             }
3622             return true;
3623             
3624         });
3625     },
3626     getWasActive : function ()
3627     {
3628         var r = false;
3629         Roo.each(this.navItems, function(e) {
3630             if (e.was_active) {
3631                r = e;
3632                return false;
3633             }
3634             return true;
3635             
3636         });
3637         return r;
3638     }
3639     
3640     
3641 });
3642
3643  
3644 Roo.apply(Roo.bootstrap.NavGroup, {
3645     
3646     groups: {},
3647      /**
3648     * register a Navigation Group
3649     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3650     */
3651     register : function(navgrp)
3652     {
3653         this.groups[navgrp.navId] = navgrp;
3654         
3655     },
3656     /**
3657     * fetch a Navigation Group based on the navigation ID
3658     * @param {string} the navgroup to add
3659     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3660     */
3661     get: function(navId) {
3662         if (typeof(this.groups[navId]) == 'undefined') {
3663             return false;
3664             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3665         }
3666         return this.groups[navId] ;
3667     }
3668     
3669     
3670     
3671 });
3672
3673  /*
3674  * - LGPL
3675  *
3676  * row
3677  * 
3678  */
3679
3680 /**
3681  * @class Roo.bootstrap.NavItem
3682  * @extends Roo.bootstrap.Component
3683  * Bootstrap Navbar.NavItem class
3684  * @cfg {String} href  link to
3685  * @cfg {String} html content of button
3686  * @cfg {String} badge text inside badge
3687  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3688  * @cfg {String} glyphicon name of glyphicon
3689  * @cfg {String} icon name of font awesome icon
3690  * @cfg {Boolean} active Is item active
3691  * @cfg {Boolean} disabled Is item disabled
3692  
3693  * @cfg {Boolean} preventDefault (true | false) default false
3694  * @cfg {String} tabId the tab that this item activates.
3695  * @cfg {String} tagtype (a|span) render as a href or span?
3696   
3697  * @constructor
3698  * Create a new Navbar Item
3699  * @param {Object} config The config object
3700  */
3701 Roo.bootstrap.NavItem = function(config){
3702     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3703     this.addEvents({
3704         // raw events
3705         /**
3706          * @event click
3707          * The raw click event for the entire grid.
3708          * @param {Roo.EventObject} e
3709          */
3710         "click" : true,
3711          /**
3712             * @event changed
3713             * Fires when the active item active state changes
3714             * @param {Roo.bootstrap.NavItem} this
3715             * @param {boolean} state the new state
3716              
3717          */
3718         'changed': true
3719     });
3720    
3721 };
3722
3723 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3724     
3725     href: false,
3726     html: '',
3727     badge: '',
3728     icon: false,
3729     glyphicon: false,
3730     active: false,
3731     preventDefault : false,
3732     tabId : false,
3733     tagtype : 'a',
3734     disabled : false,
3735     
3736     was_active : false,
3737     
3738     getAutoCreate : function(){
3739          
3740         var cfg = {
3741             tag: 'li',
3742             cls: 'nav-item'
3743             
3744         }
3745         if (this.active) {
3746             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3747         }
3748         if (this.disabled) {
3749             cfg.cls += ' disabled';
3750         }
3751         
3752         if (this.href || this.html || this.glyphicon || this.icon) {
3753             cfg.cn = [
3754                 {
3755                     tag: this.tagtype,
3756                     href : this.href || "#",
3757                     html: this.html || ''
3758                 }
3759             ];
3760             
3761             if (this.icon) {
3762                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3763             }
3764
3765             if(this.glyphicon) {
3766                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3767             }
3768             
3769             if (this.menu) {
3770                 
3771                 cfg.cn[0].html += " <span class='caret'></span>";
3772              
3773             }
3774             
3775             if (this.badge !== '') {
3776                  
3777                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3778             }
3779         }
3780         
3781         
3782         
3783         return cfg;
3784     },
3785     initEvents: function() 
3786     {
3787         if (typeof (this.menu) != 'undefined') {
3788             this.menu.parentType = this.xtype;
3789             this.menu.triggerEl = this.el;
3790             this.menu = this.addxtype(Roo.apply({}, this.menu));
3791         }
3792         
3793         this.el.select('a',true).on('click', this.onClick, this);
3794         
3795         if(this.tagtype == 'span'){
3796             this.el.select('span',true).on('click', this.onClick, this);
3797         }
3798        
3799         // at this point parent should be available..
3800         this.parent().register(this);
3801     },
3802     
3803     onClick : function(e)
3804     {
3805          
3806         if(this.preventDefault){
3807             e.preventDefault();
3808         }
3809         if (this.disabled) {
3810             return;
3811         }
3812         
3813         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3814         if (tg && tg.transition) {
3815             Roo.log("waiting for the transitionend");
3816             return;
3817         }
3818         
3819         Roo.log("fire event clicked");
3820         if(this.fireEvent('click', this, e) === false){
3821             return;
3822         };
3823         
3824         if(this.tagtype == 'span'){
3825             return;
3826         }
3827         
3828         var p = this.parent();
3829         if (['tabs','pills'].indexOf(p.type)!==-1) {
3830             if (typeof(p.setActiveItem) !== 'undefined') {
3831                 p.setActiveItem(this);
3832             }
3833         }
3834         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3835         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3836             // remove the collapsed menu expand...
3837             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3838         }
3839         
3840     },
3841     
3842     isActive: function () {
3843         return this.active
3844     },
3845     setActive : function(state, fire, is_was_active)
3846     {
3847         if (this.active && !state & this.navId) {
3848             this.was_active = true;
3849             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3850             if (nv) {
3851                 nv.clearWasActive(this);
3852             }
3853             
3854         }
3855         this.active = state;
3856         
3857         if (!state ) {
3858             this.el.removeClass('active');
3859         } else if (!this.el.hasClass('active')) {
3860             this.el.addClass('active');
3861         }
3862         if (fire) {
3863             this.fireEvent('changed', this, state);
3864         }
3865         
3866         // show a panel if it's registered and related..
3867         
3868         if (!this.navId || !this.tabId || !state || is_was_active) {
3869             return;
3870         }
3871         
3872         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3873         if (!tg) {
3874             return;
3875         }
3876         var pan = tg.getPanelByName(this.tabId);
3877         if (!pan) {
3878             return;
3879         }
3880         // if we can not flip to new panel - go back to old nav highlight..
3881         if (false == tg.showPanel(pan)) {
3882             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3883             if (nv) {
3884                 var onav = nv.getWasActive();
3885                 if (onav) {
3886                     onav.setActive(true, false, true);
3887                 }
3888             }
3889             
3890         }
3891         
3892         
3893         
3894     },
3895      // this should not be here...
3896     setDisabled : function(state)
3897     {
3898         this.disabled = state;
3899         if (!state ) {
3900             this.el.removeClass('disabled');
3901         } else if (!this.el.hasClass('disabled')) {
3902             this.el.addClass('disabled');
3903         }
3904         
3905     },
3906     
3907     /**
3908      * Fetch the element to display the tooltip on.
3909      * @return {Roo.Element} defaults to this.el
3910      */
3911     tooltipEl : function()
3912     {
3913         return this.el.select('' + this.tagtype + '', true).first();
3914     }
3915 });
3916  
3917
3918  /*
3919  * - LGPL
3920  *
3921  * sidebar item
3922  *
3923  *  li
3924  *    <span> icon </span>
3925  *    <span> text </span>
3926  *    <span>badge </span>
3927  */
3928
3929 /**
3930  * @class Roo.bootstrap.NavSidebarItem
3931  * @extends Roo.bootstrap.NavItem
3932  * Bootstrap Navbar.NavSidebarItem class
3933  * @constructor
3934  * Create a new Navbar Button
3935  * @param {Object} config The config object
3936  */
3937 Roo.bootstrap.NavSidebarItem = function(config){
3938     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3939     this.addEvents({
3940         // raw events
3941         /**
3942          * @event click
3943          * The raw click event for the entire grid.
3944          * @param {Roo.EventObject} e
3945          */
3946         "click" : true,
3947          /**
3948             * @event changed
3949             * Fires when the active item active state changes
3950             * @param {Roo.bootstrap.NavSidebarItem} this
3951             * @param {boolean} state the new state
3952              
3953          */
3954         'changed': true
3955     });
3956    
3957 };
3958
3959 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3960     
3961     
3962     getAutoCreate : function(){
3963         
3964         
3965         var a = {
3966                 tag: 'a',
3967                 href : this.href || '#',
3968                 cls: '',
3969                 html : '',
3970                 cn : []
3971         };
3972         var cfg = {
3973             tag: 'li',
3974             cls: '',
3975             cn: [ a ]
3976         }
3977         var span = {
3978             tag: 'span',
3979             html : this.html || ''
3980         }
3981         
3982         
3983         if (this.active) {
3984             cfg.cls += ' active';
3985         }
3986         
3987         // left icon..
3988         if (this.glyphicon || this.icon) {
3989             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3990             a.cn.push({ tag : 'i', cls : c }) ;
3991         }
3992         // html..
3993         a.cn.push(span);
3994         // then badge..
3995         if (this.badge !== '') {
3996             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3997         }
3998         // fi
3999         if (this.menu) {
4000             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4001             a.cls += 'dropdown-toggle treeview' ;
4002             
4003         }
4004         
4005         
4006         
4007         return cfg;
4008          
4009            
4010     }
4011    
4012      
4013  
4014 });
4015  
4016
4017  /*
4018  * - LGPL
4019  *
4020  * row
4021  * 
4022  */
4023
4024 /**
4025  * @class Roo.bootstrap.Row
4026  * @extends Roo.bootstrap.Component
4027  * Bootstrap Row class (contains columns...)
4028  * 
4029  * @constructor
4030  * Create a new Row
4031  * @param {Object} config The config object
4032  */
4033
4034 Roo.bootstrap.Row = function(config){
4035     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4036 };
4037
4038 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4039     
4040     getAutoCreate : function(){
4041        return {
4042             cls: 'row clearfix'
4043        };
4044     }
4045     
4046     
4047 });
4048
4049  
4050
4051  /*
4052  * - LGPL
4053  *
4054  * element
4055  * 
4056  */
4057
4058 /**
4059  * @class Roo.bootstrap.Element
4060  * @extends Roo.bootstrap.Component
4061  * Bootstrap Element class
4062  * @cfg {String} html contents of the element
4063  * @cfg {String} tag tag of the element
4064  * @cfg {String} cls class of the element
4065  * 
4066  * @constructor
4067  * Create a new Element
4068  * @param {Object} config The config object
4069  */
4070
4071 Roo.bootstrap.Element = function(config){
4072     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4073 };
4074
4075 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4076     
4077     tag: 'div',
4078     cls: '',
4079     html: '',
4080      
4081     
4082     getAutoCreate : function(){
4083         
4084         var cfg = {
4085             tag: this.tag,
4086             cls: this.cls,
4087             html: this.html
4088         }
4089         
4090         
4091         
4092         return cfg;
4093     }
4094    
4095 });
4096
4097  
4098
4099  /*
4100  * - LGPL
4101  *
4102  * pagination
4103  * 
4104  */
4105
4106 /**
4107  * @class Roo.bootstrap.Pagination
4108  * @extends Roo.bootstrap.Component
4109  * Bootstrap Pagination class
4110  * @cfg {String} size xs | sm | md | lg
4111  * @cfg {Boolean} inverse false | true
4112  * 
4113  * @constructor
4114  * Create a new Pagination
4115  * @param {Object} config The config object
4116  */
4117
4118 Roo.bootstrap.Pagination = function(config){
4119     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4120 };
4121
4122 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4123     
4124     cls: false,
4125     size: false,
4126     inverse: false,
4127     
4128     getAutoCreate : function(){
4129         var cfg = {
4130             tag: 'ul',
4131                 cls: 'pagination'
4132         };
4133         if (this.inverse) {
4134             cfg.cls += ' inverse';
4135         }
4136         if (this.html) {
4137             cfg.html=this.html;
4138         }
4139         if (this.cls) {
4140             cfg.cls += " " + this.cls;
4141         }
4142         return cfg;
4143     }
4144    
4145 });
4146
4147  
4148
4149  /*
4150  * - LGPL
4151  *
4152  * Pagination item
4153  * 
4154  */
4155
4156
4157 /**
4158  * @class Roo.bootstrap.PaginationItem
4159  * @extends Roo.bootstrap.Component
4160  * Bootstrap PaginationItem class
4161  * @cfg {String} html text
4162  * @cfg {String} href the link
4163  * @cfg {Boolean} preventDefault (true | false) default true
4164  * @cfg {Boolean} active (true | false) default false
4165  * @cfg {Boolean} disabled default false
4166  * 
4167  * 
4168  * @constructor
4169  * Create a new PaginationItem
4170  * @param {Object} config The config object
4171  */
4172
4173
4174 Roo.bootstrap.PaginationItem = function(config){
4175     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4176     this.addEvents({
4177         // raw events
4178         /**
4179          * @event click
4180          * The raw click event for the entire grid.
4181          * @param {Roo.EventObject} e
4182          */
4183         "click" : true
4184     });
4185 };
4186
4187 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4188     
4189     href : false,
4190     html : false,
4191     preventDefault: true,
4192     active : false,
4193     cls : false,
4194     disabled: false,
4195     
4196     getAutoCreate : function(){
4197         var cfg= {
4198             tag: 'li',
4199             cn: [
4200                 {
4201                     tag : 'a',
4202                     href : this.href ? this.href : '#',
4203                     html : this.html ? this.html : ''
4204                 }
4205             ]
4206         };
4207         
4208         if(this.cls){
4209             cfg.cls = this.cls;
4210         }
4211         
4212         if(this.disabled){
4213             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4214         }
4215         
4216         if(this.active){
4217             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4218         }
4219         
4220         return cfg;
4221     },
4222     
4223     initEvents: function() {
4224         
4225         this.el.on('click', this.onClick, this);
4226         
4227     },
4228     onClick : function(e)
4229     {
4230         Roo.log('PaginationItem on click ');
4231         if(this.preventDefault){
4232             e.preventDefault();
4233         }
4234         
4235         if(this.disabled){
4236             return;
4237         }
4238         
4239         this.fireEvent('click', this, e);
4240     }
4241    
4242 });
4243
4244  
4245
4246  /*
4247  * - LGPL
4248  *
4249  * slider
4250  * 
4251  */
4252
4253
4254 /**
4255  * @class Roo.bootstrap.Slider
4256  * @extends Roo.bootstrap.Component
4257  * Bootstrap Slider class
4258  *    
4259  * @constructor
4260  * Create a new Slider
4261  * @param {Object} config The config object
4262  */
4263
4264 Roo.bootstrap.Slider = function(config){
4265     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4266 };
4267
4268 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4269     
4270     getAutoCreate : function(){
4271         
4272         var cfg = {
4273             tag: 'div',
4274             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4275             cn: [
4276                 {
4277                     tag: 'a',
4278                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4279                 }
4280             ]
4281         }
4282         
4283         return cfg;
4284     }
4285    
4286 });
4287
4288  /*
4289  * Based on:
4290  * Ext JS Library 1.1.1
4291  * Copyright(c) 2006-2007, Ext JS, LLC.
4292  *
4293  * Originally Released Under LGPL - original licence link has changed is not relivant.
4294  *
4295  * Fork - LGPL
4296  * <script type="text/javascript">
4297  */
4298  
4299
4300 /**
4301  * @class Roo.grid.ColumnModel
4302  * @extends Roo.util.Observable
4303  * This is the default implementation of a ColumnModel used by the Grid. It defines
4304  * the columns in the grid.
4305  * <br>Usage:<br>
4306  <pre><code>
4307  var colModel = new Roo.grid.ColumnModel([
4308         {header: "Ticker", width: 60, sortable: true, locked: true},
4309         {header: "Company Name", width: 150, sortable: true},
4310         {header: "Market Cap.", width: 100, sortable: true},
4311         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4312         {header: "Employees", width: 100, sortable: true, resizable: false}
4313  ]);
4314  </code></pre>
4315  * <p>
4316  
4317  * The config options listed for this class are options which may appear in each
4318  * individual column definition.
4319  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4320  * @constructor
4321  * @param {Object} config An Array of column config objects. See this class's
4322  * config objects for details.
4323 */
4324 Roo.grid.ColumnModel = function(config){
4325         /**
4326      * The config passed into the constructor
4327      */
4328     this.config = config;
4329     this.lookup = {};
4330
4331     // if no id, create one
4332     // if the column does not have a dataIndex mapping,
4333     // map it to the order it is in the config
4334     for(var i = 0, len = config.length; i < len; i++){
4335         var c = config[i];
4336         if(typeof c.dataIndex == "undefined"){
4337             c.dataIndex = i;
4338         }
4339         if(typeof c.renderer == "string"){
4340             c.renderer = Roo.util.Format[c.renderer];
4341         }
4342         if(typeof c.id == "undefined"){
4343             c.id = Roo.id();
4344         }
4345         if(c.editor && c.editor.xtype){
4346             c.editor  = Roo.factory(c.editor, Roo.grid);
4347         }
4348         if(c.editor && c.editor.isFormField){
4349             c.editor = new Roo.grid.GridEditor(c.editor);
4350         }
4351         this.lookup[c.id] = c;
4352     }
4353
4354     /**
4355      * The width of columns which have no width specified (defaults to 100)
4356      * @type Number
4357      */
4358     this.defaultWidth = 100;
4359
4360     /**
4361      * Default sortable of columns which have no sortable specified (defaults to false)
4362      * @type Boolean
4363      */
4364     this.defaultSortable = false;
4365
4366     this.addEvents({
4367         /**
4368              * @event widthchange
4369              * Fires when the width of a column changes.
4370              * @param {ColumnModel} this
4371              * @param {Number} columnIndex The column index
4372              * @param {Number} newWidth The new width
4373              */
4374             "widthchange": true,
4375         /**
4376              * @event headerchange
4377              * Fires when the text of a header changes.
4378              * @param {ColumnModel} this
4379              * @param {Number} columnIndex The column index
4380              * @param {Number} newText The new header text
4381              */
4382             "headerchange": true,
4383         /**
4384              * @event hiddenchange
4385              * Fires when a column is hidden or "unhidden".
4386              * @param {ColumnModel} this
4387              * @param {Number} columnIndex The column index
4388              * @param {Boolean} hidden true if hidden, false otherwise
4389              */
4390             "hiddenchange": true,
4391             /**
4392          * @event columnmoved
4393          * Fires when a column is moved.
4394          * @param {ColumnModel} this
4395          * @param {Number} oldIndex
4396          * @param {Number} newIndex
4397          */
4398         "columnmoved" : true,
4399         /**
4400          * @event columlockchange
4401          * Fires when a column's locked state is changed
4402          * @param {ColumnModel} this
4403          * @param {Number} colIndex
4404          * @param {Boolean} locked true if locked
4405          */
4406         "columnlockchange" : true
4407     });
4408     Roo.grid.ColumnModel.superclass.constructor.call(this);
4409 };
4410 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4411     /**
4412      * @cfg {String} header The header text to display in the Grid view.
4413      */
4414     /**
4415      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4416      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4417      * specified, the column's index is used as an index into the Record's data Array.
4418      */
4419     /**
4420      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4421      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4422      */
4423     /**
4424      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4425      * Defaults to the value of the {@link #defaultSortable} property.
4426      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4427      */
4428     /**
4429      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4430      */
4431     /**
4432      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4433      */
4434     /**
4435      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4436      */
4437     /**
4438      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4439      */
4440     /**
4441      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4442      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4443      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4444      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4445      */
4446        /**
4447      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4448      */
4449     /**
4450      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4451      */
4452     /**
4453      * @cfg {String} cursor (Optional)
4454      */
4455     /**
4456      * @cfg {String} tooltip (Optional)
4457      */
4458     /**
4459      * Returns the id of the column at the specified index.
4460      * @param {Number} index The column index
4461      * @return {String} the id
4462      */
4463     getColumnId : function(index){
4464         return this.config[index].id;
4465     },
4466
4467     /**
4468      * Returns the column for a specified id.
4469      * @param {String} id The column id
4470      * @return {Object} the column
4471      */
4472     getColumnById : function(id){
4473         return this.lookup[id];
4474     },
4475
4476     
4477     /**
4478      * Returns the column for a specified dataIndex.
4479      * @param {String} dataIndex The column dataIndex
4480      * @return {Object|Boolean} the column or false if not found
4481      */
4482     getColumnByDataIndex: function(dataIndex){
4483         var index = this.findColumnIndex(dataIndex);
4484         return index > -1 ? this.config[index] : false;
4485     },
4486     
4487     /**
4488      * Returns the index for a specified column id.
4489      * @param {String} id The column id
4490      * @return {Number} the index, or -1 if not found
4491      */
4492     getIndexById : function(id){
4493         for(var i = 0, len = this.config.length; i < len; i++){
4494             if(this.config[i].id == id){
4495                 return i;
4496             }
4497         }
4498         return -1;
4499     },
4500     
4501     /**
4502      * Returns the index for a specified column dataIndex.
4503      * @param {String} dataIndex The column dataIndex
4504      * @return {Number} the index, or -1 if not found
4505      */
4506     
4507     findColumnIndex : function(dataIndex){
4508         for(var i = 0, len = this.config.length; i < len; i++){
4509             if(this.config[i].dataIndex == dataIndex){
4510                 return i;
4511             }
4512         }
4513         return -1;
4514     },
4515     
4516     
4517     moveColumn : function(oldIndex, newIndex){
4518         var c = this.config[oldIndex];
4519         this.config.splice(oldIndex, 1);
4520         this.config.splice(newIndex, 0, c);
4521         this.dataMap = null;
4522         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4523     },
4524
4525     isLocked : function(colIndex){
4526         return this.config[colIndex].locked === true;
4527     },
4528
4529     setLocked : function(colIndex, value, suppressEvent){
4530         if(this.isLocked(colIndex) == value){
4531             return;
4532         }
4533         this.config[colIndex].locked = value;
4534         if(!suppressEvent){
4535             this.fireEvent("columnlockchange", this, colIndex, value);
4536         }
4537     },
4538
4539     getTotalLockedWidth : function(){
4540         var totalWidth = 0;
4541         for(var i = 0; i < this.config.length; i++){
4542             if(this.isLocked(i) && !this.isHidden(i)){
4543                 this.totalWidth += this.getColumnWidth(i);
4544             }
4545         }
4546         return totalWidth;
4547     },
4548
4549     getLockedCount : function(){
4550         for(var i = 0, len = this.config.length; i < len; i++){
4551             if(!this.isLocked(i)){
4552                 return i;
4553             }
4554         }
4555     },
4556
4557     /**
4558      * Returns the number of columns.
4559      * @return {Number}
4560      */
4561     getColumnCount : function(visibleOnly){
4562         if(visibleOnly === true){
4563             var c = 0;
4564             for(var i = 0, len = this.config.length; i < len; i++){
4565                 if(!this.isHidden(i)){
4566                     c++;
4567                 }
4568             }
4569             return c;
4570         }
4571         return this.config.length;
4572     },
4573
4574     /**
4575      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4576      * @param {Function} fn
4577      * @param {Object} scope (optional)
4578      * @return {Array} result
4579      */
4580     getColumnsBy : function(fn, scope){
4581         var r = [];
4582         for(var i = 0, len = this.config.length; i < len; i++){
4583             var c = this.config[i];
4584             if(fn.call(scope||this, c, i) === true){
4585                 r[r.length] = c;
4586             }
4587         }
4588         return r;
4589     },
4590
4591     /**
4592      * Returns true if the specified column is sortable.
4593      * @param {Number} col The column index
4594      * @return {Boolean}
4595      */
4596     isSortable : function(col){
4597         if(typeof this.config[col].sortable == "undefined"){
4598             return this.defaultSortable;
4599         }
4600         return this.config[col].sortable;
4601     },
4602
4603     /**
4604      * Returns the rendering (formatting) function defined for the column.
4605      * @param {Number} col The column index.
4606      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4607      */
4608     getRenderer : function(col){
4609         if(!this.config[col].renderer){
4610             return Roo.grid.ColumnModel.defaultRenderer;
4611         }
4612         return this.config[col].renderer;
4613     },
4614
4615     /**
4616      * Sets the rendering (formatting) function for a column.
4617      * @param {Number} col The column index
4618      * @param {Function} fn The function to use to process the cell's raw data
4619      * to return HTML markup for the grid view. The render function is called with
4620      * the following parameters:<ul>
4621      * <li>Data value.</li>
4622      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4623      * <li>css A CSS style string to apply to the table cell.</li>
4624      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4625      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4626      * <li>Row index</li>
4627      * <li>Column index</li>
4628      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4629      */
4630     setRenderer : function(col, fn){
4631         this.config[col].renderer = fn;
4632     },
4633
4634     /**
4635      * Returns the width for the specified column.
4636      * @param {Number} col The column index
4637      * @return {Number}
4638      */
4639     getColumnWidth : function(col){
4640         return this.config[col].width * 1 || this.defaultWidth;
4641     },
4642
4643     /**
4644      * Sets the width for a column.
4645      * @param {Number} col The column index
4646      * @param {Number} width The new width
4647      */
4648     setColumnWidth : function(col, width, suppressEvent){
4649         this.config[col].width = width;
4650         this.totalWidth = null;
4651         if(!suppressEvent){
4652              this.fireEvent("widthchange", this, col, width);
4653         }
4654     },
4655
4656     /**
4657      * Returns the total width of all columns.
4658      * @param {Boolean} includeHidden True to include hidden column widths
4659      * @return {Number}
4660      */
4661     getTotalWidth : function(includeHidden){
4662         if(!this.totalWidth){
4663             this.totalWidth = 0;
4664             for(var i = 0, len = this.config.length; i < len; i++){
4665                 if(includeHidden || !this.isHidden(i)){
4666                     this.totalWidth += this.getColumnWidth(i);
4667                 }
4668             }
4669         }
4670         return this.totalWidth;
4671     },
4672
4673     /**
4674      * Returns the header for the specified column.
4675      * @param {Number} col The column index
4676      * @return {String}
4677      */
4678     getColumnHeader : function(col){
4679         return this.config[col].header;
4680     },
4681
4682     /**
4683      * Sets the header for a column.
4684      * @param {Number} col The column index
4685      * @param {String} header The new header
4686      */
4687     setColumnHeader : function(col, header){
4688         this.config[col].header = header;
4689         this.fireEvent("headerchange", this, col, header);
4690     },
4691
4692     /**
4693      * Returns the tooltip for the specified column.
4694      * @param {Number} col The column index
4695      * @return {String}
4696      */
4697     getColumnTooltip : function(col){
4698             return this.config[col].tooltip;
4699     },
4700     /**
4701      * Sets the tooltip for a column.
4702      * @param {Number} col The column index
4703      * @param {String} tooltip The new tooltip
4704      */
4705     setColumnTooltip : function(col, tooltip){
4706             this.config[col].tooltip = tooltip;
4707     },
4708
4709     /**
4710      * Returns the dataIndex for the specified column.
4711      * @param {Number} col The column index
4712      * @return {Number}
4713      */
4714     getDataIndex : function(col){
4715         return this.config[col].dataIndex;
4716     },
4717
4718     /**
4719      * Sets the dataIndex for a column.
4720      * @param {Number} col The column index
4721      * @param {Number} dataIndex The new dataIndex
4722      */
4723     setDataIndex : function(col, dataIndex){
4724         this.config[col].dataIndex = dataIndex;
4725     },
4726
4727     
4728     
4729     /**
4730      * Returns true if the cell is editable.
4731      * @param {Number} colIndex The column index
4732      * @param {Number} rowIndex The row index
4733      * @return {Boolean}
4734      */
4735     isCellEditable : function(colIndex, rowIndex){
4736         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4737     },
4738
4739     /**
4740      * Returns the editor defined for the cell/column.
4741      * return false or null to disable editing.
4742      * @param {Number} colIndex The column index
4743      * @param {Number} rowIndex The row index
4744      * @return {Object}
4745      */
4746     getCellEditor : function(colIndex, rowIndex){
4747         return this.config[colIndex].editor;
4748     },
4749
4750     /**
4751      * Sets if a column is editable.
4752      * @param {Number} col The column index
4753      * @param {Boolean} editable True if the column is editable
4754      */
4755     setEditable : function(col, editable){
4756         this.config[col].editable = editable;
4757     },
4758
4759
4760     /**
4761      * Returns true if the column is hidden.
4762      * @param {Number} colIndex The column index
4763      * @return {Boolean}
4764      */
4765     isHidden : function(colIndex){
4766         return this.config[colIndex].hidden;
4767     },
4768
4769
4770     /**
4771      * Returns true if the column width cannot be changed
4772      */
4773     isFixed : function(colIndex){
4774         return this.config[colIndex].fixed;
4775     },
4776
4777     /**
4778      * Returns true if the column can be resized
4779      * @return {Boolean}
4780      */
4781     isResizable : function(colIndex){
4782         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4783     },
4784     /**
4785      * Sets if a column is hidden.
4786      * @param {Number} colIndex The column index
4787      * @param {Boolean} hidden True if the column is hidden
4788      */
4789     setHidden : function(colIndex, hidden){
4790         this.config[colIndex].hidden = hidden;
4791         this.totalWidth = null;
4792         this.fireEvent("hiddenchange", this, colIndex, hidden);
4793     },
4794
4795     /**
4796      * Sets the editor for a column.
4797      * @param {Number} col The column index
4798      * @param {Object} editor The editor object
4799      */
4800     setEditor : function(col, editor){
4801         this.config[col].editor = editor;
4802     }
4803 });
4804
4805 Roo.grid.ColumnModel.defaultRenderer = function(value){
4806         if(typeof value == "string" && value.length < 1){
4807             return "&#160;";
4808         }
4809         return value;
4810 };
4811
4812 // Alias for backwards compatibility
4813 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4814 /*
4815  * Based on:
4816  * Ext JS Library 1.1.1
4817  * Copyright(c) 2006-2007, Ext JS, LLC.
4818  *
4819  * Originally Released Under LGPL - original licence link has changed is not relivant.
4820  *
4821  * Fork - LGPL
4822  * <script type="text/javascript">
4823  */
4824  
4825 /**
4826  * @class Roo.LoadMask
4827  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4828  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4829  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4830  * element's UpdateManager load indicator and will be destroyed after the initial load.
4831  * @constructor
4832  * Create a new LoadMask
4833  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4834  * @param {Object} config The config object
4835  */
4836 Roo.LoadMask = function(el, config){
4837     this.el = Roo.get(el);
4838     Roo.apply(this, config);
4839     if(this.store){
4840         this.store.on('beforeload', this.onBeforeLoad, this);
4841         this.store.on('load', this.onLoad, this);
4842         this.store.on('loadexception', this.onLoadException, this);
4843         this.removeMask = false;
4844     }else{
4845         var um = this.el.getUpdateManager();
4846         um.showLoadIndicator = false; // disable the default indicator
4847         um.on('beforeupdate', this.onBeforeLoad, this);
4848         um.on('update', this.onLoad, this);
4849         um.on('failure', this.onLoad, this);
4850         this.removeMask = true;
4851     }
4852 };
4853
4854 Roo.LoadMask.prototype = {
4855     /**
4856      * @cfg {Boolean} removeMask
4857      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4858      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4859      */
4860     /**
4861      * @cfg {String} msg
4862      * The text to display in a centered loading message box (defaults to 'Loading...')
4863      */
4864     msg : 'Loading...',
4865     /**
4866      * @cfg {String} msgCls
4867      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4868      */
4869     msgCls : 'x-mask-loading',
4870
4871     /**
4872      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4873      * @type Boolean
4874      */
4875     disabled: false,
4876
4877     /**
4878      * Disables the mask to prevent it from being displayed
4879      */
4880     disable : function(){
4881        this.disabled = true;
4882     },
4883
4884     /**
4885      * Enables the mask so that it can be displayed
4886      */
4887     enable : function(){
4888         this.disabled = false;
4889     },
4890     
4891     onLoadException : function()
4892     {
4893         Roo.log(arguments);
4894         
4895         if (typeof(arguments[3]) != 'undefined') {
4896             Roo.MessageBox.alert("Error loading",arguments[3]);
4897         } 
4898         /*
4899         try {
4900             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4901                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4902             }   
4903         } catch(e) {
4904             
4905         }
4906         */
4907     
4908         
4909         
4910         this.el.unmask(this.removeMask);
4911     },
4912     // private
4913     onLoad : function()
4914     {
4915         this.el.unmask(this.removeMask);
4916     },
4917
4918     // private
4919     onBeforeLoad : function(){
4920         if(!this.disabled){
4921             this.el.mask(this.msg, this.msgCls);
4922         }
4923     },
4924
4925     // private
4926     destroy : function(){
4927         if(this.store){
4928             this.store.un('beforeload', this.onBeforeLoad, this);
4929             this.store.un('load', this.onLoad, this);
4930             this.store.un('loadexception', this.onLoadException, this);
4931         }else{
4932             var um = this.el.getUpdateManager();
4933             um.un('beforeupdate', this.onBeforeLoad, this);
4934             um.un('update', this.onLoad, this);
4935             um.un('failure', this.onLoad, this);
4936         }
4937     }
4938 };/*
4939  * - LGPL
4940  *
4941  * table
4942  * 
4943  */
4944
4945 /**
4946  * @class Roo.bootstrap.Table
4947  * @extends Roo.bootstrap.Component
4948  * Bootstrap Table class
4949  * @cfg {String} cls table class
4950  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4951  * @cfg {String} bgcolor Specifies the background color for a table
4952  * @cfg {Number} border Specifies whether the table cells should have borders or not
4953  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4954  * @cfg {Number} cellspacing Specifies the space between cells
4955  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4956  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4957  * @cfg {String} sortable Specifies that the table should be sortable
4958  * @cfg {String} summary Specifies a summary of the content of a table
4959  * @cfg {Number} width Specifies the width of a table
4960  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4961  * 
4962  * @cfg {boolean} striped Should the rows be alternative striped
4963  * @cfg {boolean} bordered Add borders to the table
4964  * @cfg {boolean} hover Add hover highlighting
4965  * @cfg {boolean} condensed Format condensed
4966  * @cfg {boolean} responsive Format condensed
4967  * @cfg {Boolean} loadMask (true|false) default false
4968  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4969  * @cfg {Boolean} thead (true|false) generate thead, default true
4970  * @cfg {Boolean} RowSelection (true|false) default false
4971  * @cfg {Boolean} CellSelection (true|false) default false
4972  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4973  
4974  * 
4975  * @constructor
4976  * Create a new Table
4977  * @param {Object} config The config object
4978  */
4979
4980 Roo.bootstrap.Table = function(config){
4981     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4982     
4983     if (this.sm) {
4984         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4985         this.sm = this.selModel;
4986         this.sm.xmodule = this.xmodule || false;
4987     }
4988     if (this.cm && typeof(this.cm.config) == 'undefined') {
4989         this.colModel = new Roo.grid.ColumnModel(this.cm);
4990         this.cm = this.colModel;
4991         this.cm.xmodule = this.xmodule || false;
4992     }
4993     if (this.store) {
4994         this.store= Roo.factory(this.store, Roo.data);
4995         this.ds = this.store;
4996         this.ds.xmodule = this.xmodule || false;
4997          
4998     }
4999     if (this.footer && this.store) {
5000         this.footer.dataSource = this.ds;
5001         this.footer = Roo.factory(this.footer);
5002     }
5003     
5004     /** @private */
5005     this.addEvents({
5006         /**
5007          * @event cellclick
5008          * Fires when a cell is clicked
5009          * @param {Roo.bootstrap.Table} this
5010          * @param {Roo.Element} el
5011          * @param {Number} rowIndex
5012          * @param {Number} columnIndex
5013          * @param {Roo.EventObject} e
5014          */
5015         "cellclick" : true,
5016         /**
5017          * @event celldblclick
5018          * Fires when a cell is double 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         "celldblclick" : true,
5026         /**
5027          * @event rowclick
5028          * Fires when a row is clicked
5029          * @param {Roo.bootstrap.Table} this
5030          * @param {Roo.Element} el
5031          * @param {Number} rowIndex
5032          * @param {Roo.EventObject} e
5033          */
5034         "rowclick" : true,
5035         /**
5036          * @event rowdblclick
5037          * Fires when a row is double clicked
5038          * @param {Roo.bootstrap.Table} this
5039          * @param {Roo.Element} el
5040          * @param {Number} rowIndex
5041          * @param {Roo.EventObject} e
5042          */
5043         "rowdblclick" : true,
5044         /**
5045          * @event mouseover
5046          * Fires when a mouseover occur
5047          * @param {Roo.bootstrap.Table} this
5048          * @param {Roo.Element} el
5049          * @param {Number} rowIndex
5050          * @param {Number} columnIndex
5051          * @param {Roo.EventObject} e
5052          */
5053         "mouseover" : true,
5054         /**
5055          * @event mouseout
5056          * Fires when a mouseout 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         "mouseout" : true,
5064         /**
5065          * @event rowclass
5066          * Fires when a row is rendered, so you can change add a style to it.
5067          * @param {Roo.bootstrap.Table} this
5068          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5069          */
5070         'rowclass' : true,
5071           /**
5072          * @event rowsrendered
5073          * Fires when all the  rows have been rendered
5074          * @param {Roo.bootstrap.Table} this
5075          */
5076         'rowsrendered' : true
5077         
5078     });
5079 };
5080
5081 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5082     
5083     cls: false,
5084     align: false,
5085     bgcolor: false,
5086     border: false,
5087     cellpadding: false,
5088     cellspacing: false,
5089     frame: false,
5090     rules: false,
5091     sortable: false,
5092     summary: false,
5093     width: false,
5094     striped : false,
5095     bordered: false,
5096     hover:  false,
5097     condensed : false,
5098     responsive : false,
5099     sm : false,
5100     cm : false,
5101     store : false,
5102     loadMask : false,
5103     tfoot : true,
5104     thead : true,
5105     RowSelection : false,
5106     CellSelection : false,
5107     layout : false,
5108     
5109     // Roo.Element - the tbody
5110     mainBody: false, 
5111     
5112     getAutoCreate : function(){
5113         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5114         
5115         cfg = {
5116             tag: 'table',
5117             cls : 'table',
5118             cn : []
5119         }
5120             
5121         if (this.striped) {
5122             cfg.cls += ' table-striped';
5123         }
5124         
5125         if (this.hover) {
5126             cfg.cls += ' table-hover';
5127         }
5128         if (this.bordered) {
5129             cfg.cls += ' table-bordered';
5130         }
5131         if (this.condensed) {
5132             cfg.cls += ' table-condensed';
5133         }
5134         if (this.responsive) {
5135             cfg.cls += ' table-responsive';
5136         }
5137         
5138         if (this.cls) {
5139             cfg.cls+=  ' ' +this.cls;
5140         }
5141         
5142         // this lot should be simplifed...
5143         
5144         if (this.align) {
5145             cfg.align=this.align;
5146         }
5147         if (this.bgcolor) {
5148             cfg.bgcolor=this.bgcolor;
5149         }
5150         if (this.border) {
5151             cfg.border=this.border;
5152         }
5153         if (this.cellpadding) {
5154             cfg.cellpadding=this.cellpadding;
5155         }
5156         if (this.cellspacing) {
5157             cfg.cellspacing=this.cellspacing;
5158         }
5159         if (this.frame) {
5160             cfg.frame=this.frame;
5161         }
5162         if (this.rules) {
5163             cfg.rules=this.rules;
5164         }
5165         if (this.sortable) {
5166             cfg.sortable=this.sortable;
5167         }
5168         if (this.summary) {
5169             cfg.summary=this.summary;
5170         }
5171         if (this.width) {
5172             cfg.width=this.width;
5173         }
5174         if (this.layout) {
5175             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5176         }
5177         
5178         if(this.store || this.cm){
5179             if(this.thead){
5180                 cfg.cn.push(this.renderHeader());
5181             }
5182             
5183             cfg.cn.push(this.renderBody());
5184             
5185             if(this.tfoot){
5186                 cfg.cn.push(this.renderFooter());
5187             }
5188             
5189             cfg.cls+=  ' TableGrid';
5190         }
5191         
5192         return { cn : [ cfg ] };
5193     },
5194     
5195     initEvents : function()
5196     {   
5197         if(!this.store || !this.cm){
5198             return;
5199         }
5200         
5201         //Roo.log('initEvents with ds!!!!');
5202         
5203         this.mainBody = this.el.select('tbody', true).first();
5204         
5205         
5206         var _this = this;
5207         
5208         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5209             e.on('click', _this.sort, _this);
5210         });
5211         
5212         this.el.on("click", this.onClick, this);
5213         this.el.on("dblclick", this.onDblClick, this);
5214         
5215         // why is this done????? = it breaks dialogs??
5216         //this.parent().el.setStyle('position', 'relative');
5217         
5218         
5219         if (this.footer) {
5220             this.footer.parentId = this.id;
5221             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5222         }
5223         
5224         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5225         
5226         this.store.on('load', this.onLoad, this);
5227         this.store.on('beforeload', this.onBeforeLoad, this);
5228         this.store.on('update', this.onUpdate, this);
5229         this.store.on('add', this.onAdd, this);
5230         
5231     },
5232     
5233     onMouseover : function(e, el)
5234     {
5235         var cell = Roo.get(el);
5236         
5237         if(!cell){
5238             return;
5239         }
5240         
5241         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5242             cell = cell.findParent('td', false, true);
5243         }
5244         
5245         var row = cell.findParent('tr', false, true);
5246         var cellIndex = cell.dom.cellIndex;
5247         var rowIndex = row.dom.rowIndex - 1; // start from 0
5248         
5249         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5250         
5251     },
5252     
5253     onMouseout : function(e, el)
5254     {
5255         var cell = Roo.get(el);
5256         
5257         if(!cell){
5258             return;
5259         }
5260         
5261         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5262             cell = cell.findParent('td', false, true);
5263         }
5264         
5265         var row = cell.findParent('tr', false, true);
5266         var cellIndex = cell.dom.cellIndex;
5267         var rowIndex = row.dom.rowIndex - 1; // start from 0
5268         
5269         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5270         
5271     },
5272     
5273     onClick : function(e, el)
5274     {
5275         var cell = Roo.get(el);
5276         
5277         if(!cell || (!this.CellSelection && !this.RowSelection)){
5278             return;
5279         }
5280         
5281         
5282         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5283             cell = cell.findParent('td', false, true);
5284         }
5285         
5286         var row = cell.findParent('tr', false, true);
5287         var cellIndex = cell.dom.cellIndex;
5288         var rowIndex = this.getRowIndex(row);
5289         
5290         if(this.CellSelection){
5291             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5292         }
5293         
5294         if(this.RowSelection){
5295             this.fireEvent('rowclick', this, row, rowIndex, e);
5296         }
5297         
5298         
5299     },
5300     
5301     onDblClick : function(e,el)
5302     {
5303         var cell = Roo.get(el);
5304         
5305         if(!cell || (!this.CellSelection && !this.RowSelection)){
5306             return;
5307         }
5308         
5309         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5310             cell = cell.findParent('td', false, true);
5311         }
5312         
5313         var row = cell.findParent('tr', false, true);
5314         var cellIndex = cell.dom.cellIndex;
5315         var rowIndex = this.getRowIndex(row);
5316         
5317         if(this.CellSelection){
5318             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5319         }
5320         
5321         if(this.RowSelection){
5322             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5323         }
5324     },
5325     
5326     sort : function(e,el)
5327     {
5328         var col = Roo.get(el)
5329         
5330         if(!col.hasClass('sortable')){
5331             return;
5332         }
5333         
5334         var sort = col.attr('sort');
5335         var dir = 'ASC';
5336         
5337         if(col.hasClass('glyphicon-arrow-up')){
5338             dir = 'DESC';
5339         }
5340         
5341         this.store.sortInfo = {field : sort, direction : dir};
5342         
5343         if (this.footer) {
5344             Roo.log("calling footer first");
5345             this.footer.onClick('first');
5346         } else {
5347         
5348             this.store.load({ params : { start : 0 } });
5349         }
5350     },
5351     
5352     renderHeader : function()
5353     {
5354         var header = {
5355             tag: 'thead',
5356             cn : []
5357         };
5358         
5359         var cm = this.cm;
5360         
5361         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5362             
5363             var config = cm.config[i];
5364                     
5365             var c = {
5366                 tag: 'th',
5367                 style : '',
5368                 html: cm.getColumnHeader(i)
5369             };
5370             
5371             if(typeof(config.tooltip != 'undefined')){
5372                 c.tooltip = config.tooltip;
5373             }
5374             
5375             if(typeof(config.hidden) != 'undefined' && config.hidden){
5376                 c.style += ' display:none;';
5377             }
5378             
5379             if(typeof(config.dataIndex) != 'undefined'){
5380                 c.sort = config.dataIndex;
5381             }
5382             
5383             if(typeof(config.sortable) != 'undefined' && config.sortable){
5384                 c.cls = 'sortable';
5385             }
5386             
5387             if(typeof(config.align) != 'undefined' && config.align.length){
5388                 c.style += ' text-align:' + config.align + ';';
5389             }
5390             
5391             if(typeof(config.width) != 'undefined'){
5392                 c.style += ' width:' + config.width + 'px;';
5393             }
5394             
5395             header.cn.push(c)
5396         }
5397         
5398         return header;
5399     },
5400     
5401     renderBody : function()
5402     {
5403         var body = {
5404             tag: 'tbody',
5405             cn : [
5406                 {
5407                     tag: 'tr',
5408                     cn : [
5409                         {
5410                             tag : 'td',
5411                             colspan :  this.cm.getColumnCount()
5412                         }
5413                     ]
5414                 }
5415             ]
5416         };
5417         
5418         return body;
5419     },
5420     
5421     renderFooter : function()
5422     {
5423         var footer = {
5424             tag: 'tfoot',
5425             cn : [
5426                 {
5427                     tag: 'tr',
5428                     cn : [
5429                         {
5430                             tag : 'td',
5431                             colspan :  this.cm.getColumnCount()
5432                         }
5433                     ]
5434                 }
5435             ]
5436         };
5437         
5438         return footer;
5439     },
5440     
5441     
5442     
5443     onLoad : function()
5444     {
5445         Roo.log('ds onload');
5446         this.clear();
5447         
5448         var _this = this;
5449         var cm = this.cm;
5450         var ds = this.store;
5451         
5452         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5453             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5454             
5455             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5456                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5457             }
5458             
5459             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5460                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5461             }
5462         });
5463         
5464         var tbody =  this.mainBody;
5465               
5466         if(ds.getCount() > 0){
5467             ds.data.each(function(d,rowIndex){
5468                 var row =  this.renderRow(cm, ds, rowIndex);
5469                 
5470                 tbody.createChild(row);
5471                 
5472                 var _this = this;
5473                 
5474                 if(row.cellObjects.length){
5475                     Roo.each(row.cellObjects, function(r){
5476                         _this.renderCellObject(r);
5477                     })
5478                 }
5479                 
5480             }, this);
5481         }
5482         
5483         Roo.each(this.el.select('tbody td', true).elements, function(e){
5484             e.on('mouseover', _this.onMouseover, _this);
5485         });
5486         
5487         Roo.each(this.el.select('tbody td', true).elements, function(e){
5488             e.on('mouseout', _this.onMouseout, _this);
5489         });
5490         this.fireEvent('rowsrendered', this);
5491         //if(this.loadMask){
5492         //    this.maskEl.hide();
5493         //}
5494     },
5495     
5496     
5497     onUpdate : function(ds,record)
5498     {
5499         this.refreshRow(record);
5500     },
5501     
5502     onRemove : function(ds, record, index, isUpdate){
5503         if(isUpdate !== true){
5504             this.fireEvent("beforerowremoved", this, index, record);
5505         }
5506         var bt = this.mainBody.dom;
5507         
5508         var rows = this.el.select('tbody > tr', true).elements;
5509         
5510         if(typeof(rows[index]) != 'undefined'){
5511             bt.removeChild(rows[index].dom);
5512         }
5513         
5514 //        if(bt.rows[index]){
5515 //            bt.removeChild(bt.rows[index]);
5516 //        }
5517         
5518         if(isUpdate !== true){
5519             //this.stripeRows(index);
5520             //this.syncRowHeights(index, index);
5521             //this.layout();
5522             this.fireEvent("rowremoved", this, index, record);
5523         }
5524     },
5525     
5526     onAdd : function(ds, records, rowIndex)
5527     {
5528         //Roo.log('on Add called');
5529         // - note this does not handle multiple adding very well..
5530         var bt = this.mainBody.dom;
5531         for (var i =0 ; i < records.length;i++) {
5532             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5533             //Roo.log(records[i]);
5534             //Roo.log(this.store.getAt(rowIndex+i));
5535             this.insertRow(this.store, rowIndex + i, false);
5536             return;
5537         }
5538         
5539     },
5540     
5541     
5542     refreshRow : function(record){
5543         var ds = this.store, index;
5544         if(typeof record == 'number'){
5545             index = record;
5546             record = ds.getAt(index);
5547         }else{
5548             index = ds.indexOf(record);
5549         }
5550         this.insertRow(ds, index, true);
5551         this.onRemove(ds, record, index+1, true);
5552         //this.syncRowHeights(index, index);
5553         //this.layout();
5554         this.fireEvent("rowupdated", this, index, record);
5555     },
5556     
5557     insertRow : function(dm, rowIndex, isUpdate){
5558         
5559         if(!isUpdate){
5560             this.fireEvent("beforerowsinserted", this, rowIndex);
5561         }
5562             //var s = this.getScrollState();
5563         var row = this.renderRow(this.cm, this.store, rowIndex);
5564         // insert before rowIndex..
5565         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5566         
5567         var _this = this;
5568                 
5569         if(row.cellObjects.length){
5570             Roo.each(row.cellObjects, function(r){
5571                 _this.renderCellObject(r);
5572             })
5573         }
5574             
5575         if(!isUpdate){
5576             this.fireEvent("rowsinserted", this, rowIndex);
5577             //this.syncRowHeights(firstRow, lastRow);
5578             //this.stripeRows(firstRow);
5579             //this.layout();
5580         }
5581         
5582     },
5583     
5584     
5585     getRowDom : function(rowIndex)
5586     {
5587         var rows = this.el.select('tbody > tr', true).elements;
5588         
5589         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5590         
5591     },
5592     // returns the object tree for a tr..
5593   
5594     
5595     renderRow : function(cm, ds, rowIndex) 
5596     {
5597         
5598         var d = ds.getAt(rowIndex);
5599         
5600         var row = {
5601             tag : 'tr',
5602             cn : []
5603         };
5604             
5605         var cellObjects = [];
5606         
5607         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5608             var config = cm.config[i];
5609             
5610             var renderer = cm.getRenderer(i);
5611             var value = '';
5612             var id = false;
5613             
5614             if(typeof(renderer) !== 'undefined'){
5615                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5616             }
5617             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5618             // and are rendered into the cells after the row is rendered - using the id for the element.
5619             
5620             if(typeof(value) === 'object'){
5621                 id = Roo.id();
5622                 cellObjects.push({
5623                     container : id,
5624                     cfg : value 
5625                 })
5626             }
5627             
5628             var rowcfg = {
5629                 record: d,
5630                 rowIndex : rowIndex,
5631                 colIndex : i,
5632                 rowClass : ''
5633             }
5634
5635             this.fireEvent('rowclass', this, rowcfg);
5636             
5637             var td = {
5638                 tag: 'td',
5639                 cls : rowcfg.rowClass,
5640                 style: '',
5641                 html: (typeof(value) === 'object') ? '' : value
5642             };
5643             
5644             if (id) {
5645                 td.id = id;
5646             }
5647             
5648             if(typeof(config.hidden) != 'undefined' && config.hidden){
5649                 td.style += ' display:none;';
5650             }
5651             
5652             if(typeof(config.align) != 'undefined' && config.align.length){
5653                 td.style += ' text-align:' + config.align + ';';
5654             }
5655             
5656             if(typeof(config.width) != 'undefined'){
5657                 td.style += ' width:' +  config.width + 'px;';
5658             }
5659             
5660             if(typeof(config.cursor) != 'undefined'){
5661                 td.style += ' cursor:' +  config.cursor + ';';
5662             }
5663              
5664             row.cn.push(td);
5665            
5666         }
5667         
5668         row.cellObjects = cellObjects;
5669         
5670         return row;
5671           
5672     },
5673     
5674     
5675     
5676     onBeforeLoad : function()
5677     {
5678         //Roo.log('ds onBeforeLoad');
5679         
5680         //this.clear();
5681         
5682         //if(this.loadMask){
5683         //    this.maskEl.show();
5684         //}
5685     },
5686      /**
5687      * Remove all rows
5688      */
5689     clear : function()
5690     {
5691         this.el.select('tbody', true).first().dom.innerHTML = '';
5692     },
5693     /**
5694      * Show or hide a row.
5695      * @param {Number} rowIndex to show or hide
5696      * @param {Boolean} state hide
5697      */
5698     setRowVisibility : function(rowIndex, state)
5699     {
5700         var bt = this.mainBody.dom;
5701         
5702         var rows = this.el.select('tbody > tr', true).elements;
5703         
5704         if(typeof(rows[rowIndex]) == 'undefined'){
5705             return;
5706         }
5707         rows[rowIndex].dom.style.display = state ? '' : 'none';
5708     },
5709     
5710     
5711     getSelectionModel : function(){
5712         if(!this.selModel){
5713             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5714         }
5715         return this.selModel;
5716     },
5717     /*
5718      * Render the Roo.bootstrap object from renderder
5719      */
5720     renderCellObject : function(r)
5721     {
5722         var _this = this;
5723         
5724         var t = r.cfg.render(r.container);
5725         
5726         if(r.cfg.cn){
5727             Roo.each(r.cfg.cn, function(c){
5728                 var child = {
5729                     container: t.getChildContainer(),
5730                     cfg: c
5731                 }
5732                 _this.renderCellObject(child);
5733             })
5734         }
5735     },
5736     
5737     getRowIndex : function(row)
5738     {
5739         var rowIndex = -1;
5740         
5741         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
5742             if(el != row){
5743                 return;
5744             }
5745             
5746             rowIndex = index;
5747         });
5748         
5749         return rowIndex;
5750     }
5751    
5752 });
5753
5754  
5755
5756  /*
5757  * - LGPL
5758  *
5759  * table cell
5760  * 
5761  */
5762
5763 /**
5764  * @class Roo.bootstrap.TableCell
5765  * @extends Roo.bootstrap.Component
5766  * Bootstrap TableCell class
5767  * @cfg {String} html cell contain text
5768  * @cfg {String} cls cell class
5769  * @cfg {String} tag cell tag (td|th) default td
5770  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5771  * @cfg {String} align Aligns the content in a cell
5772  * @cfg {String} axis Categorizes cells
5773  * @cfg {String} bgcolor Specifies the background color of a cell
5774  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5775  * @cfg {Number} colspan Specifies the number of columns a cell should span
5776  * @cfg {String} headers Specifies one or more header cells a cell is related to
5777  * @cfg {Number} height Sets the height of a cell
5778  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5779  * @cfg {Number} rowspan Sets the number of rows a cell should span
5780  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5781  * @cfg {String} valign Vertical aligns the content in a cell
5782  * @cfg {Number} width Specifies the width of a cell
5783  * 
5784  * @constructor
5785  * Create a new TableCell
5786  * @param {Object} config The config object
5787  */
5788
5789 Roo.bootstrap.TableCell = function(config){
5790     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5791 };
5792
5793 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5794     
5795     html: false,
5796     cls: false,
5797     tag: false,
5798     abbr: false,
5799     align: false,
5800     axis: false,
5801     bgcolor: false,
5802     charoff: false,
5803     colspan: false,
5804     headers: false,
5805     height: false,
5806     nowrap: false,
5807     rowspan: false,
5808     scope: false,
5809     valign: false,
5810     width: false,
5811     
5812     
5813     getAutoCreate : function(){
5814         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5815         
5816         cfg = {
5817             tag: 'td'
5818         }
5819         
5820         if(this.tag){
5821             cfg.tag = this.tag;
5822         }
5823         
5824         if (this.html) {
5825             cfg.html=this.html
5826         }
5827         if (this.cls) {
5828             cfg.cls=this.cls
5829         }
5830         if (this.abbr) {
5831             cfg.abbr=this.abbr
5832         }
5833         if (this.align) {
5834             cfg.align=this.align
5835         }
5836         if (this.axis) {
5837             cfg.axis=this.axis
5838         }
5839         if (this.bgcolor) {
5840             cfg.bgcolor=this.bgcolor
5841         }
5842         if (this.charoff) {
5843             cfg.charoff=this.charoff
5844         }
5845         if (this.colspan) {
5846             cfg.colspan=this.colspan
5847         }
5848         if (this.headers) {
5849             cfg.headers=this.headers
5850         }
5851         if (this.height) {
5852             cfg.height=this.height
5853         }
5854         if (this.nowrap) {
5855             cfg.nowrap=this.nowrap
5856         }
5857         if (this.rowspan) {
5858             cfg.rowspan=this.rowspan
5859         }
5860         if (this.scope) {
5861             cfg.scope=this.scope
5862         }
5863         if (this.valign) {
5864             cfg.valign=this.valign
5865         }
5866         if (this.width) {
5867             cfg.width=this.width
5868         }
5869         
5870         
5871         return cfg;
5872     }
5873    
5874 });
5875
5876  
5877
5878  /*
5879  * - LGPL
5880  *
5881  * table row
5882  * 
5883  */
5884
5885 /**
5886  * @class Roo.bootstrap.TableRow
5887  * @extends Roo.bootstrap.Component
5888  * Bootstrap TableRow class
5889  * @cfg {String} cls row class
5890  * @cfg {String} align Aligns the content in a table row
5891  * @cfg {String} bgcolor Specifies a background color for a table row
5892  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5893  * @cfg {String} valign Vertical aligns the content in a table row
5894  * 
5895  * @constructor
5896  * Create a new TableRow
5897  * @param {Object} config The config object
5898  */
5899
5900 Roo.bootstrap.TableRow = function(config){
5901     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5902 };
5903
5904 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5905     
5906     cls: false,
5907     align: false,
5908     bgcolor: false,
5909     charoff: false,
5910     valign: false,
5911     
5912     getAutoCreate : function(){
5913         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5914         
5915         cfg = {
5916             tag: 'tr'
5917         }
5918             
5919         if(this.cls){
5920             cfg.cls = this.cls;
5921         }
5922         if(this.align){
5923             cfg.align = this.align;
5924         }
5925         if(this.bgcolor){
5926             cfg.bgcolor = this.bgcolor;
5927         }
5928         if(this.charoff){
5929             cfg.charoff = this.charoff;
5930         }
5931         if(this.valign){
5932             cfg.valign = this.valign;
5933         }
5934         
5935         return cfg;
5936     }
5937    
5938 });
5939
5940  
5941
5942  /*
5943  * - LGPL
5944  *
5945  * table body
5946  * 
5947  */
5948
5949 /**
5950  * @class Roo.bootstrap.TableBody
5951  * @extends Roo.bootstrap.Component
5952  * Bootstrap TableBody class
5953  * @cfg {String} cls element class
5954  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5955  * @cfg {String} align Aligns the content inside the element
5956  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5957  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5958  * 
5959  * @constructor
5960  * Create a new TableBody
5961  * @param {Object} config The config object
5962  */
5963
5964 Roo.bootstrap.TableBody = function(config){
5965     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5966 };
5967
5968 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5969     
5970     cls: false,
5971     tag: false,
5972     align: false,
5973     charoff: false,
5974     valign: false,
5975     
5976     getAutoCreate : function(){
5977         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5978         
5979         cfg = {
5980             tag: 'tbody'
5981         }
5982             
5983         if (this.cls) {
5984             cfg.cls=this.cls
5985         }
5986         if(this.tag){
5987             cfg.tag = this.tag;
5988         }
5989         
5990         if(this.align){
5991             cfg.align = this.align;
5992         }
5993         if(this.charoff){
5994             cfg.charoff = this.charoff;
5995         }
5996         if(this.valign){
5997             cfg.valign = this.valign;
5998         }
5999         
6000         return cfg;
6001     }
6002     
6003     
6004 //    initEvents : function()
6005 //    {
6006 //        
6007 //        if(!this.store){
6008 //            return;
6009 //        }
6010 //        
6011 //        this.store = Roo.factory(this.store, Roo.data);
6012 //        this.store.on('load', this.onLoad, this);
6013 //        
6014 //        this.store.load();
6015 //        
6016 //    },
6017 //    
6018 //    onLoad: function () 
6019 //    {   
6020 //        this.fireEvent('load', this);
6021 //    }
6022 //    
6023 //   
6024 });
6025
6026  
6027
6028  /*
6029  * Based on:
6030  * Ext JS Library 1.1.1
6031  * Copyright(c) 2006-2007, Ext JS, LLC.
6032  *
6033  * Originally Released Under LGPL - original licence link has changed is not relivant.
6034  *
6035  * Fork - LGPL
6036  * <script type="text/javascript">
6037  */
6038
6039 // as we use this in bootstrap.
6040 Roo.namespace('Roo.form');
6041  /**
6042  * @class Roo.form.Action
6043  * Internal Class used to handle form actions
6044  * @constructor
6045  * @param {Roo.form.BasicForm} el The form element or its id
6046  * @param {Object} config Configuration options
6047  */
6048
6049  
6050  
6051 // define the action interface
6052 Roo.form.Action = function(form, options){
6053     this.form = form;
6054     this.options = options || {};
6055 };
6056 /**
6057  * Client Validation Failed
6058  * @const 
6059  */
6060 Roo.form.Action.CLIENT_INVALID = 'client';
6061 /**
6062  * Server Validation Failed
6063  * @const 
6064  */
6065 Roo.form.Action.SERVER_INVALID = 'server';
6066  /**
6067  * Connect to Server Failed
6068  * @const 
6069  */
6070 Roo.form.Action.CONNECT_FAILURE = 'connect';
6071 /**
6072  * Reading Data from Server Failed
6073  * @const 
6074  */
6075 Roo.form.Action.LOAD_FAILURE = 'load';
6076
6077 Roo.form.Action.prototype = {
6078     type : 'default',
6079     failureType : undefined,
6080     response : undefined,
6081     result : undefined,
6082
6083     // interface method
6084     run : function(options){
6085
6086     },
6087
6088     // interface method
6089     success : function(response){
6090
6091     },
6092
6093     // interface method
6094     handleResponse : function(response){
6095
6096     },
6097
6098     // default connection failure
6099     failure : function(response){
6100         
6101         this.response = response;
6102         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6103         this.form.afterAction(this, false);
6104     },
6105
6106     processResponse : function(response){
6107         this.response = response;
6108         if(!response.responseText){
6109             return true;
6110         }
6111         this.result = this.handleResponse(response);
6112         return this.result;
6113     },
6114
6115     // utility functions used internally
6116     getUrl : function(appendParams){
6117         var url = this.options.url || this.form.url || this.form.el.dom.action;
6118         if(appendParams){
6119             var p = this.getParams();
6120             if(p){
6121                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6122             }
6123         }
6124         return url;
6125     },
6126
6127     getMethod : function(){
6128         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6129     },
6130
6131     getParams : function(){
6132         var bp = this.form.baseParams;
6133         var p = this.options.params;
6134         if(p){
6135             if(typeof p == "object"){
6136                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6137             }else if(typeof p == 'string' && bp){
6138                 p += '&' + Roo.urlEncode(bp);
6139             }
6140         }else if(bp){
6141             p = Roo.urlEncode(bp);
6142         }
6143         return p;
6144     },
6145
6146     createCallback : function(){
6147         return {
6148             success: this.success,
6149             failure: this.failure,
6150             scope: this,
6151             timeout: (this.form.timeout*1000),
6152             upload: this.form.fileUpload ? this.success : undefined
6153         };
6154     }
6155 };
6156
6157 Roo.form.Action.Submit = function(form, options){
6158     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6159 };
6160
6161 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6162     type : 'submit',
6163
6164     haveProgress : false,
6165     uploadComplete : false,
6166     
6167     // uploadProgress indicator.
6168     uploadProgress : function()
6169     {
6170         if (!this.form.progressUrl) {
6171             return;
6172         }
6173         
6174         if (!this.haveProgress) {
6175             Roo.MessageBox.progress("Uploading", "Uploading");
6176         }
6177         if (this.uploadComplete) {
6178            Roo.MessageBox.hide();
6179            return;
6180         }
6181         
6182         this.haveProgress = true;
6183    
6184         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6185         
6186         var c = new Roo.data.Connection();
6187         c.request({
6188             url : this.form.progressUrl,
6189             params: {
6190                 id : uid
6191             },
6192             method: 'GET',
6193             success : function(req){
6194                //console.log(data);
6195                 var rdata = false;
6196                 var edata;
6197                 try  {
6198                    rdata = Roo.decode(req.responseText)
6199                 } catch (e) {
6200                     Roo.log("Invalid data from server..");
6201                     Roo.log(edata);
6202                     return;
6203                 }
6204                 if (!rdata || !rdata.success) {
6205                     Roo.log(rdata);
6206                     Roo.MessageBox.alert(Roo.encode(rdata));
6207                     return;
6208                 }
6209                 var data = rdata.data;
6210                 
6211                 if (this.uploadComplete) {
6212                    Roo.MessageBox.hide();
6213                    return;
6214                 }
6215                    
6216                 if (data){
6217                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6218                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6219                     );
6220                 }
6221                 this.uploadProgress.defer(2000,this);
6222             },
6223        
6224             failure: function(data) {
6225                 Roo.log('progress url failed ');
6226                 Roo.log(data);
6227             },
6228             scope : this
6229         });
6230            
6231     },
6232     
6233     
6234     run : function()
6235     {
6236         // run get Values on the form, so it syncs any secondary forms.
6237         this.form.getValues();
6238         
6239         var o = this.options;
6240         var method = this.getMethod();
6241         var isPost = method == 'POST';
6242         if(o.clientValidation === false || this.form.isValid()){
6243             
6244             if (this.form.progressUrl) {
6245                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6246                     (new Date() * 1) + '' + Math.random());
6247                     
6248             } 
6249             
6250             
6251             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6252                 form:this.form.el.dom,
6253                 url:this.getUrl(!isPost),
6254                 method: method,
6255                 params:isPost ? this.getParams() : null,
6256                 isUpload: this.form.fileUpload
6257             }));
6258             
6259             this.uploadProgress();
6260
6261         }else if (o.clientValidation !== false){ // client validation failed
6262             this.failureType = Roo.form.Action.CLIENT_INVALID;
6263             this.form.afterAction(this, false);
6264         }
6265     },
6266
6267     success : function(response)
6268     {
6269         this.uploadComplete= true;
6270         if (this.haveProgress) {
6271             Roo.MessageBox.hide();
6272         }
6273         
6274         
6275         var result = this.processResponse(response);
6276         if(result === true || result.success){
6277             this.form.afterAction(this, true);
6278             return;
6279         }
6280         if(result.errors){
6281             this.form.markInvalid(result.errors);
6282             this.failureType = Roo.form.Action.SERVER_INVALID;
6283         }
6284         this.form.afterAction(this, false);
6285     },
6286     failure : function(response)
6287     {
6288         this.uploadComplete= true;
6289         if (this.haveProgress) {
6290             Roo.MessageBox.hide();
6291         }
6292         
6293         this.response = response;
6294         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6295         this.form.afterAction(this, false);
6296     },
6297     
6298     handleResponse : function(response){
6299         if(this.form.errorReader){
6300             var rs = this.form.errorReader.read(response);
6301             var errors = [];
6302             if(rs.records){
6303                 for(var i = 0, len = rs.records.length; i < len; i++) {
6304                     var r = rs.records[i];
6305                     errors[i] = r.data;
6306                 }
6307             }
6308             if(errors.length < 1){
6309                 errors = null;
6310             }
6311             return {
6312                 success : rs.success,
6313                 errors : errors
6314             };
6315         }
6316         var ret = false;
6317         try {
6318             ret = Roo.decode(response.responseText);
6319         } catch (e) {
6320             ret = {
6321                 success: false,
6322                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6323                 errors : []
6324             };
6325         }
6326         return ret;
6327         
6328     }
6329 });
6330
6331
6332 Roo.form.Action.Load = function(form, options){
6333     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6334     this.reader = this.form.reader;
6335 };
6336
6337 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6338     type : 'load',
6339
6340     run : function(){
6341         
6342         Roo.Ajax.request(Roo.apply(
6343                 this.createCallback(), {
6344                     method:this.getMethod(),
6345                     url:this.getUrl(false),
6346                     params:this.getParams()
6347         }));
6348     },
6349
6350     success : function(response){
6351         
6352         var result = this.processResponse(response);
6353         if(result === true || !result.success || !result.data){
6354             this.failureType = Roo.form.Action.LOAD_FAILURE;
6355             this.form.afterAction(this, false);
6356             return;
6357         }
6358         this.form.clearInvalid();
6359         this.form.setValues(result.data);
6360         this.form.afterAction(this, true);
6361     },
6362
6363     handleResponse : function(response){
6364         if(this.form.reader){
6365             var rs = this.form.reader.read(response);
6366             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6367             return {
6368                 success : rs.success,
6369                 data : data
6370             };
6371         }
6372         return Roo.decode(response.responseText);
6373     }
6374 });
6375
6376 Roo.form.Action.ACTION_TYPES = {
6377     'load' : Roo.form.Action.Load,
6378     'submit' : Roo.form.Action.Submit
6379 };/*
6380  * - LGPL
6381  *
6382  * form
6383  * 
6384  */
6385
6386 /**
6387  * @class Roo.bootstrap.Form
6388  * @extends Roo.bootstrap.Component
6389  * Bootstrap Form class
6390  * @cfg {String} method  GET | POST (default POST)
6391  * @cfg {String} labelAlign top | left (default top)
6392  * @cfg {String} align left  | right - for navbars
6393  * @cfg {Boolean} loadMask load mask when submit (default true)
6394
6395  * 
6396  * @constructor
6397  * Create a new Form
6398  * @param {Object} config The config object
6399  */
6400
6401
6402 Roo.bootstrap.Form = function(config){
6403     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6404     this.addEvents({
6405         /**
6406          * @event clientvalidation
6407          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6408          * @param {Form} this
6409          * @param {Boolean} valid true if the form has passed client-side validation
6410          */
6411         clientvalidation: true,
6412         /**
6413          * @event beforeaction
6414          * Fires before any action is performed. Return false to cancel the action.
6415          * @param {Form} this
6416          * @param {Action} action The action to be performed
6417          */
6418         beforeaction: true,
6419         /**
6420          * @event actionfailed
6421          * Fires when an action fails.
6422          * @param {Form} this
6423          * @param {Action} action The action that failed
6424          */
6425         actionfailed : true,
6426         /**
6427          * @event actioncomplete
6428          * Fires when an action is completed.
6429          * @param {Form} this
6430          * @param {Action} action The action that completed
6431          */
6432         actioncomplete : true
6433     });
6434     
6435 };
6436
6437 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6438       
6439      /**
6440      * @cfg {String} method
6441      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6442      */
6443     method : 'POST',
6444     /**
6445      * @cfg {String} url
6446      * The URL to use for form actions if one isn't supplied in the action options.
6447      */
6448     /**
6449      * @cfg {Boolean} fileUpload
6450      * Set to true if this form is a file upload.
6451      */
6452      
6453     /**
6454      * @cfg {Object} baseParams
6455      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6456      */
6457       
6458     /**
6459      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6460      */
6461     timeout: 30,
6462     /**
6463      * @cfg {Sting} align (left|right) for navbar forms
6464      */
6465     align : 'left',
6466
6467     // private
6468     activeAction : null,
6469  
6470     /**
6471      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6472      * element by passing it or its id or mask the form itself by passing in true.
6473      * @type Mixed
6474      */
6475     waitMsgTarget : false,
6476     
6477     loadMask : true,
6478     
6479     getAutoCreate : function(){
6480         
6481         var cfg = {
6482             tag: 'form',
6483             method : this.method || 'POST',
6484             id : this.id || Roo.id(),
6485             cls : ''
6486         }
6487         if (this.parent().xtype.match(/^Nav/)) {
6488             cfg.cls = 'navbar-form navbar-' + this.align;
6489             
6490         }
6491         
6492         if (this.labelAlign == 'left' ) {
6493             cfg.cls += ' form-horizontal';
6494         }
6495         
6496         
6497         return cfg;
6498     },
6499     initEvents : function()
6500     {
6501         this.el.on('submit', this.onSubmit, this);
6502         // this was added as random key presses on the form where triggering form submit.
6503         this.el.on('keypress', function(e) {
6504             if (e.getCharCode() != 13) {
6505                 return true;
6506             }
6507             // we might need to allow it for textareas.. and some other items.
6508             // check e.getTarget().
6509             
6510             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6511                 return true;
6512             }
6513         
6514             Roo.log("keypress blocked");
6515             
6516             e.preventDefault();
6517             return false;
6518         });
6519         
6520     },
6521     // private
6522     onSubmit : function(e){
6523         e.stopEvent();
6524     },
6525     
6526      /**
6527      * Returns true if client-side validation on the form is successful.
6528      * @return Boolean
6529      */
6530     isValid : function(){
6531         var items = this.getItems();
6532         var valid = true;
6533         items.each(function(f){
6534            if(!f.validate()){
6535                valid = false;
6536                
6537            }
6538         });
6539         return valid;
6540     },
6541     /**
6542      * Returns true if any fields in this form have changed since their original load.
6543      * @return Boolean
6544      */
6545     isDirty : function(){
6546         var dirty = false;
6547         var items = this.getItems();
6548         items.each(function(f){
6549            if(f.isDirty()){
6550                dirty = true;
6551                return false;
6552            }
6553            return true;
6554         });
6555         return dirty;
6556     },
6557      /**
6558      * Performs a predefined action (submit or load) or custom actions you define on this form.
6559      * @param {String} actionName The name of the action type
6560      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6561      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6562      * accept other config options):
6563      * <pre>
6564 Property          Type             Description
6565 ----------------  ---------------  ----------------------------------------------------------------------------------
6566 url               String           The url for the action (defaults to the form's url)
6567 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6568 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6569 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6570                                    validate the form on the client (defaults to false)
6571      * </pre>
6572      * @return {BasicForm} this
6573      */
6574     doAction : function(action, options){
6575         if(typeof action == 'string'){
6576             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6577         }
6578         if(this.fireEvent('beforeaction', this, action) !== false){
6579             this.beforeAction(action);
6580             action.run.defer(100, action);
6581         }
6582         return this;
6583     },
6584     
6585     // private
6586     beforeAction : function(action){
6587         var o = action.options;
6588         
6589         if(this.loadMask){
6590             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6591         }
6592         // not really supported yet.. ??
6593         
6594         //if(this.waitMsgTarget === true){
6595         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6596         //}else if(this.waitMsgTarget){
6597         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6598         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6599         //}else {
6600         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6601        // }
6602          
6603     },
6604
6605     // private
6606     afterAction : function(action, success){
6607         this.activeAction = null;
6608         var o = action.options;
6609         
6610         //if(this.waitMsgTarget === true){
6611             this.el.unmask();
6612         //}else if(this.waitMsgTarget){
6613         //    this.waitMsgTarget.unmask();
6614         //}else{
6615         //    Roo.MessageBox.updateProgress(1);
6616         //    Roo.MessageBox.hide();
6617        // }
6618         // 
6619         if(success){
6620             if(o.reset){
6621                 this.reset();
6622             }
6623             Roo.callback(o.success, o.scope, [this, action]);
6624             this.fireEvent('actioncomplete', this, action);
6625             
6626         }else{
6627             
6628             // failure condition..
6629             // we have a scenario where updates need confirming.
6630             // eg. if a locking scenario exists..
6631             // we look for { errors : { needs_confirm : true }} in the response.
6632             if (
6633                 (typeof(action.result) != 'undefined')  &&
6634                 (typeof(action.result.errors) != 'undefined')  &&
6635                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6636            ){
6637                 var _t = this;
6638                 Roo.log("not supported yet");
6639                  /*
6640                 
6641                 Roo.MessageBox.confirm(
6642                     "Change requires confirmation",
6643                     action.result.errorMsg,
6644                     function(r) {
6645                         if (r != 'yes') {
6646                             return;
6647                         }
6648                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6649                     }
6650                     
6651                 );
6652                 */
6653                 
6654                 
6655                 return;
6656             }
6657             
6658             Roo.callback(o.failure, o.scope, [this, action]);
6659             // show an error message if no failed handler is set..
6660             if (!this.hasListener('actionfailed')) {
6661                 Roo.log("need to add dialog support");
6662                 /*
6663                 Roo.MessageBox.alert("Error",
6664                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6665                         action.result.errorMsg :
6666                         "Saving Failed, please check your entries or try again"
6667                 );
6668                 */
6669             }
6670             
6671             this.fireEvent('actionfailed', this, action);
6672         }
6673         
6674     },
6675     /**
6676      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6677      * @param {String} id The value to search for
6678      * @return Field
6679      */
6680     findField : function(id){
6681         var items = this.getItems();
6682         var field = items.get(id);
6683         if(!field){
6684              items.each(function(f){
6685                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6686                     field = f;
6687                     return false;
6688                 }
6689                 return true;
6690             });
6691         }
6692         return field || null;
6693     },
6694      /**
6695      * Mark fields in this form invalid in bulk.
6696      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6697      * @return {BasicForm} this
6698      */
6699     markInvalid : function(errors){
6700         if(errors instanceof Array){
6701             for(var i = 0, len = errors.length; i < len; i++){
6702                 var fieldError = errors[i];
6703                 var f = this.findField(fieldError.id);
6704                 if(f){
6705                     f.markInvalid(fieldError.msg);
6706                 }
6707             }
6708         }else{
6709             var field, id;
6710             for(id in errors){
6711                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6712                     field.markInvalid(errors[id]);
6713                 }
6714             }
6715         }
6716         //Roo.each(this.childForms || [], function (f) {
6717         //    f.markInvalid(errors);
6718         //});
6719         
6720         return this;
6721     },
6722
6723     /**
6724      * Set values for fields in this form in bulk.
6725      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6726      * @return {BasicForm} this
6727      */
6728     setValues : function(values){
6729         if(values instanceof Array){ // array of objects
6730             for(var i = 0, len = values.length; i < len; i++){
6731                 var v = values[i];
6732                 var f = this.findField(v.id);
6733                 if(f){
6734                     f.setValue(v.value);
6735                     if(this.trackResetOnLoad){
6736                         f.originalValue = f.getValue();
6737                     }
6738                 }
6739             }
6740         }else{ // object hash
6741             var field, id;
6742             for(id in values){
6743                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6744                     
6745                     if (field.setFromData && 
6746                         field.valueField && 
6747                         field.displayField &&
6748                         // combos' with local stores can 
6749                         // be queried via setValue()
6750                         // to set their value..
6751                         (field.store && !field.store.isLocal)
6752                         ) {
6753                         // it's a combo
6754                         var sd = { };
6755                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6756                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6757                         field.setFromData(sd);
6758                         
6759                     } else {
6760                         field.setValue(values[id]);
6761                     }
6762                     
6763                     
6764                     if(this.trackResetOnLoad){
6765                         field.originalValue = field.getValue();
6766                     }
6767                 }
6768             }
6769         }
6770          
6771         //Roo.each(this.childForms || [], function (f) {
6772         //    f.setValues(values);
6773         //});
6774                 
6775         return this;
6776     },
6777
6778     /**
6779      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6780      * they are returned as an array.
6781      * @param {Boolean} asString
6782      * @return {Object}
6783      */
6784     getValues : function(asString){
6785         //if (this.childForms) {
6786             // copy values from the child forms
6787         //    Roo.each(this.childForms, function (f) {
6788         //        this.setValues(f.getValues());
6789         //    }, this);
6790         //}
6791         
6792         
6793         
6794         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6795         if(asString === true){
6796             return fs;
6797         }
6798         return Roo.urlDecode(fs);
6799     },
6800     
6801     /**
6802      * Returns the fields in this form as an object with key/value pairs. 
6803      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6804      * @return {Object}
6805      */
6806     getFieldValues : function(with_hidden)
6807     {
6808         var items = this.getItems();
6809         var ret = {};
6810         items.each(function(f){
6811             if (!f.getName()) {
6812                 return;
6813             }
6814             var v = f.getValue();
6815             if (f.inputType =='radio') {
6816                 if (typeof(ret[f.getName()]) == 'undefined') {
6817                     ret[f.getName()] = ''; // empty..
6818                 }
6819                 
6820                 if (!f.el.dom.checked) {
6821                     return;
6822                     
6823                 }
6824                 v = f.el.dom.value;
6825                 
6826             }
6827             
6828             // not sure if this supported any more..
6829             if ((typeof(v) == 'object') && f.getRawValue) {
6830                 v = f.getRawValue() ; // dates..
6831             }
6832             // combo boxes where name != hiddenName...
6833             if (f.name != f.getName()) {
6834                 ret[f.name] = f.getRawValue();
6835             }
6836             ret[f.getName()] = v;
6837         });
6838         
6839         return ret;
6840     },
6841
6842     /**
6843      * Clears all invalid messages in this form.
6844      * @return {BasicForm} this
6845      */
6846     clearInvalid : function(){
6847         var items = this.getItems();
6848         
6849         items.each(function(f){
6850            f.clearInvalid();
6851         });
6852         
6853         
6854         
6855         return this;
6856     },
6857
6858     /**
6859      * Resets this form.
6860      * @return {BasicForm} this
6861      */
6862     reset : function(){
6863         var items = this.getItems();
6864         items.each(function(f){
6865             f.reset();
6866         });
6867         
6868         Roo.each(this.childForms || [], function (f) {
6869             f.reset();
6870         });
6871        
6872         
6873         return this;
6874     },
6875     getItems : function()
6876     {
6877         var r=new Roo.util.MixedCollection(false, function(o){
6878             return o.id || (o.id = Roo.id());
6879         });
6880         var iter = function(el) {
6881             if (el.inputEl) {
6882                 r.add(el);
6883             }
6884             if (!el.items) {
6885                 return;
6886             }
6887             Roo.each(el.items,function(e) {
6888                 iter(e);
6889             });
6890             
6891             
6892         };
6893         iter(this);
6894         return r;
6895         
6896         
6897         
6898         
6899     }
6900     
6901 });
6902
6903  
6904 /*
6905  * Based on:
6906  * Ext JS Library 1.1.1
6907  * Copyright(c) 2006-2007, Ext JS, LLC.
6908  *
6909  * Originally Released Under LGPL - original licence link has changed is not relivant.
6910  *
6911  * Fork - LGPL
6912  * <script type="text/javascript">
6913  */
6914 /**
6915  * @class Roo.form.VTypes
6916  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6917  * @singleton
6918  */
6919 Roo.form.VTypes = function(){
6920     // closure these in so they are only created once.
6921     var alpha = /^[a-zA-Z_]+$/;
6922     var alphanum = /^[a-zA-Z0-9_]+$/;
6923     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6924     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6925
6926     // All these messages and functions are configurable
6927     return {
6928         /**
6929          * The function used to validate email addresses
6930          * @param {String} value The email address
6931          */
6932         'email' : function(v){
6933             return email.test(v);
6934         },
6935         /**
6936          * The error text to display when the email validation function returns false
6937          * @type String
6938          */
6939         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6940         /**
6941          * The keystroke filter mask to be applied on email input
6942          * @type RegExp
6943          */
6944         'emailMask' : /[a-z0-9_\.\-@]/i,
6945
6946         /**
6947          * The function used to validate URLs
6948          * @param {String} value The URL
6949          */
6950         'url' : function(v){
6951             return url.test(v);
6952         },
6953         /**
6954          * The error text to display when the url validation function returns false
6955          * @type String
6956          */
6957         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6958         
6959         /**
6960          * The function used to validate alpha values
6961          * @param {String} value The value
6962          */
6963         'alpha' : function(v){
6964             return alpha.test(v);
6965         },
6966         /**
6967          * The error text to display when the alpha validation function returns false
6968          * @type String
6969          */
6970         'alphaText' : 'This field should only contain letters and _',
6971         /**
6972          * The keystroke filter mask to be applied on alpha input
6973          * @type RegExp
6974          */
6975         'alphaMask' : /[a-z_]/i,
6976
6977         /**
6978          * The function used to validate alphanumeric values
6979          * @param {String} value The value
6980          */
6981         'alphanum' : function(v){
6982             return alphanum.test(v);
6983         },
6984         /**
6985          * The error text to display when the alphanumeric validation function returns false
6986          * @type String
6987          */
6988         'alphanumText' : 'This field should only contain letters, numbers and _',
6989         /**
6990          * The keystroke filter mask to be applied on alphanumeric input
6991          * @type RegExp
6992          */
6993         'alphanumMask' : /[a-z0-9_]/i
6994     };
6995 }();/*
6996  * - LGPL
6997  *
6998  * Input
6999  * 
7000  */
7001
7002 /**
7003  * @class Roo.bootstrap.Input
7004  * @extends Roo.bootstrap.Component
7005  * Bootstrap Input class
7006  * @cfg {Boolean} disabled is it disabled
7007  * @cfg {String} fieldLabel - the label associated
7008  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7009  * @cfg {String} name name of the input
7010  * @cfg {string} fieldLabel - the label associated
7011  * @cfg {string}  inputType - input / file submit ...
7012  * @cfg {string} placeholder - placeholder to put in text.
7013  * @cfg {string}  before - input group add on before
7014  * @cfg {string} after - input group add on after
7015  * @cfg {string} size - (lg|sm) or leave empty..
7016  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7017  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7018  * @cfg {Number} md colspan out of 12 for computer-sized screens
7019  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7020  * @cfg {string} value default value of the input
7021  * @cfg {Number} labelWidth set the width of label (0-12)
7022  * @cfg {String} labelAlign (top|left)
7023  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7024  * @cfg {String} align (left|center|right) Default left
7025  * 
7026  * 
7027  * @constructor
7028  * Create a new Input
7029  * @param {Object} config The config object
7030  */
7031
7032 Roo.bootstrap.Input = function(config){
7033     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7034    
7035         this.addEvents({
7036             /**
7037              * @event focus
7038              * Fires when this field receives input focus.
7039              * @param {Roo.form.Field} this
7040              */
7041             focus : true,
7042             /**
7043              * @event blur
7044              * Fires when this field loses input focus.
7045              * @param {Roo.form.Field} this
7046              */
7047             blur : true,
7048             /**
7049              * @event specialkey
7050              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7051              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7052              * @param {Roo.form.Field} this
7053              * @param {Roo.EventObject} e The event object
7054              */
7055             specialkey : true,
7056             /**
7057              * @event change
7058              * Fires just before the field blurs if the field value has changed.
7059              * @param {Roo.form.Field} this
7060              * @param {Mixed} newValue The new value
7061              * @param {Mixed} oldValue The original value
7062              */
7063             change : true,
7064             /**
7065              * @event invalid
7066              * Fires after the field has been marked as invalid.
7067              * @param {Roo.form.Field} this
7068              * @param {String} msg The validation message
7069              */
7070             invalid : true,
7071             /**
7072              * @event valid
7073              * Fires after the field has been validated with no errors.
7074              * @param {Roo.form.Field} this
7075              */
7076             valid : true,
7077              /**
7078              * @event keyup
7079              * Fires after the key up
7080              * @param {Roo.form.Field} this
7081              * @param {Roo.EventObject}  e The event Object
7082              */
7083             keyup : true
7084         });
7085 };
7086
7087 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7088      /**
7089      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7090       automatic validation (defaults to "keyup").
7091      */
7092     validationEvent : "keyup",
7093      /**
7094      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7095      */
7096     validateOnBlur : true,
7097     /**
7098      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7099      */
7100     validationDelay : 250,
7101      /**
7102      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7103      */
7104     focusClass : "x-form-focus",  // not needed???
7105     
7106        
7107     /**
7108      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7109      */
7110     invalidClass : "has-error",
7111     
7112     /**
7113      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7114      */
7115     selectOnFocus : false,
7116     
7117      /**
7118      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7119      */
7120     maskRe : null,
7121        /**
7122      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7123      */
7124     vtype : null,
7125     
7126       /**
7127      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7128      */
7129     disableKeyFilter : false,
7130     
7131        /**
7132      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7133      */
7134     disabled : false,
7135      /**
7136      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7137      */
7138     allowBlank : true,
7139     /**
7140      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7141      */
7142     blankText : "This field is required",
7143     
7144      /**
7145      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7146      */
7147     minLength : 0,
7148     /**
7149      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7150      */
7151     maxLength : Number.MAX_VALUE,
7152     /**
7153      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7154      */
7155     minLengthText : "The minimum length for this field is {0}",
7156     /**
7157      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7158      */
7159     maxLengthText : "The maximum length for this field is {0}",
7160   
7161     
7162     /**
7163      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7164      * If available, this function will be called only after the basic validators all return true, and will be passed the
7165      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7166      */
7167     validator : null,
7168     /**
7169      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7170      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7171      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7172      */
7173     regex : null,
7174     /**
7175      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7176      */
7177     regexText : "",
7178     
7179     
7180     
7181     fieldLabel : '',
7182     inputType : 'text',
7183     
7184     name : false,
7185     placeholder: false,
7186     before : false,
7187     after : false,
7188     size : false,
7189     // private
7190     hasFocus : false,
7191     preventMark: false,
7192     isFormField : true,
7193     value : '',
7194     labelWidth : 2,
7195     labelAlign : false,
7196     readOnly : false,
7197     align : false,
7198     formatedValue : false,
7199     
7200     parentLabelAlign : function()
7201     {
7202         var parent = this;
7203         while (parent.parent()) {
7204             parent = parent.parent();
7205             if (typeof(parent.labelAlign) !='undefined') {
7206                 return parent.labelAlign;
7207             }
7208         }
7209         return 'left';
7210         
7211     },
7212     
7213     getAutoCreate : function(){
7214         
7215         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7216         
7217         var id = Roo.id();
7218         
7219         var cfg = {};
7220         
7221         if(this.inputType != 'hidden'){
7222             cfg.cls = 'form-group' //input-group
7223         }
7224         
7225         var input =  {
7226             tag: 'input',
7227             id : id,
7228             type : this.inputType,
7229             value : this.value,
7230             cls : 'form-control',
7231             placeholder : this.placeholder || ''
7232             
7233         };
7234         
7235         if(this.align){
7236             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7237         }
7238         
7239         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7240             input.maxLength = this.maxLength;
7241         }
7242         
7243         if (this.disabled) {
7244             input.disabled=true;
7245         }
7246         
7247         if (this.readOnly) {
7248             input.readonly=true;
7249         }
7250         
7251         if (this.name) {
7252             input.name = this.name;
7253         }
7254         if (this.size) {
7255             input.cls += ' input-' + this.size;
7256         }
7257         var settings=this;
7258         ['xs','sm','md','lg'].map(function(size){
7259             if (settings[size]) {
7260                 cfg.cls += ' col-' + size + '-' + settings[size];
7261             }
7262         });
7263         
7264         var inputblock = input;
7265         
7266         if (this.before || this.after) {
7267             
7268             inputblock = {
7269                 cls : 'input-group',
7270                 cn :  [] 
7271             };
7272             if (this.before && typeof(this.before) == 'string') {
7273                 
7274                 inputblock.cn.push({
7275                     tag :'span',
7276                     cls : 'roo-input-before input-group-addon',
7277                     html : this.before
7278                 });
7279             }
7280             if (this.before && typeof(this.before) == 'object') {
7281                 this.before = Roo.factory(this.before);
7282                 Roo.log(this.before);
7283                 inputblock.cn.push({
7284                     tag :'span',
7285                     cls : 'roo-input-before input-group-' +
7286                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7287                 });
7288             }
7289             
7290             inputblock.cn.push(input);
7291             
7292             if (this.after && typeof(this.after) == 'string') {
7293                 inputblock.cn.push({
7294                     tag :'span',
7295                     cls : 'roo-input-after input-group-addon',
7296                     html : this.after
7297                 });
7298             }
7299             if (this.after && typeof(this.after) == 'object') {
7300                 this.after = Roo.factory(this.after);
7301                 Roo.log(this.after);
7302                 inputblock.cn.push({
7303                     tag :'span',
7304                     cls : 'roo-input-after input-group-' +
7305                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7306                 });
7307             }
7308         };
7309         
7310         if (align ==='left' && this.fieldLabel.length) {
7311                 Roo.log("left and has label");
7312                 cfg.cn = [
7313                     
7314                     {
7315                         tag: 'label',
7316                         'for' :  id,
7317                         cls : 'control-label col-sm-' + this.labelWidth,
7318                         html : this.fieldLabel
7319                         
7320                     },
7321                     {
7322                         cls : "col-sm-" + (12 - this.labelWidth), 
7323                         cn: [
7324                             inputblock
7325                         ]
7326                     }
7327                     
7328                 ];
7329         } else if ( this.fieldLabel.length) {
7330                 Roo.log(" label");
7331                  cfg.cn = [
7332                    
7333                     {
7334                         tag: 'label',
7335                         //cls : 'input-group-addon',
7336                         html : this.fieldLabel
7337                         
7338                     },
7339                     
7340                     inputblock
7341                     
7342                 ];
7343
7344         } else {
7345             
7346                 Roo.log(" no label && no align");
7347                 cfg.cn = [
7348                     
7349                         inputblock
7350                     
7351                 ];
7352                 
7353                 
7354         };
7355         Roo.log('input-parentType: ' + this.parentType);
7356         
7357         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7358            cfg.cls += ' navbar-form';
7359            Roo.log(cfg);
7360         }
7361         
7362         return cfg;
7363         
7364     },
7365     /**
7366      * return the real input element.
7367      */
7368     inputEl: function ()
7369     {
7370         return this.el.select('input.form-control',true).first();
7371     },
7372     
7373     tooltipEl : function()
7374     {
7375         return this.inputEl();
7376     },
7377     
7378     setDisabled : function(v)
7379     {
7380         var i  = this.inputEl().dom;
7381         if (!v) {
7382             i.removeAttribute('disabled');
7383             return;
7384             
7385         }
7386         i.setAttribute('disabled','true');
7387     },
7388     initEvents : function()
7389     {
7390           
7391         this.inputEl().on("keydown" , this.fireKey,  this);
7392         this.inputEl().on("focus", this.onFocus,  this);
7393         this.inputEl().on("blur", this.onBlur,  this);
7394         
7395         this.inputEl().relayEvent('keyup', this);
7396
7397         // reference to original value for reset
7398         this.originalValue = this.getValue();
7399         //Roo.form.TextField.superclass.initEvents.call(this);
7400         if(this.validationEvent == 'keyup'){
7401             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7402             this.inputEl().on('keyup', this.filterValidation, this);
7403         }
7404         else if(this.validationEvent !== false){
7405             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7406         }
7407         
7408         if(this.selectOnFocus){
7409             this.on("focus", this.preFocus, this);
7410             
7411         }
7412         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7413             this.inputEl().on("keypress", this.filterKeys, this);
7414         }
7415        /* if(this.grow){
7416             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7417             this.el.on("click", this.autoSize,  this);
7418         }
7419         */
7420         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7421             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7422         }
7423         
7424         if (typeof(this.before) == 'object') {
7425             this.before.render(this.el.select('.roo-input-before',true).first());
7426         }
7427         if (typeof(this.after) == 'object') {
7428             this.after.render(this.el.select('.roo-input-after',true).first());
7429         }
7430         
7431         
7432     },
7433     filterValidation : function(e){
7434         if(!e.isNavKeyPress()){
7435             this.validationTask.delay(this.validationDelay);
7436         }
7437     },
7438      /**
7439      * Validates the field value
7440      * @return {Boolean} True if the value is valid, else false
7441      */
7442     validate : function(){
7443         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7444         if(this.disabled || this.validateValue(this.getRawValue())){
7445             this.clearInvalid();
7446             return true;
7447         }
7448         return false;
7449     },
7450     
7451     
7452     /**
7453      * Validates a value according to the field's validation rules and marks the field as invalid
7454      * if the validation fails
7455      * @param {Mixed} value The value to validate
7456      * @return {Boolean} True if the value is valid, else false
7457      */
7458     validateValue : function(value){
7459         if(value.length < 1)  { // if it's blank
7460              if(this.allowBlank){
7461                 this.clearInvalid();
7462                 return true;
7463              }else{
7464                 this.markInvalid(this.blankText);
7465                 return false;
7466              }
7467         }
7468         if(value.length < this.minLength){
7469             this.markInvalid(String.format(this.minLengthText, this.minLength));
7470             return false;
7471         }
7472         if(value.length > this.maxLength){
7473             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7474             return false;
7475         }
7476         if(this.vtype){
7477             var vt = Roo.form.VTypes;
7478             if(!vt[this.vtype](value, this)){
7479                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7480                 return false;
7481             }
7482         }
7483         if(typeof this.validator == "function"){
7484             var msg = this.validator(value);
7485             if(msg !== true){
7486                 this.markInvalid(msg);
7487                 return false;
7488             }
7489         }
7490         if(this.regex && !this.regex.test(value)){
7491             this.markInvalid(this.regexText);
7492             return false;
7493         }
7494         return true;
7495     },
7496
7497     
7498     
7499      // private
7500     fireKey : function(e){
7501         //Roo.log('field ' + e.getKey());
7502         if(e.isNavKeyPress()){
7503             this.fireEvent("specialkey", this, e);
7504         }
7505     },
7506     focus : function (selectText){
7507         if(this.rendered){
7508             this.inputEl().focus();
7509             if(selectText === true){
7510                 this.inputEl().dom.select();
7511             }
7512         }
7513         return this;
7514     } ,
7515     
7516     onFocus : function(){
7517         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7518            // this.el.addClass(this.focusClass);
7519         }
7520         if(!this.hasFocus){
7521             this.hasFocus = true;
7522             this.startValue = this.getValue();
7523             this.fireEvent("focus", this);
7524         }
7525     },
7526     
7527     beforeBlur : Roo.emptyFn,
7528
7529     
7530     // private
7531     onBlur : function(){
7532         this.beforeBlur();
7533         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7534             //this.el.removeClass(this.focusClass);
7535         }
7536         this.hasFocus = false;
7537         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7538             this.validate();
7539         }
7540         var v = this.getValue();
7541         if(String(v) !== String(this.startValue)){
7542             this.fireEvent('change', this, v, this.startValue);
7543         }
7544         this.fireEvent("blur", this);
7545     },
7546     
7547     /**
7548      * Resets the current field value to the originally loaded value and clears any validation messages
7549      */
7550     reset : function(){
7551         this.setValue(this.originalValue);
7552         this.clearInvalid();
7553     },
7554      /**
7555      * Returns the name of the field
7556      * @return {Mixed} name The name field
7557      */
7558     getName: function(){
7559         return this.name;
7560     },
7561      /**
7562      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7563      * @return {Mixed} value The field value
7564      */
7565     getValue : function(){
7566         
7567         var v = this.inputEl().getValue();
7568         
7569         return v;
7570     },
7571     /**
7572      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7573      * @return {Mixed} value The field value
7574      */
7575     getRawValue : function(){
7576         var v = this.inputEl().getValue();
7577         
7578         return v;
7579     },
7580     
7581     /**
7582      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7583      * @param {Mixed} value The value to set
7584      */
7585     setRawValue : function(v){
7586         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7587     },
7588     
7589     selectText : function(start, end){
7590         var v = this.getRawValue();
7591         if(v.length > 0){
7592             start = start === undefined ? 0 : start;
7593             end = end === undefined ? v.length : end;
7594             var d = this.inputEl().dom;
7595             if(d.setSelectionRange){
7596                 d.setSelectionRange(start, end);
7597             }else if(d.createTextRange){
7598                 var range = d.createTextRange();
7599                 range.moveStart("character", start);
7600                 range.moveEnd("character", v.length-end);
7601                 range.select();
7602             }
7603         }
7604     },
7605     
7606     /**
7607      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7608      * @param {Mixed} value The value to set
7609      */
7610     setValue : function(v){
7611         this.value = v;
7612         if(this.rendered){
7613             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7614             this.validate();
7615         }
7616     },
7617     
7618     /*
7619     processValue : function(value){
7620         if(this.stripCharsRe){
7621             var newValue = value.replace(this.stripCharsRe, '');
7622             if(newValue !== value){
7623                 this.setRawValue(newValue);
7624                 return newValue;
7625             }
7626         }
7627         return value;
7628     },
7629   */
7630     preFocus : function(){
7631         
7632         if(this.selectOnFocus){
7633             this.inputEl().dom.select();
7634         }
7635     },
7636     filterKeys : function(e){
7637         var k = e.getKey();
7638         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7639             return;
7640         }
7641         var c = e.getCharCode(), cc = String.fromCharCode(c);
7642         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7643             return;
7644         }
7645         if(!this.maskRe.test(cc)){
7646             e.stopEvent();
7647         }
7648     },
7649      /**
7650      * Clear any invalid styles/messages for this field
7651      */
7652     clearInvalid : function(){
7653         
7654         if(!this.el || this.preventMark){ // not rendered
7655             return;
7656         }
7657         this.el.removeClass(this.invalidClass);
7658         /*
7659         switch(this.msgTarget){
7660             case 'qtip':
7661                 this.el.dom.qtip = '';
7662                 break;
7663             case 'title':
7664                 this.el.dom.title = '';
7665                 break;
7666             case 'under':
7667                 if(this.errorEl){
7668                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7669                 }
7670                 break;
7671             case 'side':
7672                 if(this.errorIcon){
7673                     this.errorIcon.dom.qtip = '';
7674                     this.errorIcon.hide();
7675                     this.un('resize', this.alignErrorIcon, this);
7676                 }
7677                 break;
7678             default:
7679                 var t = Roo.getDom(this.msgTarget);
7680                 t.innerHTML = '';
7681                 t.style.display = 'none';
7682                 break;
7683         }
7684         */
7685         this.fireEvent('valid', this);
7686     },
7687      /**
7688      * Mark this field as invalid
7689      * @param {String} msg The validation message
7690      */
7691     markInvalid : function(msg){
7692         if(!this.el  || this.preventMark){ // not rendered
7693             return;
7694         }
7695         this.el.addClass(this.invalidClass);
7696         /*
7697         msg = msg || this.invalidText;
7698         switch(this.msgTarget){
7699             case 'qtip':
7700                 this.el.dom.qtip = msg;
7701                 this.el.dom.qclass = 'x-form-invalid-tip';
7702                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7703                     Roo.QuickTips.enable();
7704                 }
7705                 break;
7706             case 'title':
7707                 this.el.dom.title = msg;
7708                 break;
7709             case 'under':
7710                 if(!this.errorEl){
7711                     var elp = this.el.findParent('.x-form-element', 5, true);
7712                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7713                     this.errorEl.setWidth(elp.getWidth(true)-20);
7714                 }
7715                 this.errorEl.update(msg);
7716                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7717                 break;
7718             case 'side':
7719                 if(!this.errorIcon){
7720                     var elp = this.el.findParent('.x-form-element', 5, true);
7721                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7722                 }
7723                 this.alignErrorIcon();
7724                 this.errorIcon.dom.qtip = msg;
7725                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7726                 this.errorIcon.show();
7727                 this.on('resize', this.alignErrorIcon, this);
7728                 break;
7729             default:
7730                 var t = Roo.getDom(this.msgTarget);
7731                 t.innerHTML = msg;
7732                 t.style.display = this.msgDisplay;
7733                 break;
7734         }
7735         */
7736         this.fireEvent('invalid', this, msg);
7737     },
7738     // private
7739     SafariOnKeyDown : function(event)
7740     {
7741         // this is a workaround for a password hang bug on chrome/ webkit.
7742         
7743         var isSelectAll = false;
7744         
7745         if(this.inputEl().dom.selectionEnd > 0){
7746             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7747         }
7748         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7749             event.preventDefault();
7750             this.setValue('');
7751             return;
7752         }
7753         
7754         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
7755             
7756             event.preventDefault();
7757             // this is very hacky as keydown always get's upper case.
7758             //
7759             var cc = String.fromCharCode(event.getCharCode());
7760             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7761             
7762         }
7763     },
7764     adjustWidth : function(tag, w){
7765         tag = tag.toLowerCase();
7766         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7767             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7768                 if(tag == 'input'){
7769                     return w + 2;
7770                 }
7771                 if(tag == 'textarea'){
7772                     return w-2;
7773                 }
7774             }else if(Roo.isOpera){
7775                 if(tag == 'input'){
7776                     return w + 2;
7777                 }
7778                 if(tag == 'textarea'){
7779                     return w-2;
7780                 }
7781             }
7782         }
7783         return w;
7784     }
7785     
7786 });
7787
7788  
7789 /*
7790  * - LGPL
7791  *
7792  * Input
7793  * 
7794  */
7795
7796 /**
7797  * @class Roo.bootstrap.TextArea
7798  * @extends Roo.bootstrap.Input
7799  * Bootstrap TextArea class
7800  * @cfg {Number} cols Specifies the visible width of a text area
7801  * @cfg {Number} rows Specifies the visible number of lines in a text area
7802  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7803  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7804  * @cfg {string} html text
7805  * 
7806  * @constructor
7807  * Create a new TextArea
7808  * @param {Object} config The config object
7809  */
7810
7811 Roo.bootstrap.TextArea = function(config){
7812     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7813    
7814 };
7815
7816 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7817      
7818     cols : false,
7819     rows : 5,
7820     readOnly : false,
7821     warp : 'soft',
7822     resize : false,
7823     value: false,
7824     html: false,
7825     
7826     getAutoCreate : function(){
7827         
7828         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7829         
7830         var id = Roo.id();
7831         
7832         var cfg = {};
7833         
7834         var input =  {
7835             tag: 'textarea',
7836             id : id,
7837             warp : this.warp,
7838             rows : this.rows,
7839             value : this.value || '',
7840             html: this.html || '',
7841             cls : 'form-control',
7842             placeholder : this.placeholder || '' 
7843             
7844         };
7845         
7846         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7847             input.maxLength = this.maxLength;
7848         }
7849         
7850         if(this.resize){
7851             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7852         }
7853         
7854         if(this.cols){
7855             input.cols = this.cols;
7856         }
7857         
7858         if (this.readOnly) {
7859             input.readonly = true;
7860         }
7861         
7862         if (this.name) {
7863             input.name = this.name;
7864         }
7865         
7866         if (this.size) {
7867             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7868         }
7869         
7870         var settings=this;
7871         ['xs','sm','md','lg'].map(function(size){
7872             if (settings[size]) {
7873                 cfg.cls += ' col-' + size + '-' + settings[size];
7874             }
7875         });
7876         
7877         var inputblock = input;
7878         
7879         if (this.before || this.after) {
7880             
7881             inputblock = {
7882                 cls : 'input-group',
7883                 cn :  [] 
7884             };
7885             if (this.before) {
7886                 inputblock.cn.push({
7887                     tag :'span',
7888                     cls : 'input-group-addon',
7889                     html : this.before
7890                 });
7891             }
7892             inputblock.cn.push(input);
7893             if (this.after) {
7894                 inputblock.cn.push({
7895                     tag :'span',
7896                     cls : 'input-group-addon',
7897                     html : this.after
7898                 });
7899             }
7900             
7901         }
7902         
7903         if (align ==='left' && this.fieldLabel.length) {
7904                 Roo.log("left and has label");
7905                 cfg.cn = [
7906                     
7907                     {
7908                         tag: 'label',
7909                         'for' :  id,
7910                         cls : 'control-label col-sm-' + this.labelWidth,
7911                         html : this.fieldLabel
7912                         
7913                     },
7914                     {
7915                         cls : "col-sm-" + (12 - this.labelWidth), 
7916                         cn: [
7917                             inputblock
7918                         ]
7919                     }
7920                     
7921                 ];
7922         } else if ( this.fieldLabel.length) {
7923                 Roo.log(" label");
7924                  cfg.cn = [
7925                    
7926                     {
7927                         tag: 'label',
7928                         //cls : 'input-group-addon',
7929                         html : this.fieldLabel
7930                         
7931                     },
7932                     
7933                     inputblock
7934                     
7935                 ];
7936
7937         } else {
7938             
7939                    Roo.log(" no label && no align");
7940                 cfg.cn = [
7941                     
7942                         inputblock
7943                     
7944                 ];
7945                 
7946                 
7947         }
7948         
7949         if (this.disabled) {
7950             input.disabled=true;
7951         }
7952         
7953         return cfg;
7954         
7955     },
7956     /**
7957      * return the real textarea element.
7958      */
7959     inputEl: function ()
7960     {
7961         return this.el.select('textarea.form-control',true).first();
7962     }
7963 });
7964
7965  
7966 /*
7967  * - LGPL
7968  *
7969  * trigger field - base class for combo..
7970  * 
7971  */
7972  
7973 /**
7974  * @class Roo.bootstrap.TriggerField
7975  * @extends Roo.bootstrap.Input
7976  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7977  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7978  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7979  * for which you can provide a custom implementation.  For example:
7980  * <pre><code>
7981 var trigger = new Roo.bootstrap.TriggerField();
7982 trigger.onTriggerClick = myTriggerFn;
7983 trigger.applyTo('my-field');
7984 </code></pre>
7985  *
7986  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7987  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7988  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7989  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7990  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
7991
7992  * @constructor
7993  * Create a new TriggerField.
7994  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7995  * to the base TextField)
7996  */
7997 Roo.bootstrap.TriggerField = function(config){
7998     this.mimicing = false;
7999     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8000 };
8001
8002 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8003     /**
8004      * @cfg {String} triggerClass A CSS class to apply to the trigger
8005      */
8006      /**
8007      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8008      */
8009     hideTrigger:false,
8010
8011     /** @cfg {Boolean} grow @hide */
8012     /** @cfg {Number} growMin @hide */
8013     /** @cfg {Number} growMax @hide */
8014
8015     /**
8016      * @hide 
8017      * @method
8018      */
8019     autoSize: Roo.emptyFn,
8020     // private
8021     monitorTab : true,
8022     // private
8023     deferHeight : true,
8024
8025     
8026     actionMode : 'wrap',
8027     
8028     caret : false,
8029     
8030     
8031     getAutoCreate : function(){
8032        
8033         var align = this.labelAlign || this.parentLabelAlign();
8034         
8035         var id = Roo.id();
8036         
8037         var cfg = {
8038             cls: 'form-group' //input-group
8039         };
8040         
8041         
8042         var input =  {
8043             tag: 'input',
8044             id : id,
8045             type : this.inputType,
8046             cls : 'form-control',
8047             autocomplete: 'false',
8048             placeholder : this.placeholder || '' 
8049             
8050         };
8051         if (this.name) {
8052             input.name = this.name;
8053         }
8054         if (this.size) {
8055             input.cls += ' input-' + this.size;
8056         }
8057         
8058         if (this.disabled) {
8059             input.disabled=true;
8060         }
8061         
8062         var inputblock = input;
8063         
8064         if (this.before || this.after) {
8065             
8066             inputblock = {
8067                 cls : 'input-group',
8068                 cn :  [] 
8069             };
8070             if (this.before) {
8071                 inputblock.cn.push({
8072                     tag :'span',
8073                     cls : 'input-group-addon',
8074                     html : this.before
8075                 });
8076             }
8077             inputblock.cn.push(input);
8078             if (this.after) {
8079                 inputblock.cn.push({
8080                     tag :'span',
8081                     cls : 'input-group-addon',
8082                     html : this.after
8083                 });
8084             }
8085             
8086         };
8087         
8088         var box = {
8089             tag: 'div',
8090             cn: [
8091                 {
8092                     tag: 'input',
8093                     type : 'hidden',
8094                     cls: 'form-hidden-field'
8095                 },
8096                 inputblock
8097             ]
8098             
8099         };
8100         
8101         if(this.multiple){
8102             Roo.log('multiple');
8103             
8104             box = {
8105                 tag: 'div',
8106                 cn: [
8107                     {
8108                         tag: 'input',
8109                         type : 'hidden',
8110                         cls: 'form-hidden-field'
8111                     },
8112                     {
8113                         tag: 'ul',
8114                         cls: 'select2-choices',
8115                         cn:[
8116                             {
8117                                 tag: 'li',
8118                                 cls: 'select2-search-field',
8119                                 cn: [
8120
8121                                     inputblock
8122                                 ]
8123                             }
8124                         ]
8125                     }
8126                 ]
8127             }
8128         };
8129         
8130         var combobox = {
8131             cls: 'select2-container input-group',
8132             cn: [
8133                 box
8134 //                {
8135 //                    tag: 'ul',
8136 //                    cls: 'typeahead typeahead-long dropdown-menu',
8137 //                    style: 'display:none'
8138 //                }
8139             ]
8140         };
8141         
8142         if(!this.multiple && this.showToggleBtn){
8143             
8144             var caret = {
8145                         tag: 'span',
8146                         cls: 'caret'
8147              };
8148             if (this.caret != false) {
8149                 caret = {
8150                      tag: 'i',
8151                      cls: 'fa fa-' + this.caret
8152                 };
8153                 
8154             }
8155             
8156             combobox.cn.push({
8157                 tag :'span',
8158                 cls : 'input-group-addon btn dropdown-toggle',
8159                 cn : [
8160                     caret,
8161                     {
8162                         tag: 'span',
8163                         cls: 'combobox-clear',
8164                         cn  : [
8165                             {
8166                                 tag : 'i',
8167                                 cls: 'icon-remove'
8168                             }
8169                         ]
8170                     }
8171                 ]
8172
8173             })
8174         }
8175         
8176         if(this.multiple){
8177             combobox.cls += ' select2-container-multi';
8178         }
8179         
8180         if (align ==='left' && this.fieldLabel.length) {
8181             
8182                 Roo.log("left and has label");
8183                 cfg.cn = [
8184                     
8185                     {
8186                         tag: 'label',
8187                         'for' :  id,
8188                         cls : 'control-label col-sm-' + this.labelWidth,
8189                         html : this.fieldLabel
8190                         
8191                     },
8192                     {
8193                         cls : "col-sm-" + (12 - this.labelWidth), 
8194                         cn: [
8195                             combobox
8196                         ]
8197                     }
8198                     
8199                 ];
8200         } else if ( this.fieldLabel.length) {
8201                 Roo.log(" label");
8202                  cfg.cn = [
8203                    
8204                     {
8205                         tag: 'label',
8206                         //cls : 'input-group-addon',
8207                         html : this.fieldLabel
8208                         
8209                     },
8210                     
8211                     combobox
8212                     
8213                 ];
8214
8215         } else {
8216             
8217                 Roo.log(" no label && no align");
8218                 cfg = combobox
8219                      
8220                 
8221         }
8222          
8223         var settings=this;
8224         ['xs','sm','md','lg'].map(function(size){
8225             if (settings[size]) {
8226                 cfg.cls += ' col-' + size + '-' + settings[size];
8227             }
8228         });
8229         
8230         return cfg;
8231         
8232     },
8233     
8234     
8235     
8236     // private
8237     onResize : function(w, h){
8238 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8239 //        if(typeof w == 'number'){
8240 //            var x = w - this.trigger.getWidth();
8241 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8242 //            this.trigger.setStyle('left', x+'px');
8243 //        }
8244     },
8245
8246     // private
8247     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8248
8249     // private
8250     getResizeEl : function(){
8251         return this.inputEl();
8252     },
8253
8254     // private
8255     getPositionEl : function(){
8256         return this.inputEl();
8257     },
8258
8259     // private
8260     alignErrorIcon : function(){
8261         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8262     },
8263
8264     // private
8265     initEvents : function(){
8266         
8267         this.createList();
8268         
8269         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8270         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8271         if(!this.multiple && this.showToggleBtn){
8272             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8273             if(this.hideTrigger){
8274                 this.trigger.setDisplayed(false);
8275             }
8276             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8277         }
8278         
8279         if(this.multiple){
8280             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8281         }
8282         
8283         //this.trigger.addClassOnOver('x-form-trigger-over');
8284         //this.trigger.addClassOnClick('x-form-trigger-click');
8285         
8286         //if(!this.width){
8287         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8288         //}
8289     },
8290     
8291     createList : function()
8292     {
8293         this.list = Roo.get(document.body).createChild({
8294             tag: 'ul',
8295             cls: 'typeahead typeahead-long dropdown-menu',
8296             style: 'display:none'
8297         });
8298         
8299         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8300         
8301     },
8302
8303     // private
8304     initTrigger : function(){
8305        
8306     },
8307
8308     // private
8309     onDestroy : function(){
8310         if(this.trigger){
8311             this.trigger.removeAllListeners();
8312           //  this.trigger.remove();
8313         }
8314         //if(this.wrap){
8315         //    this.wrap.remove();
8316         //}
8317         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8318     },
8319
8320     // private
8321     onFocus : function(){
8322         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8323         /*
8324         if(!this.mimicing){
8325             this.wrap.addClass('x-trigger-wrap-focus');
8326             this.mimicing = true;
8327             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8328             if(this.monitorTab){
8329                 this.el.on("keydown", this.checkTab, this);
8330             }
8331         }
8332         */
8333     },
8334
8335     // private
8336     checkTab : function(e){
8337         if(e.getKey() == e.TAB){
8338             this.triggerBlur();
8339         }
8340     },
8341
8342     // private
8343     onBlur : function(){
8344         // do nothing
8345     },
8346
8347     // private
8348     mimicBlur : function(e, t){
8349         /*
8350         if(!this.wrap.contains(t) && this.validateBlur()){
8351             this.triggerBlur();
8352         }
8353         */
8354     },
8355
8356     // private
8357     triggerBlur : function(){
8358         this.mimicing = false;
8359         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8360         if(this.monitorTab){
8361             this.el.un("keydown", this.checkTab, this);
8362         }
8363         //this.wrap.removeClass('x-trigger-wrap-focus');
8364         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8365     },
8366
8367     // private
8368     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8369     validateBlur : function(e, t){
8370         return true;
8371     },
8372
8373     // private
8374     onDisable : function(){
8375         this.inputEl().dom.disabled = true;
8376         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8377         //if(this.wrap){
8378         //    this.wrap.addClass('x-item-disabled');
8379         //}
8380     },
8381
8382     // private
8383     onEnable : function(){
8384         this.inputEl().dom.disabled = false;
8385         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8386         //if(this.wrap){
8387         //    this.el.removeClass('x-item-disabled');
8388         //}
8389     },
8390
8391     // private
8392     onShow : function(){
8393         var ae = this.getActionEl();
8394         
8395         if(ae){
8396             ae.dom.style.display = '';
8397             ae.dom.style.visibility = 'visible';
8398         }
8399     },
8400
8401     // private
8402     
8403     onHide : function(){
8404         var ae = this.getActionEl();
8405         ae.dom.style.display = 'none';
8406     },
8407
8408     /**
8409      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8410      * by an implementing function.
8411      * @method
8412      * @param {EventObject} e
8413      */
8414     onTriggerClick : Roo.emptyFn
8415 });
8416  /*
8417  * Based on:
8418  * Ext JS Library 1.1.1
8419  * Copyright(c) 2006-2007, Ext JS, LLC.
8420  *
8421  * Originally Released Under LGPL - original licence link has changed is not relivant.
8422  *
8423  * Fork - LGPL
8424  * <script type="text/javascript">
8425  */
8426
8427
8428 /**
8429  * @class Roo.data.SortTypes
8430  * @singleton
8431  * Defines the default sorting (casting?) comparison functions used when sorting data.
8432  */
8433 Roo.data.SortTypes = {
8434     /**
8435      * Default sort that does nothing
8436      * @param {Mixed} s The value being converted
8437      * @return {Mixed} The comparison value
8438      */
8439     none : function(s){
8440         return s;
8441     },
8442     
8443     /**
8444      * The regular expression used to strip tags
8445      * @type {RegExp}
8446      * @property
8447      */
8448     stripTagsRE : /<\/?[^>]+>/gi,
8449     
8450     /**
8451      * Strips all HTML tags to sort on text only
8452      * @param {Mixed} s The value being converted
8453      * @return {String} The comparison value
8454      */
8455     asText : function(s){
8456         return String(s).replace(this.stripTagsRE, "");
8457     },
8458     
8459     /**
8460      * Strips all HTML tags to sort on text only - Case insensitive
8461      * @param {Mixed} s The value being converted
8462      * @return {String} The comparison value
8463      */
8464     asUCText : function(s){
8465         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8466     },
8467     
8468     /**
8469      * Case insensitive string
8470      * @param {Mixed} s The value being converted
8471      * @return {String} The comparison value
8472      */
8473     asUCString : function(s) {
8474         return String(s).toUpperCase();
8475     },
8476     
8477     /**
8478      * Date sorting
8479      * @param {Mixed} s The value being converted
8480      * @return {Number} The comparison value
8481      */
8482     asDate : function(s) {
8483         if(!s){
8484             return 0;
8485         }
8486         if(s instanceof Date){
8487             return s.getTime();
8488         }
8489         return Date.parse(String(s));
8490     },
8491     
8492     /**
8493      * Float sorting
8494      * @param {Mixed} s The value being converted
8495      * @return {Float} The comparison value
8496      */
8497     asFloat : function(s) {
8498         var val = parseFloat(String(s).replace(/,/g, ""));
8499         if(isNaN(val)) val = 0;
8500         return val;
8501     },
8502     
8503     /**
8504      * Integer sorting
8505      * @param {Mixed} s The value being converted
8506      * @return {Number} The comparison value
8507      */
8508     asInt : function(s) {
8509         var val = parseInt(String(s).replace(/,/g, ""));
8510         if(isNaN(val)) val = 0;
8511         return val;
8512     }
8513 };/*
8514  * Based on:
8515  * Ext JS Library 1.1.1
8516  * Copyright(c) 2006-2007, Ext JS, LLC.
8517  *
8518  * Originally Released Under LGPL - original licence link has changed is not relivant.
8519  *
8520  * Fork - LGPL
8521  * <script type="text/javascript">
8522  */
8523
8524 /**
8525 * @class Roo.data.Record
8526  * Instances of this class encapsulate both record <em>definition</em> information, and record
8527  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8528  * to access Records cached in an {@link Roo.data.Store} object.<br>
8529  * <p>
8530  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8531  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8532  * objects.<br>
8533  * <p>
8534  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8535  * @constructor
8536  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8537  * {@link #create}. The parameters are the same.
8538  * @param {Array} data An associative Array of data values keyed by the field name.
8539  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8540  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8541  * not specified an integer id is generated.
8542  */
8543 Roo.data.Record = function(data, id){
8544     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8545     this.data = data;
8546 };
8547
8548 /**
8549  * Generate a constructor for a specific record layout.
8550  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8551  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8552  * Each field definition object may contain the following properties: <ul>
8553  * <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,
8554  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8555  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8556  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8557  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8558  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8559  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8560  * this may be omitted.</p></li>
8561  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8562  * <ul><li>auto (Default, implies no conversion)</li>
8563  * <li>string</li>
8564  * <li>int</li>
8565  * <li>float</li>
8566  * <li>boolean</li>
8567  * <li>date</li></ul></p></li>
8568  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8569  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8570  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8571  * by the Reader into an object that will be stored in the Record. It is passed the
8572  * following parameters:<ul>
8573  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8574  * </ul></p></li>
8575  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8576  * </ul>
8577  * <br>usage:<br><pre><code>
8578 var TopicRecord = Roo.data.Record.create(
8579     {name: 'title', mapping: 'topic_title'},
8580     {name: 'author', mapping: 'username'},
8581     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8582     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8583     {name: 'lastPoster', mapping: 'user2'},
8584     {name: 'excerpt', mapping: 'post_text'}
8585 );
8586
8587 var myNewRecord = new TopicRecord({
8588     title: 'Do my job please',
8589     author: 'noobie',
8590     totalPosts: 1,
8591     lastPost: new Date(),
8592     lastPoster: 'Animal',
8593     excerpt: 'No way dude!'
8594 });
8595 myStore.add(myNewRecord);
8596 </code></pre>
8597  * @method create
8598  * @static
8599  */
8600 Roo.data.Record.create = function(o){
8601     var f = function(){
8602         f.superclass.constructor.apply(this, arguments);
8603     };
8604     Roo.extend(f, Roo.data.Record);
8605     var p = f.prototype;
8606     p.fields = new Roo.util.MixedCollection(false, function(field){
8607         return field.name;
8608     });
8609     for(var i = 0, len = o.length; i < len; i++){
8610         p.fields.add(new Roo.data.Field(o[i]));
8611     }
8612     f.getField = function(name){
8613         return p.fields.get(name);  
8614     };
8615     return f;
8616 };
8617
8618 Roo.data.Record.AUTO_ID = 1000;
8619 Roo.data.Record.EDIT = 'edit';
8620 Roo.data.Record.REJECT = 'reject';
8621 Roo.data.Record.COMMIT = 'commit';
8622
8623 Roo.data.Record.prototype = {
8624     /**
8625      * Readonly flag - true if this record has been modified.
8626      * @type Boolean
8627      */
8628     dirty : false,
8629     editing : false,
8630     error: null,
8631     modified: null,
8632
8633     // private
8634     join : function(store){
8635         this.store = store;
8636     },
8637
8638     /**
8639      * Set the named field to the specified value.
8640      * @param {String} name The name of the field to set.
8641      * @param {Object} value The value to set the field to.
8642      */
8643     set : function(name, value){
8644         if(this.data[name] == value){
8645             return;
8646         }
8647         this.dirty = true;
8648         if(!this.modified){
8649             this.modified = {};
8650         }
8651         if(typeof this.modified[name] == 'undefined'){
8652             this.modified[name] = this.data[name];
8653         }
8654         this.data[name] = value;
8655         if(!this.editing && this.store){
8656             this.store.afterEdit(this);
8657         }       
8658     },
8659
8660     /**
8661      * Get the value of the named field.
8662      * @param {String} name The name of the field to get the value of.
8663      * @return {Object} The value of the field.
8664      */
8665     get : function(name){
8666         return this.data[name]; 
8667     },
8668
8669     // private
8670     beginEdit : function(){
8671         this.editing = true;
8672         this.modified = {}; 
8673     },
8674
8675     // private
8676     cancelEdit : function(){
8677         this.editing = false;
8678         delete this.modified;
8679     },
8680
8681     // private
8682     endEdit : function(){
8683         this.editing = false;
8684         if(this.dirty && this.store){
8685             this.store.afterEdit(this);
8686         }
8687     },
8688
8689     /**
8690      * Usually called by the {@link Roo.data.Store} which owns the Record.
8691      * Rejects all changes made to the Record since either creation, or the last commit operation.
8692      * Modified fields are reverted to their original values.
8693      * <p>
8694      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8695      * of reject operations.
8696      */
8697     reject : function(){
8698         var m = this.modified;
8699         for(var n in m){
8700             if(typeof m[n] != "function"){
8701                 this.data[n] = m[n];
8702             }
8703         }
8704         this.dirty = false;
8705         delete this.modified;
8706         this.editing = false;
8707         if(this.store){
8708             this.store.afterReject(this);
8709         }
8710     },
8711
8712     /**
8713      * Usually called by the {@link Roo.data.Store} which owns the Record.
8714      * Commits all changes made to the Record since either creation, or the last commit operation.
8715      * <p>
8716      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8717      * of commit operations.
8718      */
8719     commit : function(){
8720         this.dirty = false;
8721         delete this.modified;
8722         this.editing = false;
8723         if(this.store){
8724             this.store.afterCommit(this);
8725         }
8726     },
8727
8728     // private
8729     hasError : function(){
8730         return this.error != null;
8731     },
8732
8733     // private
8734     clearError : function(){
8735         this.error = null;
8736     },
8737
8738     /**
8739      * Creates a copy of this record.
8740      * @param {String} id (optional) A new record id if you don't want to use this record's id
8741      * @return {Record}
8742      */
8743     copy : function(newId) {
8744         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8745     }
8746 };/*
8747  * Based on:
8748  * Ext JS Library 1.1.1
8749  * Copyright(c) 2006-2007, Ext JS, LLC.
8750  *
8751  * Originally Released Under LGPL - original licence link has changed is not relivant.
8752  *
8753  * Fork - LGPL
8754  * <script type="text/javascript">
8755  */
8756
8757
8758
8759 /**
8760  * @class Roo.data.Store
8761  * @extends Roo.util.Observable
8762  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8763  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8764  * <p>
8765  * 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
8766  * has no knowledge of the format of the data returned by the Proxy.<br>
8767  * <p>
8768  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8769  * instances from the data object. These records are cached and made available through accessor functions.
8770  * @constructor
8771  * Creates a new Store.
8772  * @param {Object} config A config object containing the objects needed for the Store to access data,
8773  * and read the data into Records.
8774  */
8775 Roo.data.Store = function(config){
8776     this.data = new Roo.util.MixedCollection(false);
8777     this.data.getKey = function(o){
8778         return o.id;
8779     };
8780     this.baseParams = {};
8781     // private
8782     this.paramNames = {
8783         "start" : "start",
8784         "limit" : "limit",
8785         "sort" : "sort",
8786         "dir" : "dir",
8787         "multisort" : "_multisort"
8788     };
8789
8790     if(config && config.data){
8791         this.inlineData = config.data;
8792         delete config.data;
8793     }
8794
8795     Roo.apply(this, config);
8796     
8797     if(this.reader){ // reader passed
8798         this.reader = Roo.factory(this.reader, Roo.data);
8799         this.reader.xmodule = this.xmodule || false;
8800         if(!this.recordType){
8801             this.recordType = this.reader.recordType;
8802         }
8803         if(this.reader.onMetaChange){
8804             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8805         }
8806     }
8807
8808     if(this.recordType){
8809         this.fields = this.recordType.prototype.fields;
8810     }
8811     this.modified = [];
8812
8813     this.addEvents({
8814         /**
8815          * @event datachanged
8816          * Fires when the data cache has changed, and a widget which is using this Store
8817          * as a Record cache should refresh its view.
8818          * @param {Store} this
8819          */
8820         datachanged : true,
8821         /**
8822          * @event metachange
8823          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8824          * @param {Store} this
8825          * @param {Object} meta The JSON metadata
8826          */
8827         metachange : true,
8828         /**
8829          * @event add
8830          * Fires when Records have been added to the Store
8831          * @param {Store} this
8832          * @param {Roo.data.Record[]} records The array of Records added
8833          * @param {Number} index The index at which the record(s) were added
8834          */
8835         add : true,
8836         /**
8837          * @event remove
8838          * Fires when a Record has been removed from the Store
8839          * @param {Store} this
8840          * @param {Roo.data.Record} record The Record that was removed
8841          * @param {Number} index The index at which the record was removed
8842          */
8843         remove : true,
8844         /**
8845          * @event update
8846          * Fires when a Record has been updated
8847          * @param {Store} this
8848          * @param {Roo.data.Record} record The Record that was updated
8849          * @param {String} operation The update operation being performed.  Value may be one of:
8850          * <pre><code>
8851  Roo.data.Record.EDIT
8852  Roo.data.Record.REJECT
8853  Roo.data.Record.COMMIT
8854          * </code></pre>
8855          */
8856         update : true,
8857         /**
8858          * @event clear
8859          * Fires when the data cache has been cleared.
8860          * @param {Store} this
8861          */
8862         clear : true,
8863         /**
8864          * @event beforeload
8865          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8866          * the load action will be canceled.
8867          * @param {Store} this
8868          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8869          */
8870         beforeload : true,
8871         /**
8872          * @event beforeloadadd
8873          * Fires after a new set of Records has been loaded.
8874          * @param {Store} this
8875          * @param {Roo.data.Record[]} records The Records that were loaded
8876          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8877          */
8878         beforeloadadd : true,
8879         /**
8880          * @event load
8881          * Fires after a new set of Records has been loaded, before they are added to the store.
8882          * @param {Store} this
8883          * @param {Roo.data.Record[]} records The Records that were loaded
8884          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8885          * @params {Object} return from reader
8886          */
8887         load : true,
8888         /**
8889          * @event loadexception
8890          * Fires if an exception occurs in the Proxy during loading.
8891          * Called with the signature of the Proxy's "loadexception" event.
8892          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8893          * 
8894          * @param {Proxy} 
8895          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8896          * @param {Object} load options 
8897          * @param {Object} jsonData from your request (normally this contains the Exception)
8898          */
8899         loadexception : true
8900     });
8901     
8902     if(this.proxy){
8903         this.proxy = Roo.factory(this.proxy, Roo.data);
8904         this.proxy.xmodule = this.xmodule || false;
8905         this.relayEvents(this.proxy,  ["loadexception"]);
8906     }
8907     this.sortToggle = {};
8908     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8909
8910     Roo.data.Store.superclass.constructor.call(this);
8911
8912     if(this.inlineData){
8913         this.loadData(this.inlineData);
8914         delete this.inlineData;
8915     }
8916 };
8917
8918 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8919      /**
8920     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8921     * without a remote query - used by combo/forms at present.
8922     */
8923     
8924     /**
8925     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8926     */
8927     /**
8928     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8929     */
8930     /**
8931     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8932     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8933     */
8934     /**
8935     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8936     * on any HTTP request
8937     */
8938     /**
8939     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8940     */
8941     /**
8942     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8943     */
8944     multiSort: false,
8945     /**
8946     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8947     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8948     */
8949     remoteSort : false,
8950
8951     /**
8952     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8953      * loaded or when a record is removed. (defaults to false).
8954     */
8955     pruneModifiedRecords : false,
8956
8957     // private
8958     lastOptions : null,
8959
8960     /**
8961      * Add Records to the Store and fires the add event.
8962      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8963      */
8964     add : function(records){
8965         records = [].concat(records);
8966         for(var i = 0, len = records.length; i < len; i++){
8967             records[i].join(this);
8968         }
8969         var index = this.data.length;
8970         this.data.addAll(records);
8971         this.fireEvent("add", this, records, index);
8972     },
8973
8974     /**
8975      * Remove a Record from the Store and fires the remove event.
8976      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8977      */
8978     remove : function(record){
8979         var index = this.data.indexOf(record);
8980         this.data.removeAt(index);
8981         if(this.pruneModifiedRecords){
8982             this.modified.remove(record);
8983         }
8984         this.fireEvent("remove", this, record, index);
8985     },
8986
8987     /**
8988      * Remove all Records from the Store and fires the clear event.
8989      */
8990     removeAll : function(){
8991         this.data.clear();
8992         if(this.pruneModifiedRecords){
8993             this.modified = [];
8994         }
8995         this.fireEvent("clear", this);
8996     },
8997
8998     /**
8999      * Inserts Records to the Store at the given index and fires the add event.
9000      * @param {Number} index The start index at which to insert the passed Records.
9001      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9002      */
9003     insert : function(index, records){
9004         records = [].concat(records);
9005         for(var i = 0, len = records.length; i < len; i++){
9006             this.data.insert(index, records[i]);
9007             records[i].join(this);
9008         }
9009         this.fireEvent("add", this, records, index);
9010     },
9011
9012     /**
9013      * Get the index within the cache of the passed Record.
9014      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9015      * @return {Number} The index of the passed Record. Returns -1 if not found.
9016      */
9017     indexOf : function(record){
9018         return this.data.indexOf(record);
9019     },
9020
9021     /**
9022      * Get the index within the cache of the Record with the passed id.
9023      * @param {String} id The id of the Record to find.
9024      * @return {Number} The index of the Record. Returns -1 if not found.
9025      */
9026     indexOfId : function(id){
9027         return this.data.indexOfKey(id);
9028     },
9029
9030     /**
9031      * Get the Record with the specified id.
9032      * @param {String} id The id of the Record to find.
9033      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9034      */
9035     getById : function(id){
9036         return this.data.key(id);
9037     },
9038
9039     /**
9040      * Get the Record at the specified index.
9041      * @param {Number} index The index of the Record to find.
9042      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9043      */
9044     getAt : function(index){
9045         return this.data.itemAt(index);
9046     },
9047
9048     /**
9049      * Returns a range of Records between specified indices.
9050      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9051      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9052      * @return {Roo.data.Record[]} An array of Records
9053      */
9054     getRange : function(start, end){
9055         return this.data.getRange(start, end);
9056     },
9057
9058     // private
9059     storeOptions : function(o){
9060         o = Roo.apply({}, o);
9061         delete o.callback;
9062         delete o.scope;
9063         this.lastOptions = o;
9064     },
9065
9066     /**
9067      * Loads the Record cache from the configured Proxy using the configured Reader.
9068      * <p>
9069      * If using remote paging, then the first load call must specify the <em>start</em>
9070      * and <em>limit</em> properties in the options.params property to establish the initial
9071      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9072      * <p>
9073      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9074      * and this call will return before the new data has been loaded. Perform any post-processing
9075      * in a callback function, or in a "load" event handler.</strong>
9076      * <p>
9077      * @param {Object} options An object containing properties which control loading options:<ul>
9078      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9079      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9080      * passed the following arguments:<ul>
9081      * <li>r : Roo.data.Record[]</li>
9082      * <li>options: Options object from the load call</li>
9083      * <li>success: Boolean success indicator</li></ul></li>
9084      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9085      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9086      * </ul>
9087      */
9088     load : function(options){
9089         options = options || {};
9090         if(this.fireEvent("beforeload", this, options) !== false){
9091             this.storeOptions(options);
9092             var p = Roo.apply(options.params || {}, this.baseParams);
9093             // if meta was not loaded from remote source.. try requesting it.
9094             if (!this.reader.metaFromRemote) {
9095                 p._requestMeta = 1;
9096             }
9097             if(this.sortInfo && this.remoteSort){
9098                 var pn = this.paramNames;
9099                 p[pn["sort"]] = this.sortInfo.field;
9100                 p[pn["dir"]] = this.sortInfo.direction;
9101             }
9102             if (this.multiSort) {
9103                 var pn = this.paramNames;
9104                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9105             }
9106             
9107             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9108         }
9109     },
9110
9111     /**
9112      * Reloads the Record cache from the configured Proxy using the configured Reader and
9113      * the options from the last load operation performed.
9114      * @param {Object} options (optional) An object containing properties which may override the options
9115      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9116      * the most recently used options are reused).
9117      */
9118     reload : function(options){
9119         this.load(Roo.applyIf(options||{}, this.lastOptions));
9120     },
9121
9122     // private
9123     // Called as a callback by the Reader during a load operation.
9124     loadRecords : function(o, options, success){
9125         if(!o || success === false){
9126             if(success !== false){
9127                 this.fireEvent("load", this, [], options, o);
9128             }
9129             if(options.callback){
9130                 options.callback.call(options.scope || this, [], options, false);
9131             }
9132             return;
9133         }
9134         // if data returned failure - throw an exception.
9135         if (o.success === false) {
9136             // show a message if no listener is registered.
9137             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9138                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9139             }
9140             // loadmask wil be hooked into this..
9141             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9142             return;
9143         }
9144         var r = o.records, t = o.totalRecords || r.length;
9145         
9146         this.fireEvent("beforeloadadd", this, r, options, o);
9147         
9148         if(!options || options.add !== true){
9149             if(this.pruneModifiedRecords){
9150                 this.modified = [];
9151             }
9152             for(var i = 0, len = r.length; i < len; i++){
9153                 r[i].join(this);
9154             }
9155             if(this.snapshot){
9156                 this.data = this.snapshot;
9157                 delete this.snapshot;
9158             }
9159             this.data.clear();
9160             this.data.addAll(r);
9161             this.totalLength = t;
9162             this.applySort();
9163             this.fireEvent("datachanged", this);
9164         }else{
9165             this.totalLength = Math.max(t, this.data.length+r.length);
9166             this.add(r);
9167         }
9168         this.fireEvent("load", this, r, options, o);
9169         if(options.callback){
9170             options.callback.call(options.scope || this, r, options, true);
9171         }
9172     },
9173
9174
9175     /**
9176      * Loads data from a passed data block. A Reader which understands the format of the data
9177      * must have been configured in the constructor.
9178      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9179      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9180      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9181      */
9182     loadData : function(o, append){
9183         var r = this.reader.readRecords(o);
9184         this.loadRecords(r, {add: append}, true);
9185     },
9186
9187     /**
9188      * Gets the number of cached records.
9189      * <p>
9190      * <em>If using paging, this may not be the total size of the dataset. If the data object
9191      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9192      * the data set size</em>
9193      */
9194     getCount : function(){
9195         return this.data.length || 0;
9196     },
9197
9198     /**
9199      * Gets the total number of records in the dataset as returned by the server.
9200      * <p>
9201      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9202      * the dataset size</em>
9203      */
9204     getTotalCount : function(){
9205         return this.totalLength || 0;
9206     },
9207
9208     /**
9209      * Returns the sort state of the Store as an object with two properties:
9210      * <pre><code>
9211  field {String} The name of the field by which the Records are sorted
9212  direction {String} The sort order, "ASC" or "DESC"
9213      * </code></pre>
9214      */
9215     getSortState : function(){
9216         return this.sortInfo;
9217     },
9218
9219     // private
9220     applySort : function(){
9221         if(this.sortInfo && !this.remoteSort){
9222             var s = this.sortInfo, f = s.field;
9223             var st = this.fields.get(f).sortType;
9224             var fn = function(r1, r2){
9225                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9226                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9227             };
9228             this.data.sort(s.direction, fn);
9229             if(this.snapshot && this.snapshot != this.data){
9230                 this.snapshot.sort(s.direction, fn);
9231             }
9232         }
9233     },
9234
9235     /**
9236      * Sets the default sort column and order to be used by the next load operation.
9237      * @param {String} fieldName The name of the field to sort by.
9238      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9239      */
9240     setDefaultSort : function(field, dir){
9241         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9242     },
9243
9244     /**
9245      * Sort the Records.
9246      * If remote sorting is used, the sort is performed on the server, and the cache is
9247      * reloaded. If local sorting is used, the cache is sorted internally.
9248      * @param {String} fieldName The name of the field to sort by.
9249      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9250      */
9251     sort : function(fieldName, dir){
9252         var f = this.fields.get(fieldName);
9253         if(!dir){
9254             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9255             
9256             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9257                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9258             }else{
9259                 dir = f.sortDir;
9260             }
9261         }
9262         this.sortToggle[f.name] = dir;
9263         this.sortInfo = {field: f.name, direction: dir};
9264         if(!this.remoteSort){
9265             this.applySort();
9266             this.fireEvent("datachanged", this);
9267         }else{
9268             this.load(this.lastOptions);
9269         }
9270     },
9271
9272     /**
9273      * Calls the specified function for each of the Records in the cache.
9274      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9275      * Returning <em>false</em> aborts and exits the iteration.
9276      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9277      */
9278     each : function(fn, scope){
9279         this.data.each(fn, scope);
9280     },
9281
9282     /**
9283      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9284      * (e.g., during paging).
9285      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9286      */
9287     getModifiedRecords : function(){
9288         return this.modified;
9289     },
9290
9291     // private
9292     createFilterFn : function(property, value, anyMatch){
9293         if(!value.exec){ // not a regex
9294             value = String(value);
9295             if(value.length == 0){
9296                 return false;
9297             }
9298             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9299         }
9300         return function(r){
9301             return value.test(r.data[property]);
9302         };
9303     },
9304
9305     /**
9306      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9307      * @param {String} property A field on your records
9308      * @param {Number} start The record index to start at (defaults to 0)
9309      * @param {Number} end The last record index to include (defaults to length - 1)
9310      * @return {Number} The sum
9311      */
9312     sum : function(property, start, end){
9313         var rs = this.data.items, v = 0;
9314         start = start || 0;
9315         end = (end || end === 0) ? end : rs.length-1;
9316
9317         for(var i = start; i <= end; i++){
9318             v += (rs[i].data[property] || 0);
9319         }
9320         return v;
9321     },
9322
9323     /**
9324      * Filter the records by a specified property.
9325      * @param {String} field A field on your records
9326      * @param {String/RegExp} value Either a string that the field
9327      * should start with or a RegExp to test against the field
9328      * @param {Boolean} anyMatch True to match any part not just the beginning
9329      */
9330     filter : function(property, value, anyMatch){
9331         var fn = this.createFilterFn(property, value, anyMatch);
9332         return fn ? this.filterBy(fn) : this.clearFilter();
9333     },
9334
9335     /**
9336      * Filter by a function. The specified function will be called with each
9337      * record in this data source. If the function returns true the record is included,
9338      * otherwise it is filtered.
9339      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9340      * @param {Object} scope (optional) The scope of the function (defaults to this)
9341      */
9342     filterBy : function(fn, scope){
9343         this.snapshot = this.snapshot || this.data;
9344         this.data = this.queryBy(fn, scope||this);
9345         this.fireEvent("datachanged", this);
9346     },
9347
9348     /**
9349      * Query the records by a specified property.
9350      * @param {String} field A field on your records
9351      * @param {String/RegExp} value Either a string that the field
9352      * should start with or a RegExp to test against the field
9353      * @param {Boolean} anyMatch True to match any part not just the beginning
9354      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9355      */
9356     query : function(property, value, anyMatch){
9357         var fn = this.createFilterFn(property, value, anyMatch);
9358         return fn ? this.queryBy(fn) : this.data.clone();
9359     },
9360
9361     /**
9362      * Query by a function. The specified function will be called with each
9363      * record in this data source. If the function returns true the record is included
9364      * in the results.
9365      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9366      * @param {Object} scope (optional) The scope of the function (defaults to this)
9367       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9368      **/
9369     queryBy : function(fn, scope){
9370         var data = this.snapshot || this.data;
9371         return data.filterBy(fn, scope||this);
9372     },
9373
9374     /**
9375      * Collects unique values for a particular dataIndex from this store.
9376      * @param {String} dataIndex The property to collect
9377      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9378      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9379      * @return {Array} An array of the unique values
9380      **/
9381     collect : function(dataIndex, allowNull, bypassFilter){
9382         var d = (bypassFilter === true && this.snapshot) ?
9383                 this.snapshot.items : this.data.items;
9384         var v, sv, r = [], l = {};
9385         for(var i = 0, len = d.length; i < len; i++){
9386             v = d[i].data[dataIndex];
9387             sv = String(v);
9388             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9389                 l[sv] = true;
9390                 r[r.length] = v;
9391             }
9392         }
9393         return r;
9394     },
9395
9396     /**
9397      * Revert to a view of the Record cache with no filtering applied.
9398      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9399      */
9400     clearFilter : function(suppressEvent){
9401         if(this.snapshot && this.snapshot != this.data){
9402             this.data = this.snapshot;
9403             delete this.snapshot;
9404             if(suppressEvent !== true){
9405                 this.fireEvent("datachanged", this);
9406             }
9407         }
9408     },
9409
9410     // private
9411     afterEdit : function(record){
9412         if(this.modified.indexOf(record) == -1){
9413             this.modified.push(record);
9414         }
9415         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9416     },
9417     
9418     // private
9419     afterReject : function(record){
9420         this.modified.remove(record);
9421         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9422     },
9423
9424     // private
9425     afterCommit : function(record){
9426         this.modified.remove(record);
9427         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9428     },
9429
9430     /**
9431      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9432      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9433      */
9434     commitChanges : function(){
9435         var m = this.modified.slice(0);
9436         this.modified = [];
9437         for(var i = 0, len = m.length; i < len; i++){
9438             m[i].commit();
9439         }
9440     },
9441
9442     /**
9443      * Cancel outstanding changes on all changed records.
9444      */
9445     rejectChanges : function(){
9446         var m = this.modified.slice(0);
9447         this.modified = [];
9448         for(var i = 0, len = m.length; i < len; i++){
9449             m[i].reject();
9450         }
9451     },
9452
9453     onMetaChange : function(meta, rtype, o){
9454         this.recordType = rtype;
9455         this.fields = rtype.prototype.fields;
9456         delete this.snapshot;
9457         this.sortInfo = meta.sortInfo || this.sortInfo;
9458         this.modified = [];
9459         this.fireEvent('metachange', this, this.reader.meta);
9460     },
9461     
9462     moveIndex : function(data, type)
9463     {
9464         var index = this.indexOf(data);
9465         
9466         var newIndex = index + type;
9467         
9468         this.remove(data);
9469         
9470         this.insert(newIndex, data);
9471         
9472     }
9473 });/*
9474  * Based on:
9475  * Ext JS Library 1.1.1
9476  * Copyright(c) 2006-2007, Ext JS, LLC.
9477  *
9478  * Originally Released Under LGPL - original licence link has changed is not relivant.
9479  *
9480  * Fork - LGPL
9481  * <script type="text/javascript">
9482  */
9483
9484 /**
9485  * @class Roo.data.SimpleStore
9486  * @extends Roo.data.Store
9487  * Small helper class to make creating Stores from Array data easier.
9488  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9489  * @cfg {Array} fields An array of field definition objects, or field name strings.
9490  * @cfg {Array} data The multi-dimensional array of data
9491  * @constructor
9492  * @param {Object} config
9493  */
9494 Roo.data.SimpleStore = function(config){
9495     Roo.data.SimpleStore.superclass.constructor.call(this, {
9496         isLocal : true,
9497         reader: new Roo.data.ArrayReader({
9498                 id: config.id
9499             },
9500             Roo.data.Record.create(config.fields)
9501         ),
9502         proxy : new Roo.data.MemoryProxy(config.data)
9503     });
9504     this.load();
9505 };
9506 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9507  * Based on:
9508  * Ext JS Library 1.1.1
9509  * Copyright(c) 2006-2007, Ext JS, LLC.
9510  *
9511  * Originally Released Under LGPL - original licence link has changed is not relivant.
9512  *
9513  * Fork - LGPL
9514  * <script type="text/javascript">
9515  */
9516
9517 /**
9518 /**
9519  * @extends Roo.data.Store
9520  * @class Roo.data.JsonStore
9521  * Small helper class to make creating Stores for JSON data easier. <br/>
9522 <pre><code>
9523 var store = new Roo.data.JsonStore({
9524     url: 'get-images.php',
9525     root: 'images',
9526     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9527 });
9528 </code></pre>
9529  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9530  * JsonReader and HttpProxy (unless inline data is provided).</b>
9531  * @cfg {Array} fields An array of field definition objects, or field name strings.
9532  * @constructor
9533  * @param {Object} config
9534  */
9535 Roo.data.JsonStore = function(c){
9536     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9537         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9538         reader: new Roo.data.JsonReader(c, c.fields)
9539     }));
9540 };
9541 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9542  * Based on:
9543  * Ext JS Library 1.1.1
9544  * Copyright(c) 2006-2007, Ext JS, LLC.
9545  *
9546  * Originally Released Under LGPL - original licence link has changed is not relivant.
9547  *
9548  * Fork - LGPL
9549  * <script type="text/javascript">
9550  */
9551
9552  
9553 Roo.data.Field = function(config){
9554     if(typeof config == "string"){
9555         config = {name: config};
9556     }
9557     Roo.apply(this, config);
9558     
9559     if(!this.type){
9560         this.type = "auto";
9561     }
9562     
9563     var st = Roo.data.SortTypes;
9564     // named sortTypes are supported, here we look them up
9565     if(typeof this.sortType == "string"){
9566         this.sortType = st[this.sortType];
9567     }
9568     
9569     // set default sortType for strings and dates
9570     if(!this.sortType){
9571         switch(this.type){
9572             case "string":
9573                 this.sortType = st.asUCString;
9574                 break;
9575             case "date":
9576                 this.sortType = st.asDate;
9577                 break;
9578             default:
9579                 this.sortType = st.none;
9580         }
9581     }
9582
9583     // define once
9584     var stripRe = /[\$,%]/g;
9585
9586     // prebuilt conversion function for this field, instead of
9587     // switching every time we're reading a value
9588     if(!this.convert){
9589         var cv, dateFormat = this.dateFormat;
9590         switch(this.type){
9591             case "":
9592             case "auto":
9593             case undefined:
9594                 cv = function(v){ return v; };
9595                 break;
9596             case "string":
9597                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9598                 break;
9599             case "int":
9600                 cv = function(v){
9601                     return v !== undefined && v !== null && v !== '' ?
9602                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9603                     };
9604                 break;
9605             case "float":
9606                 cv = function(v){
9607                     return v !== undefined && v !== null && v !== '' ?
9608                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9609                     };
9610                 break;
9611             case "bool":
9612             case "boolean":
9613                 cv = function(v){ return v === true || v === "true" || v == 1; };
9614                 break;
9615             case "date":
9616                 cv = function(v){
9617                     if(!v){
9618                         return '';
9619                     }
9620                     if(v instanceof Date){
9621                         return v;
9622                     }
9623                     if(dateFormat){
9624                         if(dateFormat == "timestamp"){
9625                             return new Date(v*1000);
9626                         }
9627                         return Date.parseDate(v, dateFormat);
9628                     }
9629                     var parsed = Date.parse(v);
9630                     return parsed ? new Date(parsed) : null;
9631                 };
9632              break;
9633             
9634         }
9635         this.convert = cv;
9636     }
9637 };
9638
9639 Roo.data.Field.prototype = {
9640     dateFormat: null,
9641     defaultValue: "",
9642     mapping: null,
9643     sortType : null,
9644     sortDir : "ASC"
9645 };/*
9646  * Based on:
9647  * Ext JS Library 1.1.1
9648  * Copyright(c) 2006-2007, Ext JS, LLC.
9649  *
9650  * Originally Released Under LGPL - original licence link has changed is not relivant.
9651  *
9652  * Fork - LGPL
9653  * <script type="text/javascript">
9654  */
9655  
9656 // Base class for reading structured data from a data source.  This class is intended to be
9657 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9658
9659 /**
9660  * @class Roo.data.DataReader
9661  * Base class for reading structured data from a data source.  This class is intended to be
9662  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9663  */
9664
9665 Roo.data.DataReader = function(meta, recordType){
9666     
9667     this.meta = meta;
9668     
9669     this.recordType = recordType instanceof Array ? 
9670         Roo.data.Record.create(recordType) : recordType;
9671 };
9672
9673 Roo.data.DataReader.prototype = {
9674      /**
9675      * Create an empty record
9676      * @param {Object} data (optional) - overlay some values
9677      * @return {Roo.data.Record} record created.
9678      */
9679     newRow :  function(d) {
9680         var da =  {};
9681         this.recordType.prototype.fields.each(function(c) {
9682             switch( c.type) {
9683                 case 'int' : da[c.name] = 0; break;
9684                 case 'date' : da[c.name] = new Date(); break;
9685                 case 'float' : da[c.name] = 0.0; break;
9686                 case 'boolean' : da[c.name] = false; break;
9687                 default : da[c.name] = ""; break;
9688             }
9689             
9690         });
9691         return new this.recordType(Roo.apply(da, d));
9692     }
9693     
9694 };/*
9695  * Based on:
9696  * Ext JS Library 1.1.1
9697  * Copyright(c) 2006-2007, Ext JS, LLC.
9698  *
9699  * Originally Released Under LGPL - original licence link has changed is not relivant.
9700  *
9701  * Fork - LGPL
9702  * <script type="text/javascript">
9703  */
9704
9705 /**
9706  * @class Roo.data.DataProxy
9707  * @extends Roo.data.Observable
9708  * This class is an abstract base class for implementations which provide retrieval of
9709  * unformatted data objects.<br>
9710  * <p>
9711  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9712  * (of the appropriate type which knows how to parse the data object) to provide a block of
9713  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9714  * <p>
9715  * Custom implementations must implement the load method as described in
9716  * {@link Roo.data.HttpProxy#load}.
9717  */
9718 Roo.data.DataProxy = function(){
9719     this.addEvents({
9720         /**
9721          * @event beforeload
9722          * Fires before a network request is made to retrieve a data object.
9723          * @param {Object} This DataProxy object.
9724          * @param {Object} params The params parameter to the load function.
9725          */
9726         beforeload : true,
9727         /**
9728          * @event load
9729          * Fires before the load method's callback is called.
9730          * @param {Object} This DataProxy object.
9731          * @param {Object} o The data object.
9732          * @param {Object} arg The callback argument object passed to the load function.
9733          */
9734         load : true,
9735         /**
9736          * @event loadexception
9737          * Fires if an Exception occurs during data retrieval.
9738          * @param {Object} This DataProxy object.
9739          * @param {Object} o The data object.
9740          * @param {Object} arg The callback argument object passed to the load function.
9741          * @param {Object} e The Exception.
9742          */
9743         loadexception : true
9744     });
9745     Roo.data.DataProxy.superclass.constructor.call(this);
9746 };
9747
9748 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9749
9750     /**
9751      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9752      */
9753 /*
9754  * Based on:
9755  * Ext JS Library 1.1.1
9756  * Copyright(c) 2006-2007, Ext JS, LLC.
9757  *
9758  * Originally Released Under LGPL - original licence link has changed is not relivant.
9759  *
9760  * Fork - LGPL
9761  * <script type="text/javascript">
9762  */
9763 /**
9764  * @class Roo.data.MemoryProxy
9765  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9766  * to the Reader when its load method is called.
9767  * @constructor
9768  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9769  */
9770 Roo.data.MemoryProxy = function(data){
9771     if (data.data) {
9772         data = data.data;
9773     }
9774     Roo.data.MemoryProxy.superclass.constructor.call(this);
9775     this.data = data;
9776 };
9777
9778 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9779     /**
9780      * Load data from the requested source (in this case an in-memory
9781      * data object passed to the constructor), read the data object into
9782      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9783      * process that block using the passed callback.
9784      * @param {Object} params This parameter is not used by the MemoryProxy class.
9785      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9786      * object into a block of Roo.data.Records.
9787      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9788      * The function must be passed <ul>
9789      * <li>The Record block object</li>
9790      * <li>The "arg" argument from the load function</li>
9791      * <li>A boolean success indicator</li>
9792      * </ul>
9793      * @param {Object} scope The scope in which to call the callback
9794      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9795      */
9796     load : function(params, reader, callback, scope, arg){
9797         params = params || {};
9798         var result;
9799         try {
9800             result = reader.readRecords(this.data);
9801         }catch(e){
9802             this.fireEvent("loadexception", this, arg, null, e);
9803             callback.call(scope, null, arg, false);
9804             return;
9805         }
9806         callback.call(scope, result, arg, true);
9807     },
9808     
9809     // private
9810     update : function(params, records){
9811         
9812     }
9813 });/*
9814  * Based on:
9815  * Ext JS Library 1.1.1
9816  * Copyright(c) 2006-2007, Ext JS, LLC.
9817  *
9818  * Originally Released Under LGPL - original licence link has changed is not relivant.
9819  *
9820  * Fork - LGPL
9821  * <script type="text/javascript">
9822  */
9823 /**
9824  * @class Roo.data.HttpProxy
9825  * @extends Roo.data.DataProxy
9826  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9827  * configured to reference a certain URL.<br><br>
9828  * <p>
9829  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9830  * from which the running page was served.<br><br>
9831  * <p>
9832  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9833  * <p>
9834  * Be aware that to enable the browser to parse an XML document, the server must set
9835  * the Content-Type header in the HTTP response to "text/xml".
9836  * @constructor
9837  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9838  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9839  * will be used to make the request.
9840  */
9841 Roo.data.HttpProxy = function(conn){
9842     Roo.data.HttpProxy.superclass.constructor.call(this);
9843     // is conn a conn config or a real conn?
9844     this.conn = conn;
9845     this.useAjax = !conn || !conn.events;
9846   
9847 };
9848
9849 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9850     // thse are take from connection...
9851     
9852     /**
9853      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9854      */
9855     /**
9856      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9857      * extra parameters to each request made by this object. (defaults to undefined)
9858      */
9859     /**
9860      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9861      *  to each request made by this object. (defaults to undefined)
9862      */
9863     /**
9864      * @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)
9865      */
9866     /**
9867      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9868      */
9869      /**
9870      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9871      * @type Boolean
9872      */
9873   
9874
9875     /**
9876      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9877      * @type Boolean
9878      */
9879     /**
9880      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9881      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9882      * a finer-grained basis than the DataProxy events.
9883      */
9884     getConnection : function(){
9885         return this.useAjax ? Roo.Ajax : this.conn;
9886     },
9887
9888     /**
9889      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9890      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9891      * process that block using the passed callback.
9892      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9893      * for the request to the remote server.
9894      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9895      * object into a block of Roo.data.Records.
9896      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9897      * The function must be passed <ul>
9898      * <li>The Record block object</li>
9899      * <li>The "arg" argument from the load function</li>
9900      * <li>A boolean success indicator</li>
9901      * </ul>
9902      * @param {Object} scope The scope in which to call the callback
9903      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9904      */
9905     load : function(params, reader, callback, scope, arg){
9906         if(this.fireEvent("beforeload", this, params) !== false){
9907             var  o = {
9908                 params : params || {},
9909                 request: {
9910                     callback : callback,
9911                     scope : scope,
9912                     arg : arg
9913                 },
9914                 reader: reader,
9915                 callback : this.loadResponse,
9916                 scope: this
9917             };
9918             if(this.useAjax){
9919                 Roo.applyIf(o, this.conn);
9920                 if(this.activeRequest){
9921                     Roo.Ajax.abort(this.activeRequest);
9922                 }
9923                 this.activeRequest = Roo.Ajax.request(o);
9924             }else{
9925                 this.conn.request(o);
9926             }
9927         }else{
9928             callback.call(scope||this, null, arg, false);
9929         }
9930     },
9931
9932     // private
9933     loadResponse : function(o, success, response){
9934         delete this.activeRequest;
9935         if(!success){
9936             this.fireEvent("loadexception", this, o, response);
9937             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9938             return;
9939         }
9940         var result;
9941         try {
9942             result = o.reader.read(response);
9943         }catch(e){
9944             this.fireEvent("loadexception", this, o, response, e);
9945             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9946             return;
9947         }
9948         
9949         this.fireEvent("load", this, o, o.request.arg);
9950         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9951     },
9952
9953     // private
9954     update : function(dataSet){
9955
9956     },
9957
9958     // private
9959     updateResponse : function(dataSet){
9960
9961     }
9962 });/*
9963  * Based on:
9964  * Ext JS Library 1.1.1
9965  * Copyright(c) 2006-2007, Ext JS, LLC.
9966  *
9967  * Originally Released Under LGPL - original licence link has changed is not relivant.
9968  *
9969  * Fork - LGPL
9970  * <script type="text/javascript">
9971  */
9972
9973 /**
9974  * @class Roo.data.ScriptTagProxy
9975  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9976  * other than the originating domain of the running page.<br><br>
9977  * <p>
9978  * <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
9979  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9980  * <p>
9981  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9982  * source code that is used as the source inside a &lt;script> tag.<br><br>
9983  * <p>
9984  * In order for the browser to process the returned data, the server must wrap the data object
9985  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9986  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9987  * depending on whether the callback name was passed:
9988  * <p>
9989  * <pre><code>
9990 boolean scriptTag = false;
9991 String cb = request.getParameter("callback");
9992 if (cb != null) {
9993     scriptTag = true;
9994     response.setContentType("text/javascript");
9995 } else {
9996     response.setContentType("application/x-json");
9997 }
9998 Writer out = response.getWriter();
9999 if (scriptTag) {
10000     out.write(cb + "(");
10001 }
10002 out.print(dataBlock.toJsonString());
10003 if (scriptTag) {
10004     out.write(");");
10005 }
10006 </pre></code>
10007  *
10008  * @constructor
10009  * @param {Object} config A configuration object.
10010  */
10011 Roo.data.ScriptTagProxy = function(config){
10012     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10013     Roo.apply(this, config);
10014     this.head = document.getElementsByTagName("head")[0];
10015 };
10016
10017 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10018
10019 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10020     /**
10021      * @cfg {String} url The URL from which to request the data object.
10022      */
10023     /**
10024      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10025      */
10026     timeout : 30000,
10027     /**
10028      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10029      * the server the name of the callback function set up by the load call to process the returned data object.
10030      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10031      * javascript output which calls this named function passing the data object as its only parameter.
10032      */
10033     callbackParam : "callback",
10034     /**
10035      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10036      * name to the request.
10037      */
10038     nocache : true,
10039
10040     /**
10041      * Load data from the configured URL, read the data object into
10042      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10043      * process that block using the passed callback.
10044      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10045      * for the request to the remote server.
10046      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10047      * object into a block of Roo.data.Records.
10048      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10049      * The function must be passed <ul>
10050      * <li>The Record block object</li>
10051      * <li>The "arg" argument from the load function</li>
10052      * <li>A boolean success indicator</li>
10053      * </ul>
10054      * @param {Object} scope The scope in which to call the callback
10055      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10056      */
10057     load : function(params, reader, callback, scope, arg){
10058         if(this.fireEvent("beforeload", this, params) !== false){
10059
10060             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10061
10062             var url = this.url;
10063             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10064             if(this.nocache){
10065                 url += "&_dc=" + (new Date().getTime());
10066             }
10067             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10068             var trans = {
10069                 id : transId,
10070                 cb : "stcCallback"+transId,
10071                 scriptId : "stcScript"+transId,
10072                 params : params,
10073                 arg : arg,
10074                 url : url,
10075                 callback : callback,
10076                 scope : scope,
10077                 reader : reader
10078             };
10079             var conn = this;
10080
10081             window[trans.cb] = function(o){
10082                 conn.handleResponse(o, trans);
10083             };
10084
10085             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10086
10087             if(this.autoAbort !== false){
10088                 this.abort();
10089             }
10090
10091             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10092
10093             var script = document.createElement("script");
10094             script.setAttribute("src", url);
10095             script.setAttribute("type", "text/javascript");
10096             script.setAttribute("id", trans.scriptId);
10097             this.head.appendChild(script);
10098
10099             this.trans = trans;
10100         }else{
10101             callback.call(scope||this, null, arg, false);
10102         }
10103     },
10104
10105     // private
10106     isLoading : function(){
10107         return this.trans ? true : false;
10108     },
10109
10110     /**
10111      * Abort the current server request.
10112      */
10113     abort : function(){
10114         if(this.isLoading()){
10115             this.destroyTrans(this.trans);
10116         }
10117     },
10118
10119     // private
10120     destroyTrans : function(trans, isLoaded){
10121         this.head.removeChild(document.getElementById(trans.scriptId));
10122         clearTimeout(trans.timeoutId);
10123         if(isLoaded){
10124             window[trans.cb] = undefined;
10125             try{
10126                 delete window[trans.cb];
10127             }catch(e){}
10128         }else{
10129             // if hasn't been loaded, wait for load to remove it to prevent script error
10130             window[trans.cb] = function(){
10131                 window[trans.cb] = undefined;
10132                 try{
10133                     delete window[trans.cb];
10134                 }catch(e){}
10135             };
10136         }
10137     },
10138
10139     // private
10140     handleResponse : function(o, trans){
10141         this.trans = false;
10142         this.destroyTrans(trans, true);
10143         var result;
10144         try {
10145             result = trans.reader.readRecords(o);
10146         }catch(e){
10147             this.fireEvent("loadexception", this, o, trans.arg, e);
10148             trans.callback.call(trans.scope||window, null, trans.arg, false);
10149             return;
10150         }
10151         this.fireEvent("load", this, o, trans.arg);
10152         trans.callback.call(trans.scope||window, result, trans.arg, true);
10153     },
10154
10155     // private
10156     handleFailure : function(trans){
10157         this.trans = false;
10158         this.destroyTrans(trans, false);
10159         this.fireEvent("loadexception", this, null, trans.arg);
10160         trans.callback.call(trans.scope||window, null, trans.arg, false);
10161     }
10162 });/*
10163  * Based on:
10164  * Ext JS Library 1.1.1
10165  * Copyright(c) 2006-2007, Ext JS, LLC.
10166  *
10167  * Originally Released Under LGPL - original licence link has changed is not relivant.
10168  *
10169  * Fork - LGPL
10170  * <script type="text/javascript">
10171  */
10172
10173 /**
10174  * @class Roo.data.JsonReader
10175  * @extends Roo.data.DataReader
10176  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10177  * based on mappings in a provided Roo.data.Record constructor.
10178  * 
10179  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10180  * in the reply previously. 
10181  * 
10182  * <p>
10183  * Example code:
10184  * <pre><code>
10185 var RecordDef = Roo.data.Record.create([
10186     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10187     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10188 ]);
10189 var myReader = new Roo.data.JsonReader({
10190     totalProperty: "results",    // The property which contains the total dataset size (optional)
10191     root: "rows",                // The property which contains an Array of row objects
10192     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10193 }, RecordDef);
10194 </code></pre>
10195  * <p>
10196  * This would consume a JSON file like this:
10197  * <pre><code>
10198 { 'results': 2, 'rows': [
10199     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10200     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10201 }
10202 </code></pre>
10203  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10204  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10205  * paged from the remote server.
10206  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10207  * @cfg {String} root name of the property which contains the Array of row objects.
10208  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10209  * @constructor
10210  * Create a new JsonReader
10211  * @param {Object} meta Metadata configuration options
10212  * @param {Object} recordType Either an Array of field definition objects,
10213  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10214  */
10215 Roo.data.JsonReader = function(meta, recordType){
10216     
10217     meta = meta || {};
10218     // set some defaults:
10219     Roo.applyIf(meta, {
10220         totalProperty: 'total',
10221         successProperty : 'success',
10222         root : 'data',
10223         id : 'id'
10224     });
10225     
10226     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10227 };
10228 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10229     
10230     /**
10231      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10232      * Used by Store query builder to append _requestMeta to params.
10233      * 
10234      */
10235     metaFromRemote : false,
10236     /**
10237      * This method is only used by a DataProxy which has retrieved data from a remote server.
10238      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10239      * @return {Object} data A data block which is used by an Roo.data.Store object as
10240      * a cache of Roo.data.Records.
10241      */
10242     read : function(response){
10243         var json = response.responseText;
10244        
10245         var o = /* eval:var:o */ eval("("+json+")");
10246         if(!o) {
10247             throw {message: "JsonReader.read: Json object not found"};
10248         }
10249         
10250         if(o.metaData){
10251             
10252             delete this.ef;
10253             this.metaFromRemote = true;
10254             this.meta = o.metaData;
10255             this.recordType = Roo.data.Record.create(o.metaData.fields);
10256             this.onMetaChange(this.meta, this.recordType, o);
10257         }
10258         return this.readRecords(o);
10259     },
10260
10261     // private function a store will implement
10262     onMetaChange : function(meta, recordType, o){
10263
10264     },
10265
10266     /**
10267          * @ignore
10268          */
10269     simpleAccess: function(obj, subsc) {
10270         return obj[subsc];
10271     },
10272
10273         /**
10274          * @ignore
10275          */
10276     getJsonAccessor: function(){
10277         var re = /[\[\.]/;
10278         return function(expr) {
10279             try {
10280                 return(re.test(expr))
10281                     ? new Function("obj", "return obj." + expr)
10282                     : function(obj){
10283                         return obj[expr];
10284                     };
10285             } catch(e){}
10286             return Roo.emptyFn;
10287         };
10288     }(),
10289
10290     /**
10291      * Create a data block containing Roo.data.Records from an XML document.
10292      * @param {Object} o An object which contains an Array of row objects in the property specified
10293      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10294      * which contains the total size of the dataset.
10295      * @return {Object} data A data block which is used by an Roo.data.Store object as
10296      * a cache of Roo.data.Records.
10297      */
10298     readRecords : function(o){
10299         /**
10300          * After any data loads, the raw JSON data is available for further custom processing.
10301          * @type Object
10302          */
10303         this.o = o;
10304         var s = this.meta, Record = this.recordType,
10305             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10306
10307 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10308         if (!this.ef) {
10309             if(s.totalProperty) {
10310                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10311                 }
10312                 if(s.successProperty) {
10313                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10314                 }
10315                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10316                 if (s.id) {
10317                         var g = this.getJsonAccessor(s.id);
10318                         this.getId = function(rec) {
10319                                 var r = g(rec);  
10320                                 return (r === undefined || r === "") ? null : r;
10321                         };
10322                 } else {
10323                         this.getId = function(){return null;};
10324                 }
10325             this.ef = [];
10326             for(var jj = 0; jj < fl; jj++){
10327                 f = fi[jj];
10328                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10329                 this.ef[jj] = this.getJsonAccessor(map);
10330             }
10331         }
10332
10333         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10334         if(s.totalProperty){
10335             var vt = parseInt(this.getTotal(o), 10);
10336             if(!isNaN(vt)){
10337                 totalRecords = vt;
10338             }
10339         }
10340         if(s.successProperty){
10341             var vs = this.getSuccess(o);
10342             if(vs === false || vs === 'false'){
10343                 success = false;
10344             }
10345         }
10346         var records = [];
10347         for(var i = 0; i < c; i++){
10348                 var n = root[i];
10349             var values = {};
10350             var id = this.getId(n);
10351             for(var j = 0; j < fl; j++){
10352                 f = fi[j];
10353             var v = this.ef[j](n);
10354             if (!f.convert) {
10355                 Roo.log('missing convert for ' + f.name);
10356                 Roo.log(f);
10357                 continue;
10358             }
10359             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10360             }
10361             var record = new Record(values, id);
10362             record.json = n;
10363             records[i] = record;
10364         }
10365         return {
10366             raw : o,
10367             success : success,
10368             records : records,
10369             totalRecords : totalRecords
10370         };
10371     }
10372 });/*
10373  * Based on:
10374  * Ext JS Library 1.1.1
10375  * Copyright(c) 2006-2007, Ext JS, LLC.
10376  *
10377  * Originally Released Under LGPL - original licence link has changed is not relivant.
10378  *
10379  * Fork - LGPL
10380  * <script type="text/javascript">
10381  */
10382
10383 /**
10384  * @class Roo.data.ArrayReader
10385  * @extends Roo.data.DataReader
10386  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10387  * Each element of that Array represents a row of data fields. The
10388  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10389  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10390  * <p>
10391  * Example code:.
10392  * <pre><code>
10393 var RecordDef = Roo.data.Record.create([
10394     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10395     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10396 ]);
10397 var myReader = new Roo.data.ArrayReader({
10398     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10399 }, RecordDef);
10400 </code></pre>
10401  * <p>
10402  * This would consume an Array like this:
10403  * <pre><code>
10404 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10405   </code></pre>
10406  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10407  * @constructor
10408  * Create a new JsonReader
10409  * @param {Object} meta Metadata configuration options.
10410  * @param {Object} recordType Either an Array of field definition objects
10411  * as specified to {@link Roo.data.Record#create},
10412  * or an {@link Roo.data.Record} object
10413  * created using {@link Roo.data.Record#create}.
10414  */
10415 Roo.data.ArrayReader = function(meta, recordType){
10416     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10417 };
10418
10419 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10420     /**
10421      * Create a data block containing Roo.data.Records from an XML document.
10422      * @param {Object} o An Array of row objects which represents the dataset.
10423      * @return {Object} data A data block which is used by an Roo.data.Store object as
10424      * a cache of Roo.data.Records.
10425      */
10426     readRecords : function(o){
10427         var sid = this.meta ? this.meta.id : null;
10428         var recordType = this.recordType, fields = recordType.prototype.fields;
10429         var records = [];
10430         var root = o;
10431             for(var i = 0; i < root.length; i++){
10432                     var n = root[i];
10433                 var values = {};
10434                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10435                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10436                 var f = fields.items[j];
10437                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10438                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10439                 v = f.convert(v);
10440                 values[f.name] = v;
10441             }
10442                 var record = new recordType(values, id);
10443                 record.json = n;
10444                 records[records.length] = record;
10445             }
10446             return {
10447                 records : records,
10448                 totalRecords : records.length
10449             };
10450     }
10451 });/*
10452  * - LGPL
10453  * * 
10454  */
10455
10456 /**
10457  * @class Roo.bootstrap.ComboBox
10458  * @extends Roo.bootstrap.TriggerField
10459  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10460  * @cfg {Boolean} append (true|false) default false
10461  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10462  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10463  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10464  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10465  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10466  * @constructor
10467  * Create a new ComboBox.
10468  * @param {Object} config Configuration options
10469  */
10470 Roo.bootstrap.ComboBox = function(config){
10471     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10472     this.addEvents({
10473         /**
10474          * @event expand
10475          * Fires when the dropdown list is expanded
10476              * @param {Roo.bootstrap.ComboBox} combo This combo box
10477              */
10478         'expand' : true,
10479         /**
10480          * @event collapse
10481          * Fires when the dropdown list is collapsed
10482              * @param {Roo.bootstrap.ComboBox} combo This combo box
10483              */
10484         'collapse' : true,
10485         /**
10486          * @event beforeselect
10487          * Fires before a list item is selected. Return false to cancel the selection.
10488              * @param {Roo.bootstrap.ComboBox} combo This combo box
10489              * @param {Roo.data.Record} record The data record returned from the underlying store
10490              * @param {Number} index The index of the selected item in the dropdown list
10491              */
10492         'beforeselect' : true,
10493         /**
10494          * @event select
10495          * Fires when a list item is selected
10496              * @param {Roo.bootstrap.ComboBox} combo This combo box
10497              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10498              * @param {Number} index The index of the selected item in the dropdown list
10499              */
10500         'select' : true,
10501         /**
10502          * @event beforequery
10503          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10504          * The event object passed has these properties:
10505              * @param {Roo.bootstrap.ComboBox} combo This combo box
10506              * @param {String} query The query
10507              * @param {Boolean} forceAll true to force "all" query
10508              * @param {Boolean} cancel true to cancel the query
10509              * @param {Object} e The query event object
10510              */
10511         'beforequery': true,
10512          /**
10513          * @event add
10514          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10515              * @param {Roo.bootstrap.ComboBox} combo This combo box
10516              */
10517         'add' : true,
10518         /**
10519          * @event edit
10520          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10521              * @param {Roo.bootstrap.ComboBox} combo This combo box
10522              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10523              */
10524         'edit' : true,
10525         /**
10526          * @event remove
10527          * Fires when the remove value from the combobox array
10528              * @param {Roo.bootstrap.ComboBox} combo This combo box
10529              */
10530         'remove' : true
10531         
10532     });
10533     
10534     this.item = [];
10535     this.tickItems = [];
10536     
10537     this.selectedIndex = -1;
10538     if(this.mode == 'local'){
10539         if(config.queryDelay === undefined){
10540             this.queryDelay = 10;
10541         }
10542         if(config.minChars === undefined){
10543             this.minChars = 0;
10544         }
10545     }
10546 };
10547
10548 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10549      
10550     /**
10551      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10552      * rendering into an Roo.Editor, defaults to false)
10553      */
10554     /**
10555      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10556      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10557      */
10558     /**
10559      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10560      */
10561     /**
10562      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10563      * the dropdown list (defaults to undefined, with no header element)
10564      */
10565
10566      /**
10567      * @cfg {String/Roo.Template} tpl The template to use to render the output
10568      */
10569      
10570      /**
10571      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10572      */
10573     listWidth: undefined,
10574     /**
10575      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10576      * mode = 'remote' or 'text' if mode = 'local')
10577      */
10578     displayField: undefined,
10579     /**
10580      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10581      * mode = 'remote' or 'value' if mode = 'local'). 
10582      * Note: use of a valueField requires the user make a selection
10583      * in order for a value to be mapped.
10584      */
10585     valueField: undefined,
10586     
10587     
10588     /**
10589      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10590      * field's data value (defaults to the underlying DOM element's name)
10591      */
10592     hiddenName: undefined,
10593     /**
10594      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10595      */
10596     listClass: '',
10597     /**
10598      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10599      */
10600     selectedClass: 'active',
10601     
10602     /**
10603      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10604      */
10605     shadow:'sides',
10606     /**
10607      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10608      * anchor positions (defaults to 'tl-bl')
10609      */
10610     listAlign: 'tl-bl?',
10611     /**
10612      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10613      */
10614     maxHeight: 300,
10615     /**
10616      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10617      * query specified by the allQuery config option (defaults to 'query')
10618      */
10619     triggerAction: 'query',
10620     /**
10621      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10622      * (defaults to 4, does not apply if editable = false)
10623      */
10624     minChars : 4,
10625     /**
10626      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10627      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10628      */
10629     typeAhead: false,
10630     /**
10631      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10632      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10633      */
10634     queryDelay: 500,
10635     /**
10636      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10637      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10638      */
10639     pageSize: 0,
10640     /**
10641      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10642      * when editable = true (defaults to false)
10643      */
10644     selectOnFocus:false,
10645     /**
10646      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10647      */
10648     queryParam: 'query',
10649     /**
10650      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10651      * when mode = 'remote' (defaults to 'Loading...')
10652      */
10653     loadingText: 'Loading...',
10654     /**
10655      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10656      */
10657     resizable: false,
10658     /**
10659      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10660      */
10661     handleHeight : 8,
10662     /**
10663      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10664      * traditional select (defaults to true)
10665      */
10666     editable: true,
10667     /**
10668      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10669      */
10670     allQuery: '',
10671     /**
10672      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10673      */
10674     mode: 'remote',
10675     /**
10676      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10677      * listWidth has a higher value)
10678      */
10679     minListWidth : 70,
10680     /**
10681      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10682      * allow the user to set arbitrary text into the field (defaults to false)
10683      */
10684     forceSelection:false,
10685     /**
10686      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10687      * if typeAhead = true (defaults to 250)
10688      */
10689     typeAheadDelay : 250,
10690     /**
10691      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10692      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10693      */
10694     valueNotFoundText : undefined,
10695     /**
10696      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10697      */
10698     blockFocus : false,
10699     
10700     /**
10701      * @cfg {Boolean} disableClear Disable showing of clear button.
10702      */
10703     disableClear : false,
10704     /**
10705      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10706      */
10707     alwaysQuery : false,
10708     
10709     /**
10710      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10711      */
10712     multiple : false,
10713     
10714     //private
10715     addicon : false,
10716     editicon: false,
10717     
10718     page: 0,
10719     hasQuery: false,
10720     append: false,
10721     loadNext: false,
10722     autoFocus : true,
10723     tickable : false,
10724     btnPosition : 'right',
10725     triggerList : true,
10726     showToggleBtn : true,
10727     // element that contains real text value.. (when hidden is used..)
10728     
10729     getAutoCreate : function()
10730     {
10731         var cfg = false;
10732         
10733         /*
10734          *  Normal ComboBox
10735          */
10736         if(!this.tickable){
10737             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10738             return cfg;
10739         }
10740         
10741         /*
10742          *  ComboBox with tickable selections
10743          */
10744              
10745         var align = this.labelAlign || this.parentLabelAlign();
10746         
10747         cfg = {
10748             cls : 'form-group roo-combobox-tickable' //input-group
10749         };
10750         
10751         
10752         var buttons = {
10753             tag : 'div',
10754             cls : 'tickable-buttons',
10755             cn : [
10756                 {
10757                     tag : 'button',
10758                     type : 'button',
10759                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10760                     html : 'Edit'
10761                 },
10762                 {
10763                     tag : 'button',
10764                     type : 'button',
10765                     name : 'ok',
10766                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10767                     html : 'Done'
10768                 },
10769                 {
10770                     tag : 'button',
10771                     type : 'button',
10772                     name : 'cancel',
10773                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10774                     html : 'Cancel'
10775                 }
10776             ]
10777         };
10778         
10779         var _this = this;
10780         Roo.each(buttons.cn, function(c){
10781             if (_this.size) {
10782                 c.cls += ' btn-' + _this.size;
10783             }
10784
10785             if (_this.disabled) {
10786                 c.disabled = true;
10787             }
10788         });
10789         
10790         var box = {
10791             tag: 'div',
10792             cn: [
10793                 {
10794                     tag: 'input',
10795                     type : 'hidden',
10796                     cls: 'form-hidden-field'
10797                 },
10798                 {
10799                     tag: 'ul',
10800                     cls: 'select2-choices',
10801                     cn:[
10802                         {
10803                             tag: 'li',
10804                             cls: 'select2-search-field',
10805                             cn: [
10806
10807                                 buttons
10808                             ]
10809                         }
10810                     ]
10811                 }
10812             ]
10813         }
10814         
10815         var combobox = {
10816             cls: 'select2-container input-group select2-container-multi',
10817             cn: [
10818                 box
10819 //                {
10820 //                    tag: 'ul',
10821 //                    cls: 'typeahead typeahead-long dropdown-menu',
10822 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10823 //                }
10824             ]
10825         };
10826         
10827         if (align ==='left' && this.fieldLabel.length) {
10828             
10829                 Roo.log("left and has label");
10830                 cfg.cn = [
10831                     
10832                     {
10833                         tag: 'label',
10834                         'for' :  id,
10835                         cls : 'control-label col-sm-' + this.labelWidth,
10836                         html : this.fieldLabel
10837                         
10838                     },
10839                     {
10840                         cls : "col-sm-" + (12 - this.labelWidth), 
10841                         cn: [
10842                             combobox
10843                         ]
10844                     }
10845                     
10846                 ];
10847         } else if ( this.fieldLabel.length) {
10848                 Roo.log(" label");
10849                  cfg.cn = [
10850                    
10851                     {
10852                         tag: 'label',
10853                         //cls : 'input-group-addon',
10854                         html : this.fieldLabel
10855                         
10856                     },
10857                     
10858                     combobox
10859                     
10860                 ];
10861
10862         } else {
10863             
10864                 Roo.log(" no label && no align");
10865                 cfg = combobox
10866                      
10867                 
10868         }
10869          
10870         var settings=this;
10871         ['xs','sm','md','lg'].map(function(size){
10872             if (settings[size]) {
10873                 cfg.cls += ' col-' + size + '-' + settings[size];
10874             }
10875         });
10876         
10877         return cfg;
10878         
10879     },
10880     
10881     // private
10882     initEvents: function()
10883     {
10884         
10885         if (!this.store) {
10886             throw "can not find store for combo";
10887         }
10888         this.store = Roo.factory(this.store, Roo.data);
10889         
10890         if(this.tickable){
10891             this.initTickableEvents();
10892             return;
10893         }
10894         
10895         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10896         
10897         if(this.hiddenName){
10898             
10899             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10900             
10901             this.hiddenField.dom.value =
10902                 this.hiddenValue !== undefined ? this.hiddenValue :
10903                 this.value !== undefined ? this.value : '';
10904
10905             // prevent input submission
10906             this.el.dom.removeAttribute('name');
10907             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10908              
10909              
10910         }
10911         //if(Roo.isGecko){
10912         //    this.el.dom.setAttribute('autocomplete', 'off');
10913         //}
10914         
10915         var cls = 'x-combo-list';
10916         
10917         //this.list = new Roo.Layer({
10918         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10919         //});
10920         
10921         var _this = this;
10922         
10923         (function(){
10924             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10925             _this.list.setWidth(lw);
10926         }).defer(100);
10927         
10928         this.list.on('mouseover', this.onViewOver, this);
10929         this.list.on('mousemove', this.onViewMove, this);
10930         
10931         this.list.on('scroll', this.onViewScroll, this);
10932         
10933         /*
10934         this.list.swallowEvent('mousewheel');
10935         this.assetHeight = 0;
10936
10937         if(this.title){
10938             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10939             this.assetHeight += this.header.getHeight();
10940         }
10941
10942         this.innerList = this.list.createChild({cls:cls+'-inner'});
10943         this.innerList.on('mouseover', this.onViewOver, this);
10944         this.innerList.on('mousemove', this.onViewMove, this);
10945         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10946         
10947         if(this.allowBlank && !this.pageSize && !this.disableClear){
10948             this.footer = this.list.createChild({cls:cls+'-ft'});
10949             this.pageTb = new Roo.Toolbar(this.footer);
10950            
10951         }
10952         if(this.pageSize){
10953             this.footer = this.list.createChild({cls:cls+'-ft'});
10954             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10955                     {pageSize: this.pageSize});
10956             
10957         }
10958         
10959         if (this.pageTb && this.allowBlank && !this.disableClear) {
10960             var _this = this;
10961             this.pageTb.add(new Roo.Toolbar.Fill(), {
10962                 cls: 'x-btn-icon x-btn-clear',
10963                 text: '&#160;',
10964                 handler: function()
10965                 {
10966                     _this.collapse();
10967                     _this.clearValue();
10968                     _this.onSelect(false, -1);
10969                 }
10970             });
10971         }
10972         if (this.footer) {
10973             this.assetHeight += this.footer.getHeight();
10974         }
10975         */
10976             
10977         if(!this.tpl){
10978             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10979         }
10980
10981         this.view = new Roo.View(this.list, this.tpl, {
10982             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10983         });
10984         //this.view.wrapEl.setDisplayed(false);
10985         this.view.on('click', this.onViewClick, this);
10986         
10987         
10988         
10989         this.store.on('beforeload', this.onBeforeLoad, this);
10990         this.store.on('load', this.onLoad, this);
10991         this.store.on('loadexception', this.onLoadException, this);
10992         /*
10993         if(this.resizable){
10994             this.resizer = new Roo.Resizable(this.list,  {
10995                pinned:true, handles:'se'
10996             });
10997             this.resizer.on('resize', function(r, w, h){
10998                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10999                 this.listWidth = w;
11000                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11001                 this.restrictHeight();
11002             }, this);
11003             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11004         }
11005         */
11006         if(!this.editable){
11007             this.editable = true;
11008             this.setEditable(false);
11009         }
11010         
11011         /*
11012         
11013         if (typeof(this.events.add.listeners) != 'undefined') {
11014             
11015             this.addicon = this.wrap.createChild(
11016                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11017        
11018             this.addicon.on('click', function(e) {
11019                 this.fireEvent('add', this);
11020             }, this);
11021         }
11022         if (typeof(this.events.edit.listeners) != 'undefined') {
11023             
11024             this.editicon = this.wrap.createChild(
11025                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11026             if (this.addicon) {
11027                 this.editicon.setStyle('margin-left', '40px');
11028             }
11029             this.editicon.on('click', function(e) {
11030                 
11031                 // we fire even  if inothing is selected..
11032                 this.fireEvent('edit', this, this.lastData );
11033                 
11034             }, this);
11035         }
11036         */
11037         
11038         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11039             "up" : function(e){
11040                 this.inKeyMode = true;
11041                 this.selectPrev();
11042             },
11043
11044             "down" : function(e){
11045                 if(!this.isExpanded()){
11046                     this.onTriggerClick();
11047                 }else{
11048                     this.inKeyMode = true;
11049                     this.selectNext();
11050                 }
11051             },
11052
11053             "enter" : function(e){
11054 //                this.onViewClick();
11055                 //return true;
11056                 this.collapse();
11057                 
11058                 if(this.fireEvent("specialkey", this, e)){
11059                     this.onViewClick(false);
11060                 }
11061                 
11062                 return true;
11063             },
11064
11065             "esc" : function(e){
11066                 this.collapse();
11067             },
11068
11069             "tab" : function(e){
11070                 this.collapse();
11071                 
11072                 if(this.fireEvent("specialkey", this, e)){
11073                     this.onViewClick(false);
11074                 }
11075                 
11076                 return true;
11077             },
11078
11079             scope : this,
11080
11081             doRelay : function(foo, bar, hname){
11082                 if(hname == 'down' || this.scope.isExpanded()){
11083                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11084                 }
11085                 return true;
11086             },
11087
11088             forceKeyDown: true
11089         });
11090         
11091         
11092         this.queryDelay = Math.max(this.queryDelay || 10,
11093                 this.mode == 'local' ? 10 : 250);
11094         
11095         
11096         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11097         
11098         if(this.typeAhead){
11099             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11100         }
11101         if(this.editable !== false){
11102             this.inputEl().on("keyup", this.onKeyUp, this);
11103         }
11104         if(this.forceSelection){
11105             this.inputEl().on('blur', this.doForce, this);
11106         }
11107         
11108         if(this.multiple){
11109             this.choices = this.el.select('ul.select2-choices', true).first();
11110             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11111         }
11112     },
11113     
11114     initTickableEvents: function()
11115     {   
11116         this.createList();
11117         
11118         if(this.hiddenName){
11119             
11120             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11121             
11122             this.hiddenField.dom.value =
11123                 this.hiddenValue !== undefined ? this.hiddenValue :
11124                 this.value !== undefined ? this.value : '';
11125
11126             // prevent input submission
11127             this.el.dom.removeAttribute('name');
11128             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11129              
11130              
11131         }
11132         
11133 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11134         
11135         this.choices = this.el.select('ul.select2-choices', true).first();
11136         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11137         if(this.triggerList){
11138             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11139         }
11140          
11141         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11142         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11143         
11144         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11145         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11146         
11147         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11148         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11149         
11150         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11151         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11152         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11153         
11154         this.okBtn.hide();
11155         this.cancelBtn.hide();
11156         
11157         var _this = this;
11158         
11159         (function(){
11160             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11161             _this.list.setWidth(lw);
11162         }).defer(100);
11163         
11164         this.list.on('mouseover', this.onViewOver, this);
11165         this.list.on('mousemove', this.onViewMove, this);
11166         
11167         this.list.on('scroll', this.onViewScroll, this);
11168         
11169         if(!this.tpl){
11170             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>';
11171         }
11172
11173         this.view = new Roo.View(this.list, this.tpl, {
11174             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11175         });
11176         
11177         //this.view.wrapEl.setDisplayed(false);
11178         this.view.on('click', this.onViewClick, this);
11179         
11180         
11181         
11182         this.store.on('beforeload', this.onBeforeLoad, this);
11183         this.store.on('load', this.onLoad, this);
11184         this.store.on('loadexception', this.onLoadException, this);
11185         
11186 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11187 //            "up" : function(e){
11188 //                this.inKeyMode = true;
11189 //                this.selectPrev();
11190 //            },
11191 //
11192 //            "down" : function(e){
11193 //                if(!this.isExpanded()){
11194 //                    this.onTriggerClick();
11195 //                }else{
11196 //                    this.inKeyMode = true;
11197 //                    this.selectNext();
11198 //                }
11199 //            },
11200 //
11201 //            "enter" : function(e){
11202 ////                this.onViewClick();
11203 //                //return true;
11204 //                this.collapse();
11205 //                
11206 //                if(this.fireEvent("specialkey", this, e)){
11207 //                    this.onViewClick(false);
11208 //                }
11209 //                
11210 //                return true;
11211 //            },
11212 //
11213 //            "esc" : function(e){
11214 //                this.collapse();
11215 //            },
11216 //
11217 //            "tab" : function(e){
11218 //                this.collapse();
11219 //                
11220 //                if(this.fireEvent("specialkey", this, e)){
11221 //                    this.onViewClick(false);
11222 //                }
11223 //                
11224 //                return true;
11225 //            },
11226 //
11227 //            scope : this,
11228 //
11229 //            doRelay : function(foo, bar, hname){
11230 //                if(hname == 'down' || this.scope.isExpanded()){
11231 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11232 //                }
11233 //                return true;
11234 //            },
11235 //
11236 //            forceKeyDown: true
11237 //        });
11238         
11239         
11240         this.queryDelay = Math.max(this.queryDelay || 10,
11241                 this.mode == 'local' ? 10 : 250);
11242         
11243         
11244         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11245         
11246         if(this.typeAhead){
11247             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11248         }
11249     },
11250
11251     onDestroy : function(){
11252         if(this.view){
11253             this.view.setStore(null);
11254             this.view.el.removeAllListeners();
11255             this.view.el.remove();
11256             this.view.purgeListeners();
11257         }
11258         if(this.list){
11259             this.list.dom.innerHTML  = '';
11260         }
11261         
11262         if(this.store){
11263             this.store.un('beforeload', this.onBeforeLoad, this);
11264             this.store.un('load', this.onLoad, this);
11265             this.store.un('loadexception', this.onLoadException, this);
11266         }
11267         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11268     },
11269
11270     // private
11271     fireKey : function(e){
11272         if(e.isNavKeyPress() && !this.list.isVisible()){
11273             this.fireEvent("specialkey", this, e);
11274         }
11275     },
11276
11277     // private
11278     onResize: function(w, h){
11279 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11280 //        
11281 //        if(typeof w != 'number'){
11282 //            // we do not handle it!?!?
11283 //            return;
11284 //        }
11285 //        var tw = this.trigger.getWidth();
11286 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11287 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11288 //        var x = w - tw;
11289 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11290 //            
11291 //        //this.trigger.setStyle('left', x+'px');
11292 //        
11293 //        if(this.list && this.listWidth === undefined){
11294 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11295 //            this.list.setWidth(lw);
11296 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11297 //        }
11298         
11299     
11300         
11301     },
11302
11303     /**
11304      * Allow or prevent the user from directly editing the field text.  If false is passed,
11305      * the user will only be able to select from the items defined in the dropdown list.  This method
11306      * is the runtime equivalent of setting the 'editable' config option at config time.
11307      * @param {Boolean} value True to allow the user to directly edit the field text
11308      */
11309     setEditable : function(value){
11310         if(value == this.editable){
11311             return;
11312         }
11313         this.editable = value;
11314         if(!value){
11315             this.inputEl().dom.setAttribute('readOnly', true);
11316             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11317             this.inputEl().addClass('x-combo-noedit');
11318         }else{
11319             this.inputEl().dom.setAttribute('readOnly', false);
11320             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11321             this.inputEl().removeClass('x-combo-noedit');
11322         }
11323     },
11324
11325     // private
11326     
11327     onBeforeLoad : function(combo,opts){
11328         if(!this.hasFocus){
11329             return;
11330         }
11331          if (!opts.add) {
11332             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11333          }
11334         this.restrictHeight();
11335         this.selectedIndex = -1;
11336     },
11337
11338     // private
11339     onLoad : function(){
11340         
11341         this.hasQuery = false;
11342         
11343         if(!this.hasFocus){
11344             return;
11345         }
11346         
11347         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11348             this.loading.hide();
11349         }
11350         
11351         if(this.store.getCount() > 0){
11352             this.expand();
11353 //            this.restrictHeight();
11354             if(this.lastQuery == this.allQuery){
11355                 if(this.editable && !this.tickable){
11356                     this.inputEl().dom.select();
11357                 }
11358                 
11359                 if(
11360                     !this.selectByValue(this.value, true) &&
11361                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11362                     this.store.lastOptions.add != true)
11363                 ){
11364                     this.select(0, true);
11365                 }
11366             }else{
11367                 if(this.autoFocus){
11368                     this.selectNext();
11369                 }
11370                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11371                     this.taTask.delay(this.typeAheadDelay);
11372                 }
11373             }
11374         }else{
11375             this.onEmptyResults();
11376         }
11377         
11378         //this.el.focus();
11379     },
11380     // private
11381     onLoadException : function()
11382     {
11383         this.hasQuery = false;
11384         
11385         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11386             this.loading.hide();
11387         }
11388         
11389         this.collapse();
11390         Roo.log(this.store.reader.jsonData);
11391         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11392             // fixme
11393             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11394         }
11395         
11396         
11397     },
11398     // private
11399     onTypeAhead : function(){
11400         if(this.store.getCount() > 0){
11401             var r = this.store.getAt(0);
11402             var newValue = r.data[this.displayField];
11403             var len = newValue.length;
11404             var selStart = this.getRawValue().length;
11405             
11406             if(selStart != len){
11407                 this.setRawValue(newValue);
11408                 this.selectText(selStart, newValue.length);
11409             }
11410         }
11411     },
11412
11413     // private
11414     onSelect : function(record, index){
11415         
11416         if(this.fireEvent('beforeselect', this, record, index) !== false){
11417         
11418             this.setFromData(index > -1 ? record.data : false);
11419             
11420             this.collapse();
11421             this.fireEvent('select', this, record, index);
11422         }
11423     },
11424
11425     /**
11426      * Returns the currently selected field value or empty string if no value is set.
11427      * @return {String} value The selected value
11428      */
11429     getValue : function(){
11430         
11431         if(this.multiple){
11432             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11433         }
11434         
11435         if(this.valueField){
11436             return typeof this.value != 'undefined' ? this.value : '';
11437         }else{
11438             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11439         }
11440     },
11441
11442     /**
11443      * Clears any text/value currently set in the field
11444      */
11445     clearValue : function(){
11446         if(this.hiddenField){
11447             this.hiddenField.dom.value = '';
11448         }
11449         this.value = '';
11450         this.setRawValue('');
11451         this.lastSelectionText = '';
11452         
11453     },
11454
11455     /**
11456      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11457      * will be displayed in the field.  If the value does not match the data value of an existing item,
11458      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11459      * Otherwise the field will be blank (although the value will still be set).
11460      * @param {String} value The value to match
11461      */
11462     setValue : function(v){
11463         if(this.multiple){
11464             this.syncValue();
11465             return;
11466         }
11467         
11468         var text = v;
11469         if(this.valueField){
11470             var r = this.findRecord(this.valueField, v);
11471             if(r){
11472                 text = r.data[this.displayField];
11473             }else if(this.valueNotFoundText !== undefined){
11474                 text = this.valueNotFoundText;
11475             }
11476         }
11477         this.lastSelectionText = text;
11478         if(this.hiddenField){
11479             this.hiddenField.dom.value = v;
11480         }
11481         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11482         this.value = v;
11483     },
11484     /**
11485      * @property {Object} the last set data for the element
11486      */
11487     
11488     lastData : false,
11489     /**
11490      * Sets the value of the field based on a object which is related to the record format for the store.
11491      * @param {Object} value the value to set as. or false on reset?
11492      */
11493     setFromData : function(o){
11494         
11495         if(this.multiple){
11496             this.addItem(o);
11497             return;
11498         }
11499             
11500         var dv = ''; // display value
11501         var vv = ''; // value value..
11502         this.lastData = o;
11503         if (this.displayField) {
11504             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11505         } else {
11506             // this is an error condition!!!
11507             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11508         }
11509         
11510         if(this.valueField){
11511             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11512         }
11513         
11514         if(this.hiddenField){
11515             this.hiddenField.dom.value = vv;
11516             
11517             this.lastSelectionText = dv;
11518             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11519             this.value = vv;
11520             return;
11521         }
11522         // no hidden field.. - we store the value in 'value', but still display
11523         // display field!!!!
11524         this.lastSelectionText = dv;
11525         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11526         this.value = vv;
11527         
11528         
11529     },
11530     // private
11531     reset : function(){
11532         // overridden so that last data is reset..
11533         this.setValue(this.originalValue);
11534         this.clearInvalid();
11535         this.lastData = false;
11536         if (this.view) {
11537             this.view.clearSelections();
11538         }
11539     },
11540     // private
11541     findRecord : function(prop, value){
11542         var record;
11543         if(this.store.getCount() > 0){
11544             this.store.each(function(r){
11545                 if(r.data[prop] == value){
11546                     record = r;
11547                     return false;
11548                 }
11549                 return true;
11550             });
11551         }
11552         return record;
11553     },
11554     
11555     getName: function()
11556     {
11557         // returns hidden if it's set..
11558         if (!this.rendered) {return ''};
11559         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11560         
11561     },
11562     // private
11563     onViewMove : function(e, t){
11564         this.inKeyMode = false;
11565     },
11566
11567     // private
11568     onViewOver : function(e, t){
11569         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11570             return;
11571         }
11572         var item = this.view.findItemFromChild(t);
11573         
11574         if(item){
11575             var index = this.view.indexOf(item);
11576             this.select(index, false);
11577         }
11578     },
11579
11580     // private
11581     onViewClick : function(view, doFocus, el, e)
11582     {
11583         var index = this.view.getSelectedIndexes()[0];
11584         
11585         var r = this.store.getAt(index);
11586         
11587         if(this.tickable){
11588             
11589             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11590                 return;
11591             }
11592             
11593             var rm = false;
11594             var _this = this;
11595             
11596             Roo.each(this.tickItems, function(v,k){
11597                 
11598                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11599                     _this.tickItems.splice(k, 1);
11600                     rm = true;
11601                     return;
11602                 }
11603             })
11604             
11605             if(rm){
11606                 return;
11607             }
11608             
11609             this.tickItems.push(r.data);
11610             return;
11611         }
11612         
11613         if(r){
11614             this.onSelect(r, index);
11615         }
11616         if(doFocus !== false && !this.blockFocus){
11617             this.inputEl().focus();
11618         }
11619     },
11620
11621     // private
11622     restrictHeight : function(){
11623         //this.innerList.dom.style.height = '';
11624         //var inner = this.innerList.dom;
11625         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11626         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11627         //this.list.beginUpdate();
11628         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11629         this.list.alignTo(this.inputEl(), this.listAlign);
11630         this.list.alignTo(this.inputEl(), this.listAlign);
11631         //this.list.endUpdate();
11632     },
11633
11634     // private
11635     onEmptyResults : function(){
11636         this.collapse();
11637     },
11638
11639     /**
11640      * Returns true if the dropdown list is expanded, else false.
11641      */
11642     isExpanded : function(){
11643         return this.list.isVisible();
11644     },
11645
11646     /**
11647      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11648      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11649      * @param {String} value The data value of the item to select
11650      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11651      * selected item if it is not currently in view (defaults to true)
11652      * @return {Boolean} True if the value matched an item in the list, else false
11653      */
11654     selectByValue : function(v, scrollIntoView){
11655         if(v !== undefined && v !== null){
11656             var r = this.findRecord(this.valueField || this.displayField, v);
11657             if(r){
11658                 this.select(this.store.indexOf(r), scrollIntoView);
11659                 return true;
11660             }
11661         }
11662         return false;
11663     },
11664
11665     /**
11666      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11667      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11668      * @param {Number} index The zero-based index of the list item to select
11669      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11670      * selected item if it is not currently in view (defaults to true)
11671      */
11672     select : function(index, scrollIntoView){
11673         this.selectedIndex = index;
11674         this.view.select(index);
11675         if(scrollIntoView !== false){
11676             var el = this.view.getNode(index);
11677             if(el && !this.multiple && !this.tickable){
11678                 this.list.scrollChildIntoView(el, false);
11679             }
11680         }
11681     },
11682
11683     // private
11684     selectNext : function(){
11685         var ct = this.store.getCount();
11686         if(ct > 0){
11687             if(this.selectedIndex == -1){
11688                 this.select(0);
11689             }else if(this.selectedIndex < ct-1){
11690                 this.select(this.selectedIndex+1);
11691             }
11692         }
11693     },
11694
11695     // private
11696     selectPrev : function(){
11697         var ct = this.store.getCount();
11698         if(ct > 0){
11699             if(this.selectedIndex == -1){
11700                 this.select(0);
11701             }else if(this.selectedIndex != 0){
11702                 this.select(this.selectedIndex-1);
11703             }
11704         }
11705     },
11706
11707     // private
11708     onKeyUp : function(e){
11709         if(this.editable !== false && !e.isSpecialKey()){
11710             this.lastKey = e.getKey();
11711             this.dqTask.delay(this.queryDelay);
11712         }
11713     },
11714
11715     // private
11716     validateBlur : function(){
11717         return !this.list || !this.list.isVisible();   
11718     },
11719
11720     // private
11721     initQuery : function(){
11722         this.doQuery(this.getRawValue());
11723     },
11724
11725     // private
11726     doForce : function(){
11727         if(this.inputEl().dom.value.length > 0){
11728             this.inputEl().dom.value =
11729                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11730              
11731         }
11732     },
11733
11734     /**
11735      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11736      * query allowing the query action to be canceled if needed.
11737      * @param {String} query The SQL query to execute
11738      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11739      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11740      * saved in the current store (defaults to false)
11741      */
11742     doQuery : function(q, forceAll){
11743         
11744         if(q === undefined || q === null){
11745             q = '';
11746         }
11747         var qe = {
11748             query: q,
11749             forceAll: forceAll,
11750             combo: this,
11751             cancel:false
11752         };
11753         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11754             return false;
11755         }
11756         q = qe.query;
11757         
11758         forceAll = qe.forceAll;
11759         if(forceAll === true || (q.length >= this.minChars)){
11760             
11761             this.hasQuery = true;
11762             
11763             if(this.lastQuery != q || this.alwaysQuery){
11764                 this.lastQuery = q;
11765                 if(this.mode == 'local'){
11766                     this.selectedIndex = -1;
11767                     if(forceAll){
11768                         this.store.clearFilter();
11769                     }else{
11770                         this.store.filter(this.displayField, q);
11771                     }
11772                     this.onLoad();
11773                 }else{
11774                     this.store.baseParams[this.queryParam] = q;
11775                     
11776                     var options = {params : this.getParams(q)};
11777                     
11778                     if(this.loadNext){
11779                         options.add = true;
11780                         options.params.start = this.page * this.pageSize;
11781                     }
11782                     
11783                     this.store.load(options);
11784                     /*
11785                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11786                      *  we should expand the list on onLoad
11787                      *  so command out it
11788                      */
11789 //                    this.expand();
11790                 }
11791             }else{
11792                 this.selectedIndex = -1;
11793                 this.onLoad();   
11794             }
11795         }
11796         
11797         this.loadNext = false;
11798     },
11799
11800     // private
11801     getParams : function(q){
11802         var p = {};
11803         //p[this.queryParam] = q;
11804         
11805         if(this.pageSize){
11806             p.start = 0;
11807             p.limit = this.pageSize;
11808         }
11809         return p;
11810     },
11811
11812     /**
11813      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11814      */
11815     collapse : function(){
11816         if(!this.isExpanded()){
11817             return;
11818         }
11819         
11820         this.list.hide();
11821         
11822         if(this.tickable){
11823             this.okBtn.hide();
11824             this.cancelBtn.hide();
11825             this.trigger.show();
11826         }
11827         
11828         Roo.get(document).un('mousedown', this.collapseIf, this);
11829         Roo.get(document).un('mousewheel', this.collapseIf, this);
11830         if (!this.editable) {
11831             Roo.get(document).un('keydown', this.listKeyPress, this);
11832         }
11833         this.fireEvent('collapse', this);
11834     },
11835
11836     // private
11837     collapseIf : function(e){
11838         var in_combo  = e.within(this.el);
11839         var in_list =  e.within(this.list);
11840         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11841         
11842         if (in_combo || in_list || is_list) {
11843             //e.stopPropagation();
11844             return;
11845         }
11846         
11847         if(this.tickable){
11848             this.onTickableFooterButtonClick(e, false, false);
11849         }
11850
11851         this.collapse();
11852         
11853     },
11854
11855     /**
11856      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11857      */
11858     expand : function(){
11859        
11860         if(this.isExpanded() || !this.hasFocus){
11861             return;
11862         }
11863         
11864         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11865         this.list.setWidth(lw);
11866         
11867         
11868          Roo.log('expand');
11869         
11870         this.list.show();
11871         
11872         this.restrictHeight();
11873         
11874         if(this.tickable){
11875             
11876             this.tickItems = Roo.apply([], this.item);
11877             
11878             this.okBtn.show();
11879             this.cancelBtn.show();
11880             this.trigger.hide();
11881             
11882         }
11883         
11884         Roo.get(document).on('mousedown', this.collapseIf, this);
11885         Roo.get(document).on('mousewheel', this.collapseIf, this);
11886         if (!this.editable) {
11887             Roo.get(document).on('keydown', this.listKeyPress, this);
11888         }
11889         
11890         this.fireEvent('expand', this);
11891     },
11892
11893     // private
11894     // Implements the default empty TriggerField.onTriggerClick function
11895     onTriggerClick : function(e)
11896     {
11897         Roo.log('trigger click');
11898         
11899         if(this.disabled || !this.triggerList){
11900             return;
11901         }
11902         
11903         this.page = 0;
11904         this.loadNext = false;
11905         
11906         if(this.isExpanded()){
11907             this.collapse();
11908             if (!this.blockFocus) {
11909                 this.inputEl().focus();
11910             }
11911             
11912         }else {
11913             this.hasFocus = true;
11914             if(this.triggerAction == 'all') {
11915                 this.doQuery(this.allQuery, true);
11916             } else {
11917                 this.doQuery(this.getRawValue());
11918             }
11919             if (!this.blockFocus) {
11920                 this.inputEl().focus();
11921             }
11922         }
11923     },
11924     
11925     onTickableTriggerClick : function(e)
11926     {
11927         if(this.disabled){
11928             return;
11929         }
11930         
11931         this.page = 0;
11932         this.loadNext = false;
11933         this.hasFocus = true;
11934         
11935         if(this.triggerAction == 'all') {
11936             this.doQuery(this.allQuery, true);
11937         } else {
11938             this.doQuery(this.getRawValue());
11939         }
11940     },
11941     
11942     onSearchFieldClick : function(e)
11943     {
11944         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11945             return;
11946         }
11947         
11948         this.page = 0;
11949         this.loadNext = false;
11950         this.hasFocus = true;
11951         
11952         if(this.triggerAction == 'all') {
11953             this.doQuery(this.allQuery, true);
11954         } else {
11955             this.doQuery(this.getRawValue());
11956         }
11957     },
11958     
11959     listKeyPress : function(e)
11960     {
11961         //Roo.log('listkeypress');
11962         // scroll to first matching element based on key pres..
11963         if (e.isSpecialKey()) {
11964             return false;
11965         }
11966         var k = String.fromCharCode(e.getKey()).toUpperCase();
11967         //Roo.log(k);
11968         var match  = false;
11969         var csel = this.view.getSelectedNodes();
11970         var cselitem = false;
11971         if (csel.length) {
11972             var ix = this.view.indexOf(csel[0]);
11973             cselitem  = this.store.getAt(ix);
11974             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11975                 cselitem = false;
11976             }
11977             
11978         }
11979         
11980         this.store.each(function(v) { 
11981             if (cselitem) {
11982                 // start at existing selection.
11983                 if (cselitem.id == v.id) {
11984                     cselitem = false;
11985                 }
11986                 return true;
11987             }
11988                 
11989             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11990                 match = this.store.indexOf(v);
11991                 return false;
11992             }
11993             return true;
11994         }, this);
11995         
11996         if (match === false) {
11997             return true; // no more action?
11998         }
11999         // scroll to?
12000         this.view.select(match);
12001         var sn = Roo.get(this.view.getSelectedNodes()[0])
12002         sn.scrollIntoView(sn.dom.parentNode, false);
12003     },
12004     
12005     onViewScroll : function(e, t){
12006         
12007         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){
12008             return;
12009         }
12010         
12011         this.hasQuery = true;
12012         
12013         this.loading = this.list.select('.loading', true).first();
12014         
12015         if(this.loading === null){
12016             this.list.createChild({
12017                 tag: 'div',
12018                 cls: 'loading select2-more-results select2-active',
12019                 html: 'Loading more results...'
12020             })
12021             
12022             this.loading = this.list.select('.loading', true).first();
12023             
12024             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12025             
12026             this.loading.hide();
12027         }
12028         
12029         this.loading.show();
12030         
12031         var _combo = this;
12032         
12033         this.page++;
12034         this.loadNext = true;
12035         
12036         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12037         
12038         return;
12039     },
12040     
12041     addItem : function(o)
12042     {   
12043         var dv = ''; // display value
12044         
12045         if (this.displayField) {
12046             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12047         } else {
12048             // this is an error condition!!!
12049             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12050         }
12051         
12052         if(!dv.length){
12053             return;
12054         }
12055         
12056         var choice = this.choices.createChild({
12057             tag: 'li',
12058             cls: 'select2-search-choice',
12059             cn: [
12060                 {
12061                     tag: 'div',
12062                     html: dv
12063                 },
12064                 {
12065                     tag: 'a',
12066                     href: '#',
12067                     cls: 'select2-search-choice-close',
12068                     tabindex: '-1'
12069                 }
12070             ]
12071             
12072         }, this.searchField);
12073         
12074         var close = choice.select('a.select2-search-choice-close', true).first()
12075         
12076         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12077         
12078         this.item.push(o);
12079         
12080         this.lastData = o;
12081         
12082         this.syncValue();
12083         
12084         this.inputEl().dom.value = '';
12085         
12086     },
12087     
12088     onRemoveItem : function(e, _self, o)
12089     {
12090         e.preventDefault();
12091         var index = this.item.indexOf(o.data) * 1;
12092         
12093         if( index < 0){
12094             Roo.log('not this item?!');
12095             return;
12096         }
12097         
12098         this.item.splice(index, 1);
12099         o.item.remove();
12100         
12101         this.syncValue();
12102         
12103         this.fireEvent('remove', this, e);
12104         
12105     },
12106     
12107     syncValue : function()
12108     {
12109         if(!this.item.length){
12110             this.clearValue();
12111             return;
12112         }
12113             
12114         var value = [];
12115         var _this = this;
12116         Roo.each(this.item, function(i){
12117             if(_this.valueField){
12118                 value.push(i[_this.valueField]);
12119                 return;
12120             }
12121
12122             value.push(i);
12123         });
12124
12125         this.value = value.join(',');
12126
12127         if(this.hiddenField){
12128             this.hiddenField.dom.value = this.value;
12129         }
12130     },
12131     
12132     clearItem : function()
12133     {
12134         if(!this.multiple){
12135             return;
12136         }
12137         
12138         this.item = [];
12139         
12140         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12141            c.remove();
12142         });
12143         
12144         this.syncValue();
12145     },
12146     
12147     inputEl: function ()
12148     {
12149         if(this.tickable){
12150             return this.searchField;
12151         }
12152         return this.el.select('input.form-control',true).first();
12153     },
12154     
12155     
12156     onTickableFooterButtonClick : function(e, btn, el)
12157     {
12158         e.preventDefault();
12159         
12160         if(btn && btn.name == 'cancel'){
12161             this.tickItems = Roo.apply([], this.item);
12162             this.collapse();
12163             return;
12164         }
12165         
12166         this.clearItem();
12167         
12168         var _this = this;
12169         
12170         Roo.each(this.tickItems, function(o){
12171             _this.addItem(o);
12172         });
12173         
12174         this.collapse();
12175         
12176     },
12177     
12178     validate : function()
12179     {
12180         var v = this.getRawValue();
12181         
12182         if(this.multiple){
12183             v = this.getValue();
12184         }
12185         
12186         if(this.disabled || this.validateValue(v)){
12187             this.clearInvalid();
12188             return true;
12189         }
12190         return false;
12191     }
12192     
12193     
12194
12195     /** 
12196     * @cfg {Boolean} grow 
12197     * @hide 
12198     */
12199     /** 
12200     * @cfg {Number} growMin 
12201     * @hide 
12202     */
12203     /** 
12204     * @cfg {Number} growMax 
12205     * @hide 
12206     */
12207     /**
12208      * @hide
12209      * @method autoSize
12210      */
12211 });
12212 /*
12213  * Based on:
12214  * Ext JS Library 1.1.1
12215  * Copyright(c) 2006-2007, Ext JS, LLC.
12216  *
12217  * Originally Released Under LGPL - original licence link has changed is not relivant.
12218  *
12219  * Fork - LGPL
12220  * <script type="text/javascript">
12221  */
12222
12223 /**
12224  * @class Roo.View
12225  * @extends Roo.util.Observable
12226  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12227  * This class also supports single and multi selection modes. <br>
12228  * Create a data model bound view:
12229  <pre><code>
12230  var store = new Roo.data.Store(...);
12231
12232  var view = new Roo.View({
12233     el : "my-element",
12234     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12235  
12236     singleSelect: true,
12237     selectedClass: "ydataview-selected",
12238     store: store
12239  });
12240
12241  // listen for node click?
12242  view.on("click", function(vw, index, node, e){
12243  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12244  });
12245
12246  // load XML data
12247  dataModel.load("foobar.xml");
12248  </code></pre>
12249  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12250  * <br><br>
12251  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12252  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12253  * 
12254  * Note: old style constructor is still suported (container, template, config)
12255  * 
12256  * @constructor
12257  * Create a new View
12258  * @param {Object} config The config object
12259  * 
12260  */
12261 Roo.View = function(config, depreciated_tpl, depreciated_config){
12262     
12263     this.parent = false;
12264     
12265     if (typeof(depreciated_tpl) == 'undefined') {
12266         // new way.. - universal constructor.
12267         Roo.apply(this, config);
12268         this.el  = Roo.get(this.el);
12269     } else {
12270         // old format..
12271         this.el  = Roo.get(config);
12272         this.tpl = depreciated_tpl;
12273         Roo.apply(this, depreciated_config);
12274     }
12275     this.wrapEl  = this.el.wrap().wrap();
12276     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12277     
12278     
12279     if(typeof(this.tpl) == "string"){
12280         this.tpl = new Roo.Template(this.tpl);
12281     } else {
12282         // support xtype ctors..
12283         this.tpl = new Roo.factory(this.tpl, Roo);
12284     }
12285     
12286     
12287     this.tpl.compile();
12288     
12289     /** @private */
12290     this.addEvents({
12291         /**
12292          * @event beforeclick
12293          * Fires before a click is processed. Returns false to cancel the default action.
12294          * @param {Roo.View} this
12295          * @param {Number} index The index of the target node
12296          * @param {HTMLElement} node The target node
12297          * @param {Roo.EventObject} e The raw event object
12298          */
12299             "beforeclick" : true,
12300         /**
12301          * @event click
12302          * Fires when a template node is clicked.
12303          * @param {Roo.View} this
12304          * @param {Number} index The index of the target node
12305          * @param {HTMLElement} node The target node
12306          * @param {Roo.EventObject} e The raw event object
12307          */
12308             "click" : true,
12309         /**
12310          * @event dblclick
12311          * Fires when a template node is double clicked.
12312          * @param {Roo.View} this
12313          * @param {Number} index The index of the target node
12314          * @param {HTMLElement} node The target node
12315          * @param {Roo.EventObject} e The raw event object
12316          */
12317             "dblclick" : true,
12318         /**
12319          * @event contextmenu
12320          * Fires when a template node is right clicked.
12321          * @param {Roo.View} this
12322          * @param {Number} index The index of the target node
12323          * @param {HTMLElement} node The target node
12324          * @param {Roo.EventObject} e The raw event object
12325          */
12326             "contextmenu" : true,
12327         /**
12328          * @event selectionchange
12329          * Fires when the selected nodes change.
12330          * @param {Roo.View} this
12331          * @param {Array} selections Array of the selected nodes
12332          */
12333             "selectionchange" : true,
12334     
12335         /**
12336          * @event beforeselect
12337          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12338          * @param {Roo.View} this
12339          * @param {HTMLElement} node The node to be selected
12340          * @param {Array} selections Array of currently selected nodes
12341          */
12342             "beforeselect" : true,
12343         /**
12344          * @event preparedata
12345          * Fires on every row to render, to allow you to change the data.
12346          * @param {Roo.View} this
12347          * @param {Object} data to be rendered (change this)
12348          */
12349           "preparedata" : true
12350           
12351           
12352         });
12353
12354
12355
12356     this.el.on({
12357         "click": this.onClick,
12358         "dblclick": this.onDblClick,
12359         "contextmenu": this.onContextMenu,
12360         scope:this
12361     });
12362
12363     this.selections = [];
12364     this.nodes = [];
12365     this.cmp = new Roo.CompositeElementLite([]);
12366     if(this.store){
12367         this.store = Roo.factory(this.store, Roo.data);
12368         this.setStore(this.store, true);
12369     }
12370     
12371     if ( this.footer && this.footer.xtype) {
12372            
12373          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12374         
12375         this.footer.dataSource = this.store
12376         this.footer.container = fctr;
12377         this.footer = Roo.factory(this.footer, Roo);
12378         fctr.insertFirst(this.el);
12379         
12380         // this is a bit insane - as the paging toolbar seems to detach the el..
12381 //        dom.parentNode.parentNode.parentNode
12382          // they get detached?
12383     }
12384     
12385     
12386     Roo.View.superclass.constructor.call(this);
12387     
12388     
12389 };
12390
12391 Roo.extend(Roo.View, Roo.util.Observable, {
12392     
12393      /**
12394      * @cfg {Roo.data.Store} store Data store to load data from.
12395      */
12396     store : false,
12397     
12398     /**
12399      * @cfg {String|Roo.Element} el The container element.
12400      */
12401     el : '',
12402     
12403     /**
12404      * @cfg {String|Roo.Template} tpl The template used by this View 
12405      */
12406     tpl : false,
12407     /**
12408      * @cfg {String} dataName the named area of the template to use as the data area
12409      *                          Works with domtemplates roo-name="name"
12410      */
12411     dataName: false,
12412     /**
12413      * @cfg {String} selectedClass The css class to add to selected nodes
12414      */
12415     selectedClass : "x-view-selected",
12416      /**
12417      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12418      */
12419     emptyText : "",
12420     
12421     /**
12422      * @cfg {String} text to display on mask (default Loading)
12423      */
12424     mask : false,
12425     /**
12426      * @cfg {Boolean} multiSelect Allow multiple selection
12427      */
12428     multiSelect : false,
12429     /**
12430      * @cfg {Boolean} singleSelect Allow single selection
12431      */
12432     singleSelect:  false,
12433     
12434     /**
12435      * @cfg {Boolean} toggleSelect - selecting 
12436      */
12437     toggleSelect : false,
12438     
12439     /**
12440      * @cfg {Boolean} tickable - selecting 
12441      */
12442     tickable : false,
12443     
12444     /**
12445      * Returns the element this view is bound to.
12446      * @return {Roo.Element}
12447      */
12448     getEl : function(){
12449         return this.wrapEl;
12450     },
12451     
12452     
12453
12454     /**
12455      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12456      */
12457     refresh : function(){
12458         //Roo.log('refresh');
12459         var t = this.tpl;
12460         
12461         // if we are using something like 'domtemplate', then
12462         // the what gets used is:
12463         // t.applySubtemplate(NAME, data, wrapping data..)
12464         // the outer template then get' applied with
12465         //     the store 'extra data'
12466         // and the body get's added to the
12467         //      roo-name="data" node?
12468         //      <span class='roo-tpl-{name}'></span> ?????
12469         
12470         
12471         
12472         this.clearSelections();
12473         this.el.update("");
12474         var html = [];
12475         var records = this.store.getRange();
12476         if(records.length < 1) {
12477             
12478             // is this valid??  = should it render a template??
12479             
12480             this.el.update(this.emptyText);
12481             return;
12482         }
12483         var el = this.el;
12484         if (this.dataName) {
12485             this.el.update(t.apply(this.store.meta)); //????
12486             el = this.el.child('.roo-tpl-' + this.dataName);
12487         }
12488         
12489         for(var i = 0, len = records.length; i < len; i++){
12490             var data = this.prepareData(records[i].data, i, records[i]);
12491             this.fireEvent("preparedata", this, data, i, records[i]);
12492             
12493             var d = Roo.apply({}, data);
12494             
12495             if(this.tickable){
12496                 Roo.apply(d, {'roo-id' : Roo.id()});
12497                 
12498                 var _this = this;
12499             
12500                 Roo.each(this.parent.item, function(item){
12501                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12502                         return;
12503                     }
12504                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12505                 });
12506             }
12507             
12508             html[html.length] = Roo.util.Format.trim(
12509                 this.dataName ?
12510                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12511                     t.apply(d)
12512             );
12513         }
12514         
12515         
12516         
12517         el.update(html.join(""));
12518         this.nodes = el.dom.childNodes;
12519         this.updateIndexes(0);
12520     },
12521     
12522
12523     /**
12524      * Function to override to reformat the data that is sent to
12525      * the template for each node.
12526      * DEPRICATED - use the preparedata event handler.
12527      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12528      * a JSON object for an UpdateManager bound view).
12529      */
12530     prepareData : function(data, index, record)
12531     {
12532         this.fireEvent("preparedata", this, data, index, record);
12533         return data;
12534     },
12535
12536     onUpdate : function(ds, record){
12537         // Roo.log('on update');   
12538         this.clearSelections();
12539         var index = this.store.indexOf(record);
12540         var n = this.nodes[index];
12541         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12542         n.parentNode.removeChild(n);
12543         this.updateIndexes(index, index);
12544     },
12545
12546     
12547     
12548 // --------- FIXME     
12549     onAdd : function(ds, records, index)
12550     {
12551         //Roo.log(['on Add', ds, records, index] );        
12552         this.clearSelections();
12553         if(this.nodes.length == 0){
12554             this.refresh();
12555             return;
12556         }
12557         var n = this.nodes[index];
12558         for(var i = 0, len = records.length; i < len; i++){
12559             var d = this.prepareData(records[i].data, i, records[i]);
12560             if(n){
12561                 this.tpl.insertBefore(n, d);
12562             }else{
12563                 
12564                 this.tpl.append(this.el, d);
12565             }
12566         }
12567         this.updateIndexes(index);
12568     },
12569
12570     onRemove : function(ds, record, index){
12571        // Roo.log('onRemove');
12572         this.clearSelections();
12573         var el = this.dataName  ?
12574             this.el.child('.roo-tpl-' + this.dataName) :
12575             this.el; 
12576         
12577         el.dom.removeChild(this.nodes[index]);
12578         this.updateIndexes(index);
12579     },
12580
12581     /**
12582      * Refresh an individual node.
12583      * @param {Number} index
12584      */
12585     refreshNode : function(index){
12586         this.onUpdate(this.store, this.store.getAt(index));
12587     },
12588
12589     updateIndexes : function(startIndex, endIndex){
12590         var ns = this.nodes;
12591         startIndex = startIndex || 0;
12592         endIndex = endIndex || ns.length - 1;
12593         for(var i = startIndex; i <= endIndex; i++){
12594             ns[i].nodeIndex = i;
12595         }
12596     },
12597
12598     /**
12599      * Changes the data store this view uses and refresh the view.
12600      * @param {Store} store
12601      */
12602     setStore : function(store, initial){
12603         if(!initial && this.store){
12604             this.store.un("datachanged", this.refresh);
12605             this.store.un("add", this.onAdd);
12606             this.store.un("remove", this.onRemove);
12607             this.store.un("update", this.onUpdate);
12608             this.store.un("clear", this.refresh);
12609             this.store.un("beforeload", this.onBeforeLoad);
12610             this.store.un("load", this.onLoad);
12611             this.store.un("loadexception", this.onLoad);
12612         }
12613         if(store){
12614           
12615             store.on("datachanged", this.refresh, this);
12616             store.on("add", this.onAdd, this);
12617             store.on("remove", this.onRemove, this);
12618             store.on("update", this.onUpdate, this);
12619             store.on("clear", this.refresh, this);
12620             store.on("beforeload", this.onBeforeLoad, this);
12621             store.on("load", this.onLoad, this);
12622             store.on("loadexception", this.onLoad, this);
12623         }
12624         
12625         if(store){
12626             this.refresh();
12627         }
12628     },
12629     /**
12630      * onbeforeLoad - masks the loading area.
12631      *
12632      */
12633     onBeforeLoad : function(store,opts)
12634     {
12635          //Roo.log('onBeforeLoad');   
12636         if (!opts.add) {
12637             this.el.update("");
12638         }
12639         this.el.mask(this.mask ? this.mask : "Loading" ); 
12640     },
12641     onLoad : function ()
12642     {
12643         this.el.unmask();
12644     },
12645     
12646
12647     /**
12648      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12649      * @param {HTMLElement} node
12650      * @return {HTMLElement} The template node
12651      */
12652     findItemFromChild : function(node){
12653         var el = this.dataName  ?
12654             this.el.child('.roo-tpl-' + this.dataName,true) :
12655             this.el.dom; 
12656         
12657         if(!node || node.parentNode == el){
12658                     return node;
12659             }
12660             var p = node.parentNode;
12661             while(p && p != el){
12662             if(p.parentNode == el){
12663                 return p;
12664             }
12665             p = p.parentNode;
12666         }
12667             return null;
12668     },
12669
12670     /** @ignore */
12671     onClick : function(e){
12672         var item = this.findItemFromChild(e.getTarget());
12673         if(item){
12674             var index = this.indexOf(item);
12675             if(this.onItemClick(item, index, e) !== false){
12676                 this.fireEvent("click", this, index, item, e);
12677             }
12678         }else{
12679             this.clearSelections();
12680         }
12681     },
12682
12683     /** @ignore */
12684     onContextMenu : function(e){
12685         var item = this.findItemFromChild(e.getTarget());
12686         if(item){
12687             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12688         }
12689     },
12690
12691     /** @ignore */
12692     onDblClick : function(e){
12693         var item = this.findItemFromChild(e.getTarget());
12694         if(item){
12695             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12696         }
12697     },
12698
12699     onItemClick : function(item, index, e)
12700     {
12701         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12702             return false;
12703         }
12704         if (this.toggleSelect) {
12705             var m = this.isSelected(item) ? 'unselect' : 'select';
12706             //Roo.log(m);
12707             var _t = this;
12708             _t[m](item, true, false);
12709             return true;
12710         }
12711         if(this.multiSelect || this.singleSelect){
12712             if(this.multiSelect && e.shiftKey && this.lastSelection){
12713                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12714             }else{
12715                 this.select(item, this.multiSelect && e.ctrlKey);
12716                 this.lastSelection = item;
12717             }
12718             
12719             if(!this.tickable){
12720                 e.preventDefault();
12721             }
12722             
12723         }
12724         return true;
12725     },
12726
12727     /**
12728      * Get the number of selected nodes.
12729      * @return {Number}
12730      */
12731     getSelectionCount : function(){
12732         return this.selections.length;
12733     },
12734
12735     /**
12736      * Get the currently selected nodes.
12737      * @return {Array} An array of HTMLElements
12738      */
12739     getSelectedNodes : function(){
12740         return this.selections;
12741     },
12742
12743     /**
12744      * Get the indexes of the selected nodes.
12745      * @return {Array}
12746      */
12747     getSelectedIndexes : function(){
12748         var indexes = [], s = this.selections;
12749         for(var i = 0, len = s.length; i < len; i++){
12750             indexes.push(s[i].nodeIndex);
12751         }
12752         return indexes;
12753     },
12754
12755     /**
12756      * Clear all selections
12757      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12758      */
12759     clearSelections : function(suppressEvent){
12760         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12761             this.cmp.elements = this.selections;
12762             this.cmp.removeClass(this.selectedClass);
12763             this.selections = [];
12764             if(!suppressEvent){
12765                 this.fireEvent("selectionchange", this, this.selections);
12766             }
12767         }
12768     },
12769
12770     /**
12771      * Returns true if the passed node is selected
12772      * @param {HTMLElement/Number} node The node or node index
12773      * @return {Boolean}
12774      */
12775     isSelected : function(node){
12776         var s = this.selections;
12777         if(s.length < 1){
12778             return false;
12779         }
12780         node = this.getNode(node);
12781         return s.indexOf(node) !== -1;
12782     },
12783
12784     /**
12785      * Selects nodes.
12786      * @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
12787      * @param {Boolean} keepExisting (optional) true to keep existing selections
12788      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12789      */
12790     select : function(nodeInfo, keepExisting, suppressEvent){
12791         if(nodeInfo instanceof Array){
12792             if(!keepExisting){
12793                 this.clearSelections(true);
12794             }
12795             for(var i = 0, len = nodeInfo.length; i < len; i++){
12796                 this.select(nodeInfo[i], true, true);
12797             }
12798             return;
12799         } 
12800         var node = this.getNode(nodeInfo);
12801         if(!node || this.isSelected(node)){
12802             return; // already selected.
12803         }
12804         if(!keepExisting){
12805             this.clearSelections(true);
12806         }
12807         
12808         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12809             Roo.fly(node).addClass(this.selectedClass);
12810             this.selections.push(node);
12811             if(!suppressEvent){
12812                 this.fireEvent("selectionchange", this, this.selections);
12813             }
12814         }
12815         
12816         
12817     },
12818       /**
12819      * Unselects nodes.
12820      * @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
12821      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12822      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12823      */
12824     unselect : function(nodeInfo, keepExisting, suppressEvent)
12825     {
12826         if(nodeInfo instanceof Array){
12827             Roo.each(this.selections, function(s) {
12828                 this.unselect(s, nodeInfo);
12829             }, this);
12830             return;
12831         }
12832         var node = this.getNode(nodeInfo);
12833         if(!node || !this.isSelected(node)){
12834             //Roo.log("not selected");
12835             return; // not selected.
12836         }
12837         // fireevent???
12838         var ns = [];
12839         Roo.each(this.selections, function(s) {
12840             if (s == node ) {
12841                 Roo.fly(node).removeClass(this.selectedClass);
12842
12843                 return;
12844             }
12845             ns.push(s);
12846         },this);
12847         
12848         this.selections= ns;
12849         this.fireEvent("selectionchange", this, this.selections);
12850     },
12851
12852     /**
12853      * Gets a template node.
12854      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12855      * @return {HTMLElement} The node or null if it wasn't found
12856      */
12857     getNode : function(nodeInfo){
12858         if(typeof nodeInfo == "string"){
12859             return document.getElementById(nodeInfo);
12860         }else if(typeof nodeInfo == "number"){
12861             return this.nodes[nodeInfo];
12862         }
12863         return nodeInfo;
12864     },
12865
12866     /**
12867      * Gets a range template nodes.
12868      * @param {Number} startIndex
12869      * @param {Number} endIndex
12870      * @return {Array} An array of nodes
12871      */
12872     getNodes : function(start, end){
12873         var ns = this.nodes;
12874         start = start || 0;
12875         end = typeof end == "undefined" ? ns.length - 1 : end;
12876         var nodes = [];
12877         if(start <= end){
12878             for(var i = start; i <= end; i++){
12879                 nodes.push(ns[i]);
12880             }
12881         } else{
12882             for(var i = start; i >= end; i--){
12883                 nodes.push(ns[i]);
12884             }
12885         }
12886         return nodes;
12887     },
12888
12889     /**
12890      * Finds the index of the passed node
12891      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12892      * @return {Number} The index of the node or -1
12893      */
12894     indexOf : function(node){
12895         node = this.getNode(node);
12896         if(typeof node.nodeIndex == "number"){
12897             return node.nodeIndex;
12898         }
12899         var ns = this.nodes;
12900         for(var i = 0, len = ns.length; i < len; i++){
12901             if(ns[i] == node){
12902                 return i;
12903             }
12904         }
12905         return -1;
12906     }
12907 });
12908 /*
12909  * - LGPL
12910  *
12911  * based on jquery fullcalendar
12912  * 
12913  */
12914
12915 Roo.bootstrap = Roo.bootstrap || {};
12916 /**
12917  * @class Roo.bootstrap.Calendar
12918  * @extends Roo.bootstrap.Component
12919  * Bootstrap Calendar class
12920  * @cfg {Boolean} loadMask (true|false) default false
12921  * @cfg {Object} header generate the user specific header of the calendar, default false
12922
12923  * @constructor
12924  * Create a new Container
12925  * @param {Object} config The config object
12926  */
12927
12928
12929
12930 Roo.bootstrap.Calendar = function(config){
12931     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12932      this.addEvents({
12933         /**
12934              * @event select
12935              * Fires when a date is selected
12936              * @param {DatePicker} this
12937              * @param {Date} date The selected date
12938              */
12939         'select': true,
12940         /**
12941              * @event monthchange
12942              * Fires when the displayed month changes 
12943              * @param {DatePicker} this
12944              * @param {Date} date The selected month
12945              */
12946         'monthchange': true,
12947         /**
12948              * @event evententer
12949              * Fires when mouse over an event
12950              * @param {Calendar} this
12951              * @param {event} Event
12952              */
12953         'evententer': true,
12954         /**
12955              * @event eventleave
12956              * Fires when the mouse leaves an
12957              * @param {Calendar} this
12958              * @param {event}
12959              */
12960         'eventleave': true,
12961         /**
12962              * @event eventclick
12963              * Fires when the mouse click an
12964              * @param {Calendar} this
12965              * @param {event}
12966              */
12967         'eventclick': true
12968         
12969     });
12970
12971 };
12972
12973 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12974     
12975      /**
12976      * @cfg {Number} startDay
12977      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12978      */
12979     startDay : 0,
12980     
12981     loadMask : false,
12982     
12983     header : false,
12984       
12985     getAutoCreate : function(){
12986         
12987         
12988         var fc_button = function(name, corner, style, content ) {
12989             return Roo.apply({},{
12990                 tag : 'span',
12991                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12992                          (corner.length ?
12993                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12994                             ''
12995                         ),
12996                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12997                 unselectable: 'on'
12998             });
12999         };
13000         
13001         var header = {};
13002         
13003         if(!this.header){
13004             header = {
13005                 tag : 'table',
13006                 cls : 'fc-header',
13007                 style : 'width:100%',
13008                 cn : [
13009                     {
13010                         tag: 'tr',
13011                         cn : [
13012                             {
13013                                 tag : 'td',
13014                                 cls : 'fc-header-left',
13015                                 cn : [
13016                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13017                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13018                                     { tag: 'span', cls: 'fc-header-space' },
13019                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13020
13021
13022                                 ]
13023                             },
13024
13025                             {
13026                                 tag : 'td',
13027                                 cls : 'fc-header-center',
13028                                 cn : [
13029                                     {
13030                                         tag: 'span',
13031                                         cls: 'fc-header-title',
13032                                         cn : {
13033                                             tag: 'H2',
13034                                             html : 'month / year'
13035                                         }
13036                                     }
13037
13038                                 ]
13039                             },
13040                             {
13041                                 tag : 'td',
13042                                 cls : 'fc-header-right',
13043                                 cn : [
13044                               /*      fc_button('month', 'left', '', 'month' ),
13045                                     fc_button('week', '', '', 'week' ),
13046                                     fc_button('day', 'right', '', 'day' )
13047                                 */    
13048
13049                                 ]
13050                             }
13051
13052                         ]
13053                     }
13054                 ]
13055             };
13056         }
13057         
13058         header = this.header;
13059         
13060        
13061         var cal_heads = function() {
13062             var ret = [];
13063             // fixme - handle this.
13064             
13065             for (var i =0; i < Date.dayNames.length; i++) {
13066                 var d = Date.dayNames[i];
13067                 ret.push({
13068                     tag: 'th',
13069                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13070                     html : d.substring(0,3)
13071                 });
13072                 
13073             }
13074             ret[0].cls += ' fc-first';
13075             ret[6].cls += ' fc-last';
13076             return ret;
13077         };
13078         var cal_cell = function(n) {
13079             return  {
13080                 tag: 'td',
13081                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13082                 cn : [
13083                     {
13084                         cn : [
13085                             {
13086                                 cls: 'fc-day-number',
13087                                 html: 'D'
13088                             },
13089                             {
13090                                 cls: 'fc-day-content',
13091                              
13092                                 cn : [
13093                                      {
13094                                         style: 'position: relative;' // height: 17px;
13095                                     }
13096                                 ]
13097                             }
13098                             
13099                             
13100                         ]
13101                     }
13102                 ]
13103                 
13104             }
13105         };
13106         var cal_rows = function() {
13107             
13108             var ret = []
13109             for (var r = 0; r < 6; r++) {
13110                 var row= {
13111                     tag : 'tr',
13112                     cls : 'fc-week',
13113                     cn : []
13114                 };
13115                 
13116                 for (var i =0; i < Date.dayNames.length; i++) {
13117                     var d = Date.dayNames[i];
13118                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13119
13120                 }
13121                 row.cn[0].cls+=' fc-first';
13122                 row.cn[0].cn[0].style = 'min-height:90px';
13123                 row.cn[6].cls+=' fc-last';
13124                 ret.push(row);
13125                 
13126             }
13127             ret[0].cls += ' fc-first';
13128             ret[4].cls += ' fc-prev-last';
13129             ret[5].cls += ' fc-last';
13130             return ret;
13131             
13132         };
13133         
13134         var cal_table = {
13135             tag: 'table',
13136             cls: 'fc-border-separate',
13137             style : 'width:100%',
13138             cellspacing  : 0,
13139             cn : [
13140                 { 
13141                     tag: 'thead',
13142                     cn : [
13143                         { 
13144                             tag: 'tr',
13145                             cls : 'fc-first fc-last',
13146                             cn : cal_heads()
13147                         }
13148                     ]
13149                 },
13150                 { 
13151                     tag: 'tbody',
13152                     cn : cal_rows()
13153                 }
13154                   
13155             ]
13156         };
13157          
13158          var cfg = {
13159             cls : 'fc fc-ltr',
13160             cn : [
13161                 header,
13162                 {
13163                     cls : 'fc-content',
13164                     style : "position: relative;",
13165                     cn : [
13166                         {
13167                             cls : 'fc-view fc-view-month fc-grid',
13168                             style : 'position: relative',
13169                             unselectable : 'on',
13170                             cn : [
13171                                 {
13172                                     cls : 'fc-event-container',
13173                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13174                                 },
13175                                 cal_table
13176                             ]
13177                         }
13178                     ]
13179     
13180                 }
13181            ] 
13182             
13183         };
13184         
13185          
13186         
13187         return cfg;
13188     },
13189     
13190     
13191     initEvents : function()
13192     {
13193         if(!this.store){
13194             throw "can not find store for calendar";
13195         }
13196         
13197         var mark = {
13198             tag: "div",
13199             cls:"x-dlg-mask",
13200             style: "text-align:center",
13201             cn: [
13202                 {
13203                     tag: "div",
13204                     style: "background-color:white;width:50%;margin:250 auto",
13205                     cn: [
13206                         {
13207                             tag: "img",
13208                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13209                         },
13210                         {
13211                             tag: "span",
13212                             html: "Loading"
13213                         }
13214                         
13215                     ]
13216                 }
13217             ]
13218         }
13219         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13220         
13221         var size = this.el.select('.fc-content', true).first().getSize();
13222         this.maskEl.setSize(size.width, size.height);
13223         this.maskEl.enableDisplayMode("block");
13224         if(!this.loadMask){
13225             this.maskEl.hide();
13226         }
13227         
13228         this.store = Roo.factory(this.store, Roo.data);
13229         this.store.on('load', this.onLoad, this);
13230         this.store.on('beforeload', this.onBeforeLoad, this);
13231         
13232         this.resize();
13233         
13234         this.cells = this.el.select('.fc-day',true);
13235         //Roo.log(this.cells);
13236         this.textNodes = this.el.query('.fc-day-number');
13237         this.cells.addClassOnOver('fc-state-hover');
13238         
13239         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13240         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13241         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13242         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13243         
13244         this.on('monthchange', this.onMonthChange, this);
13245         
13246         this.update(new Date().clearTime());
13247     },
13248     
13249     resize : function() {
13250         var sz  = this.el.getSize();
13251         
13252         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13253         this.el.select('.fc-day-content div',true).setHeight(34);
13254     },
13255     
13256     
13257     // private
13258     showPrevMonth : function(e){
13259         this.update(this.activeDate.add("mo", -1));
13260     },
13261     showToday : function(e){
13262         this.update(new Date().clearTime());
13263     },
13264     // private
13265     showNextMonth : function(e){
13266         this.update(this.activeDate.add("mo", 1));
13267     },
13268
13269     // private
13270     showPrevYear : function(){
13271         this.update(this.activeDate.add("y", -1));
13272     },
13273
13274     // private
13275     showNextYear : function(){
13276         this.update(this.activeDate.add("y", 1));
13277     },
13278
13279     
13280    // private
13281     update : function(date)
13282     {
13283         var vd = this.activeDate;
13284         this.activeDate = date;
13285 //        if(vd && this.el){
13286 //            var t = date.getTime();
13287 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13288 //                Roo.log('using add remove');
13289 //                
13290 //                this.fireEvent('monthchange', this, date);
13291 //                
13292 //                this.cells.removeClass("fc-state-highlight");
13293 //                this.cells.each(function(c){
13294 //                   if(c.dateValue == t){
13295 //                       c.addClass("fc-state-highlight");
13296 //                       setTimeout(function(){
13297 //                            try{c.dom.firstChild.focus();}catch(e){}
13298 //                       }, 50);
13299 //                       return false;
13300 //                   }
13301 //                   return true;
13302 //                });
13303 //                return;
13304 //            }
13305 //        }
13306         
13307         var days = date.getDaysInMonth();
13308         
13309         var firstOfMonth = date.getFirstDateOfMonth();
13310         var startingPos = firstOfMonth.getDay()-this.startDay;
13311         
13312         if(startingPos < this.startDay){
13313             startingPos += 7;
13314         }
13315         
13316         var pm = date.add(Date.MONTH, -1);
13317         var prevStart = pm.getDaysInMonth()-startingPos;
13318 //        
13319         this.cells = this.el.select('.fc-day',true);
13320         this.textNodes = this.el.query('.fc-day-number');
13321         this.cells.addClassOnOver('fc-state-hover');
13322         
13323         var cells = this.cells.elements;
13324         var textEls = this.textNodes;
13325         
13326         Roo.each(cells, function(cell){
13327             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13328         });
13329         
13330         days += startingPos;
13331
13332         // convert everything to numbers so it's fast
13333         var day = 86400000;
13334         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13335         //Roo.log(d);
13336         //Roo.log(pm);
13337         //Roo.log(prevStart);
13338         
13339         var today = new Date().clearTime().getTime();
13340         var sel = date.clearTime().getTime();
13341         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13342         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13343         var ddMatch = this.disabledDatesRE;
13344         var ddText = this.disabledDatesText;
13345         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13346         var ddaysText = this.disabledDaysText;
13347         var format = this.format;
13348         
13349         var setCellClass = function(cal, cell){
13350             cell.row = 0;
13351             cell.events = [];
13352             cell.more = [];
13353             //Roo.log('set Cell Class');
13354             cell.title = "";
13355             var t = d.getTime();
13356             
13357             //Roo.log(d);
13358             
13359             cell.dateValue = t;
13360             if(t == today){
13361                 cell.className += " fc-today";
13362                 cell.className += " fc-state-highlight";
13363                 cell.title = cal.todayText;
13364             }
13365             if(t == sel){
13366                 // disable highlight in other month..
13367                 //cell.className += " fc-state-highlight";
13368                 
13369             }
13370             // disabling
13371             if(t < min) {
13372                 cell.className = " fc-state-disabled";
13373                 cell.title = cal.minText;
13374                 return;
13375             }
13376             if(t > max) {
13377                 cell.className = " fc-state-disabled";
13378                 cell.title = cal.maxText;
13379                 return;
13380             }
13381             if(ddays){
13382                 if(ddays.indexOf(d.getDay()) != -1){
13383                     cell.title = ddaysText;
13384                     cell.className = " fc-state-disabled";
13385                 }
13386             }
13387             if(ddMatch && format){
13388                 var fvalue = d.dateFormat(format);
13389                 if(ddMatch.test(fvalue)){
13390                     cell.title = ddText.replace("%0", fvalue);
13391                     cell.className = " fc-state-disabled";
13392                 }
13393             }
13394             
13395             if (!cell.initialClassName) {
13396                 cell.initialClassName = cell.dom.className;
13397             }
13398             
13399             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13400         };
13401
13402         var i = 0;
13403         
13404         for(; i < startingPos; i++) {
13405             textEls[i].innerHTML = (++prevStart);
13406             d.setDate(d.getDate()+1);
13407             
13408             cells[i].className = "fc-past fc-other-month";
13409             setCellClass(this, cells[i]);
13410         }
13411         
13412         var intDay = 0;
13413         
13414         for(; i < days; i++){
13415             intDay = i - startingPos + 1;
13416             textEls[i].innerHTML = (intDay);
13417             d.setDate(d.getDate()+1);
13418             
13419             cells[i].className = ''; // "x-date-active";
13420             setCellClass(this, cells[i]);
13421         }
13422         var extraDays = 0;
13423         
13424         for(; i < 42; i++) {
13425             textEls[i].innerHTML = (++extraDays);
13426             d.setDate(d.getDate()+1);
13427             
13428             cells[i].className = "fc-future fc-other-month";
13429             setCellClass(this, cells[i]);
13430         }
13431         
13432         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13433         
13434         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13435         
13436         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13437         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13438         
13439         if(totalRows != 6){
13440             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13441             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13442         }
13443         
13444         this.fireEvent('monthchange', this, date);
13445         
13446         
13447         /*
13448         if(!this.internalRender){
13449             var main = this.el.dom.firstChild;
13450             var w = main.offsetWidth;
13451             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13452             Roo.fly(main).setWidth(w);
13453             this.internalRender = true;
13454             // opera does not respect the auto grow header center column
13455             // then, after it gets a width opera refuses to recalculate
13456             // without a second pass
13457             if(Roo.isOpera && !this.secondPass){
13458                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13459                 this.secondPass = true;
13460                 this.update.defer(10, this, [date]);
13461             }
13462         }
13463         */
13464         
13465     },
13466     
13467     findCell : function(dt) {
13468         dt = dt.clearTime().getTime();
13469         var ret = false;
13470         this.cells.each(function(c){
13471             //Roo.log("check " +c.dateValue + '?=' + dt);
13472             if(c.dateValue == dt){
13473                 ret = c;
13474                 return false;
13475             }
13476             return true;
13477         });
13478         
13479         return ret;
13480     },
13481     
13482     findCells : function(ev) {
13483         var s = ev.start.clone().clearTime().getTime();
13484        // Roo.log(s);
13485         var e= ev.end.clone().clearTime().getTime();
13486        // Roo.log(e);
13487         var ret = [];
13488         this.cells.each(function(c){
13489              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13490             
13491             if(c.dateValue > e){
13492                 return ;
13493             }
13494             if(c.dateValue < s){
13495                 return ;
13496             }
13497             ret.push(c);
13498         });
13499         
13500         return ret;    
13501     },
13502     
13503 //    findBestRow: function(cells)
13504 //    {
13505 //        var ret = 0;
13506 //        
13507 //        for (var i =0 ; i < cells.length;i++) {
13508 //            ret  = Math.max(cells[i].rows || 0,ret);
13509 //        }
13510 //        return ret;
13511 //        
13512 //    },
13513     
13514     
13515     addItem : function(ev)
13516     {
13517         // look for vertical location slot in
13518         var cells = this.findCells(ev);
13519         
13520 //        ev.row = this.findBestRow(cells);
13521         
13522         // work out the location.
13523         
13524         var crow = false;
13525         var rows = [];
13526         for(var i =0; i < cells.length; i++) {
13527             
13528             cells[i].row = cells[0].row;
13529             
13530             if(i == 0){
13531                 cells[i].row = cells[i].row + 1;
13532             }
13533             
13534             if (!crow) {
13535                 crow = {
13536                     start : cells[i],
13537                     end :  cells[i]
13538                 };
13539                 continue;
13540             }
13541             if (crow.start.getY() == cells[i].getY()) {
13542                 // on same row.
13543                 crow.end = cells[i];
13544                 continue;
13545             }
13546             // different row.
13547             rows.push(crow);
13548             crow = {
13549                 start: cells[i],
13550                 end : cells[i]
13551             };
13552             
13553         }
13554         
13555         rows.push(crow);
13556         ev.els = [];
13557         ev.rows = rows;
13558         ev.cells = cells;
13559         
13560         cells[0].events.push(ev);
13561         
13562         this.calevents.push(ev);
13563     },
13564     
13565     clearEvents: function() {
13566         
13567         if(!this.calevents){
13568             return;
13569         }
13570         
13571         Roo.each(this.cells.elements, function(c){
13572             c.row = 0;
13573             c.events = [];
13574             c.more = [];
13575         });
13576         
13577         Roo.each(this.calevents, function(e) {
13578             Roo.each(e.els, function(el) {
13579                 el.un('mouseenter' ,this.onEventEnter, this);
13580                 el.un('mouseleave' ,this.onEventLeave, this);
13581                 el.remove();
13582             },this);
13583         },this);
13584         
13585         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13586             e.remove();
13587         });
13588         
13589     },
13590     
13591     renderEvents: function()
13592     {   
13593         var _this = this;
13594         
13595         this.cells.each(function(c) {
13596             
13597             if(c.row < 5){
13598                 return;
13599             }
13600             
13601             var ev = c.events;
13602             
13603             var r = 4;
13604             if(c.row != c.events.length){
13605                 r = 4 - (4 - (c.row - c.events.length));
13606             }
13607             
13608             c.events = ev.slice(0, r);
13609             c.more = ev.slice(r);
13610             
13611             if(c.more.length && c.more.length == 1){
13612                 c.events.push(c.more.pop());
13613             }
13614             
13615             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13616             
13617         });
13618             
13619         this.cells.each(function(c) {
13620             
13621             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13622             
13623             
13624             for (var e = 0; e < c.events.length; e++){
13625                 var ev = c.events[e];
13626                 var rows = ev.rows;
13627                 
13628                 for(var i = 0; i < rows.length; i++) {
13629                 
13630                     // how many rows should it span..
13631
13632                     var  cfg = {
13633                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13634                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13635
13636                         unselectable : "on",
13637                         cn : [
13638                             {
13639                                 cls: 'fc-event-inner',
13640                                 cn : [
13641     //                                {
13642     //                                  tag:'span',
13643     //                                  cls: 'fc-event-time',
13644     //                                  html : cells.length > 1 ? '' : ev.time
13645     //                                },
13646                                     {
13647                                       tag:'span',
13648                                       cls: 'fc-event-title',
13649                                       html : String.format('{0}', ev.title)
13650                                     }
13651
13652
13653                                 ]
13654                             },
13655                             {
13656                                 cls: 'ui-resizable-handle ui-resizable-e',
13657                                 html : '&nbsp;&nbsp;&nbsp'
13658                             }
13659
13660                         ]
13661                     };
13662
13663                     if (i == 0) {
13664                         cfg.cls += ' fc-event-start';
13665                     }
13666                     if ((i+1) == rows.length) {
13667                         cfg.cls += ' fc-event-end';
13668                     }
13669
13670                     var ctr = _this.el.select('.fc-event-container',true).first();
13671                     var cg = ctr.createChild(cfg);
13672
13673                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13674                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13675
13676                     var r = (c.more.length) ? 1 : 0;
13677                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13678                     cg.setWidth(ebox.right - sbox.x -2);
13679
13680                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13681                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13682                     cg.on('click', _this.onEventClick, _this, ev);
13683
13684                     ev.els.push(cg);
13685                     
13686                 }
13687                 
13688             }
13689             
13690             
13691             if(c.more.length){
13692                 var  cfg = {
13693                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13694                     style : 'position: absolute',
13695                     unselectable : "on",
13696                     cn : [
13697                         {
13698                             cls: 'fc-event-inner',
13699                             cn : [
13700                                 {
13701                                   tag:'span',
13702                                   cls: 'fc-event-title',
13703                                   html : 'More'
13704                                 }
13705
13706
13707                             ]
13708                         },
13709                         {
13710                             cls: 'ui-resizable-handle ui-resizable-e',
13711                             html : '&nbsp;&nbsp;&nbsp'
13712                         }
13713
13714                     ]
13715                 };
13716
13717                 var ctr = _this.el.select('.fc-event-container',true).first();
13718                 var cg = ctr.createChild(cfg);
13719
13720                 var sbox = c.select('.fc-day-content',true).first().getBox();
13721                 var ebox = c.select('.fc-day-content',true).first().getBox();
13722                 //Roo.log(cg);
13723                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13724                 cg.setWidth(ebox.right - sbox.x -2);
13725
13726                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13727                 
13728             }
13729             
13730         });
13731         
13732         
13733         
13734     },
13735     
13736     onEventEnter: function (e, el,event,d) {
13737         this.fireEvent('evententer', this, el, event);
13738     },
13739     
13740     onEventLeave: function (e, el,event,d) {
13741         this.fireEvent('eventleave', this, el, event);
13742     },
13743     
13744     onEventClick: function (e, el,event,d) {
13745         this.fireEvent('eventclick', this, el, event);
13746     },
13747     
13748     onMonthChange: function () {
13749         this.store.load();
13750     },
13751     
13752     onMoreEventClick: function(e, el, more)
13753     {
13754         var _this = this;
13755         
13756         this.calpopover.placement = 'right';
13757         this.calpopover.setTitle('More');
13758         
13759         this.calpopover.setContent('');
13760         
13761         var ctr = this.calpopover.el.select('.popover-content', true).first();
13762         
13763         Roo.each(more, function(m){
13764             var cfg = {
13765                 cls : 'fc-event-hori fc-event-draggable',
13766                 html : m.title
13767             }
13768             var cg = ctr.createChild(cfg);
13769             
13770             cg.on('click', _this.onEventClick, _this, m);
13771         });
13772         
13773         this.calpopover.show(el);
13774         
13775         
13776     },
13777     
13778     onLoad: function () 
13779     {   
13780         this.calevents = [];
13781         var cal = this;
13782         
13783         if(this.store.getCount() > 0){
13784             this.store.data.each(function(d){
13785                cal.addItem({
13786                     id : d.data.id,
13787                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13788                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13789                     time : d.data.start_time,
13790                     title : d.data.title,
13791                     description : d.data.description,
13792                     venue : d.data.venue
13793                 });
13794             });
13795         }
13796         
13797         this.renderEvents();
13798         
13799         if(this.calevents.length && this.loadMask){
13800             this.maskEl.hide();
13801         }
13802     },
13803     
13804     onBeforeLoad: function()
13805     {
13806         this.clearEvents();
13807         if(this.loadMask){
13808             this.maskEl.show();
13809         }
13810     }
13811 });
13812
13813  
13814  /*
13815  * - LGPL
13816  *
13817  * element
13818  * 
13819  */
13820
13821 /**
13822  * @class Roo.bootstrap.Popover
13823  * @extends Roo.bootstrap.Component
13824  * Bootstrap Popover class
13825  * @cfg {String} html contents of the popover   (or false to use children..)
13826  * @cfg {String} title of popover (or false to hide)
13827  * @cfg {String} placement how it is placed
13828  * @cfg {String} trigger click || hover (or false to trigger manually)
13829  * @cfg {String} over what (parent or false to trigger manually.)
13830  * @cfg {Number} delay - delay before showing
13831  
13832  * @constructor
13833  * Create a new Popover
13834  * @param {Object} config The config object
13835  */
13836
13837 Roo.bootstrap.Popover = function(config){
13838     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13839 };
13840
13841 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13842     
13843     title: 'Fill in a title',
13844     html: false,
13845     
13846     placement : 'right',
13847     trigger : 'hover', // hover
13848     
13849     delay : 0,
13850     
13851     over: 'parent',
13852     
13853     can_build_overlaid : false,
13854     
13855     getChildContainer : function()
13856     {
13857         return this.el.select('.popover-content',true).first();
13858     },
13859     
13860     getAutoCreate : function(){
13861          Roo.log('make popover?');
13862         var cfg = {
13863            cls : 'popover roo-dynamic',
13864            style: 'display:block',
13865            cn : [
13866                 {
13867                     cls : 'arrow'
13868                 },
13869                 {
13870                     cls : 'popover-inner',
13871                     cn : [
13872                         {
13873                             tag: 'h3',
13874                             cls: 'popover-title',
13875                             html : this.title
13876                         },
13877                         {
13878                             cls : 'popover-content',
13879                             html : this.html
13880                         }
13881                     ]
13882                     
13883                 }
13884            ]
13885         };
13886         
13887         return cfg;
13888     },
13889     setTitle: function(str)
13890     {
13891         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13892     },
13893     setContent: function(str)
13894     {
13895         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13896     },
13897     // as it get's added to the bottom of the page.
13898     onRender : function(ct, position)
13899     {
13900         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13901         if(!this.el){
13902             var cfg = Roo.apply({},  this.getAutoCreate());
13903             cfg.id = Roo.id();
13904             
13905             if (this.cls) {
13906                 cfg.cls += ' ' + this.cls;
13907             }
13908             if (this.style) {
13909                 cfg.style = this.style;
13910             }
13911             Roo.log("adding to ")
13912             this.el = Roo.get(document.body).createChild(cfg, position);
13913             Roo.log(this.el);
13914         }
13915         this.initEvents();
13916     },
13917     
13918     initEvents : function()
13919     {
13920         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13921         this.el.enableDisplayMode('block');
13922         this.el.hide();
13923         if (this.over === false) {
13924             return; 
13925         }
13926         if (this.triggers === false) {
13927             return;
13928         }
13929         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13930         var triggers = this.trigger ? this.trigger.split(' ') : [];
13931         Roo.each(triggers, function(trigger) {
13932         
13933             if (trigger == 'click') {
13934                 on_el.on('click', this.toggle, this);
13935             } else if (trigger != 'manual') {
13936                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13937                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13938       
13939                 on_el.on(eventIn  ,this.enter, this);
13940                 on_el.on(eventOut, this.leave, this);
13941             }
13942         }, this);
13943         
13944     },
13945     
13946     
13947     // private
13948     timeout : null,
13949     hoverState : null,
13950     
13951     toggle : function () {
13952         this.hoverState == 'in' ? this.leave() : this.enter();
13953     },
13954     
13955     enter : function () {
13956        
13957     
13958         clearTimeout(this.timeout);
13959     
13960         this.hoverState = 'in'
13961     
13962         if (!this.delay || !this.delay.show) {
13963             this.show();
13964             return 
13965         }
13966         var _t = this;
13967         this.timeout = setTimeout(function () {
13968             if (_t.hoverState == 'in') {
13969                 _t.show();
13970             }
13971         }, this.delay.show)
13972     },
13973     leave : function() {
13974         clearTimeout(this.timeout);
13975     
13976         this.hoverState = 'out'
13977     
13978         if (!this.delay || !this.delay.hide) {
13979             this.hide();
13980             return 
13981         }
13982         var _t = this;
13983         this.timeout = setTimeout(function () {
13984             if (_t.hoverState == 'out') {
13985                 _t.hide();
13986             }
13987         }, this.delay.hide)
13988     },
13989     
13990     show : function (on_el)
13991     {
13992         if (!on_el) {
13993             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13994         }
13995         // set content.
13996         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13997         if (this.html !== false) {
13998             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13999         }
14000         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14001         if (!this.title.length) {
14002             this.el.select('.popover-title',true).hide();
14003         }
14004         
14005         var placement = typeof this.placement == 'function' ?
14006             this.placement.call(this, this.el, on_el) :
14007             this.placement;
14008             
14009         var autoToken = /\s?auto?\s?/i;
14010         var autoPlace = autoToken.test(placement);
14011         if (autoPlace) {
14012             placement = placement.replace(autoToken, '') || 'top';
14013         }
14014         
14015         //this.el.detach()
14016         //this.el.setXY([0,0]);
14017         this.el.show();
14018         this.el.dom.style.display='block';
14019         this.el.addClass(placement);
14020         
14021         //this.el.appendTo(on_el);
14022         
14023         var p = this.getPosition();
14024         var box = this.el.getBox();
14025         
14026         if (autoPlace) {
14027             // fixme..
14028         }
14029         var align = Roo.bootstrap.Popover.alignment[placement]
14030         this.el.alignTo(on_el, align[0],align[1]);
14031         //var arrow = this.el.select('.arrow',true).first();
14032         //arrow.set(align[2], 
14033         
14034         this.el.addClass('in');
14035         this.hoverState = null;
14036         
14037         if (this.el.hasClass('fade')) {
14038             // fade it?
14039         }
14040         
14041     },
14042     hide : function()
14043     {
14044         this.el.setXY([0,0]);
14045         this.el.removeClass('in');
14046         this.el.hide();
14047         
14048     }
14049     
14050 });
14051
14052 Roo.bootstrap.Popover.alignment = {
14053     'left' : ['r-l', [-10,0], 'right'],
14054     'right' : ['l-r', [10,0], 'left'],
14055     'bottom' : ['t-b', [0,10], 'top'],
14056     'top' : [ 'b-t', [0,-10], 'bottom']
14057 };
14058
14059  /*
14060  * - LGPL
14061  *
14062  * Progress
14063  * 
14064  */
14065
14066 /**
14067  * @class Roo.bootstrap.Progress
14068  * @extends Roo.bootstrap.Component
14069  * Bootstrap Progress class
14070  * @cfg {Boolean} striped striped of the progress bar
14071  * @cfg {Boolean} active animated of the progress bar
14072  * 
14073  * 
14074  * @constructor
14075  * Create a new Progress
14076  * @param {Object} config The config object
14077  */
14078
14079 Roo.bootstrap.Progress = function(config){
14080     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14081 };
14082
14083 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14084     
14085     striped : false,
14086     active: false,
14087     
14088     getAutoCreate : function(){
14089         var cfg = {
14090             tag: 'div',
14091             cls: 'progress'
14092         };
14093         
14094         
14095         if(this.striped){
14096             cfg.cls += ' progress-striped';
14097         }
14098       
14099         if(this.active){
14100             cfg.cls += ' active';
14101         }
14102         
14103         
14104         return cfg;
14105     }
14106    
14107 });
14108
14109  
14110
14111  /*
14112  * - LGPL
14113  *
14114  * ProgressBar
14115  * 
14116  */
14117
14118 /**
14119  * @class Roo.bootstrap.ProgressBar
14120  * @extends Roo.bootstrap.Component
14121  * Bootstrap ProgressBar class
14122  * @cfg {Number} aria_valuenow aria-value now
14123  * @cfg {Number} aria_valuemin aria-value min
14124  * @cfg {Number} aria_valuemax aria-value max
14125  * @cfg {String} label label for the progress bar
14126  * @cfg {String} panel (success | info | warning | danger )
14127  * @cfg {String} role role of the progress bar
14128  * @cfg {String} sr_only text
14129  * 
14130  * 
14131  * @constructor
14132  * Create a new ProgressBar
14133  * @param {Object} config The config object
14134  */
14135
14136 Roo.bootstrap.ProgressBar = function(config){
14137     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14138 };
14139
14140 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14141     
14142     aria_valuenow : 0,
14143     aria_valuemin : 0,
14144     aria_valuemax : 100,
14145     label : false,
14146     panel : false,
14147     role : false,
14148     sr_only: false,
14149     
14150     getAutoCreate : function()
14151     {
14152         
14153         var cfg = {
14154             tag: 'div',
14155             cls: 'progress-bar',
14156             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14157         };
14158         
14159         if(this.sr_only){
14160             cfg.cn = {
14161                 tag: 'span',
14162                 cls: 'sr-only',
14163                 html: this.sr_only
14164             }
14165         }
14166         
14167         if(this.role){
14168             cfg.role = this.role;
14169         }
14170         
14171         if(this.aria_valuenow){
14172             cfg['aria-valuenow'] = this.aria_valuenow;
14173         }
14174         
14175         if(this.aria_valuemin){
14176             cfg['aria-valuemin'] = this.aria_valuemin;
14177         }
14178         
14179         if(this.aria_valuemax){
14180             cfg['aria-valuemax'] = this.aria_valuemax;
14181         }
14182         
14183         if(this.label && !this.sr_only){
14184             cfg.html = this.label;
14185         }
14186         
14187         if(this.panel){
14188             cfg.cls += ' progress-bar-' + this.panel;
14189         }
14190         
14191         return cfg;
14192     },
14193     
14194     update : function(aria_valuenow)
14195     {
14196         this.aria_valuenow = aria_valuenow;
14197         
14198         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14199     }
14200    
14201 });
14202
14203  
14204
14205  /*
14206  * - LGPL
14207  *
14208  * column
14209  * 
14210  */
14211
14212 /**
14213  * @class Roo.bootstrap.TabGroup
14214  * @extends Roo.bootstrap.Column
14215  * Bootstrap Column class
14216  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14217  * @cfg {Boolean} carousel true to make the group behave like a carousel
14218  * 
14219  * @constructor
14220  * Create a new TabGroup
14221  * @param {Object} config The config object
14222  */
14223
14224 Roo.bootstrap.TabGroup = function(config){
14225     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14226     if (!this.navId) {
14227         this.navId = Roo.id();
14228     }
14229     this.tabs = [];
14230     Roo.bootstrap.TabGroup.register(this);
14231     
14232 };
14233
14234 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14235     
14236     carousel : false,
14237     transition : false,
14238      
14239     getAutoCreate : function()
14240     {
14241         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14242         
14243         cfg.cls += ' tab-content';
14244         
14245         if (this.carousel) {
14246             cfg.cls += ' carousel slide';
14247             cfg.cn = [{
14248                cls : 'carousel-inner'
14249             }]
14250         }
14251         
14252         
14253         return cfg;
14254     },
14255     getChildContainer : function()
14256     {
14257         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14258     },
14259     
14260     /**
14261     * register a Navigation item
14262     * @param {Roo.bootstrap.NavItem} the navitem to add
14263     */
14264     register : function(item)
14265     {
14266         this.tabs.push( item);
14267         item.navId = this.navId; // not really needed..
14268     
14269     },
14270     
14271     getActivePanel : function()
14272     {
14273         var r = false;
14274         Roo.each(this.tabs, function(t) {
14275             if (t.active) {
14276                 r = t;
14277                 return false;
14278             }
14279             return null;
14280         });
14281         return r;
14282         
14283     },
14284     getPanelByName : function(n)
14285     {
14286         var r = false;
14287         Roo.each(this.tabs, function(t) {
14288             if (t.tabId == n) {
14289                 r = t;
14290                 return false;
14291             }
14292             return null;
14293         });
14294         return r;
14295     },
14296     indexOfPanel : function(p)
14297     {
14298         var r = false;
14299         Roo.each(this.tabs, function(t,i) {
14300             if (t.tabId == p.tabId) {
14301                 r = i;
14302                 return false;
14303             }
14304             return null;
14305         });
14306         return r;
14307     },
14308     /**
14309      * show a specific panel
14310      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14311      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14312      */
14313     showPanel : function (pan)
14314     {
14315         
14316         if (typeof(pan) == 'number') {
14317             pan = this.tabs[pan];
14318         }
14319         if (typeof(pan) == 'string') {
14320             pan = this.getPanelByName(pan);
14321         }
14322         if (pan.tabId == this.getActivePanel().tabId) {
14323             return true;
14324         }
14325         var cur = this.getActivePanel();
14326         
14327         if (false === cur.fireEvent('beforedeactivate')) {
14328             return false;
14329         }
14330         
14331         if (this.carousel) {
14332             this.transition = true;
14333             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14334             var lr = dir == 'next' ? 'left' : 'right';
14335             pan.el.addClass(dir); // or prev
14336             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14337             cur.el.addClass(lr); // or right
14338             pan.el.addClass(lr);
14339             
14340             var _this = this;
14341             cur.el.on('transitionend', function() {
14342                 Roo.log("trans end?");
14343                 
14344                 pan.el.removeClass([lr,dir]);
14345                 pan.setActive(true);
14346                 
14347                 cur.el.removeClass([lr]);
14348                 cur.setActive(false);
14349                 
14350                 _this.transition = false;
14351                 
14352             }, this, { single:  true } );
14353             return true;
14354         }
14355         
14356         cur.setActive(false);
14357         pan.setActive(true);
14358         return true;
14359         
14360     },
14361     showPanelNext : function()
14362     {
14363         var i = this.indexOfPanel(this.getActivePanel());
14364         if (i > this.tabs.length) {
14365             return;
14366         }
14367         this.showPanel(this.tabs[i+1]);
14368     },
14369     showPanelPrev : function()
14370     {
14371         var i = this.indexOfPanel(this.getActivePanel());
14372         if (i  < 1) {
14373             return;
14374         }
14375         this.showPanel(this.tabs[i-1]);
14376     }
14377     
14378     
14379   
14380 });
14381
14382  
14383
14384  
14385  
14386 Roo.apply(Roo.bootstrap.TabGroup, {
14387     
14388     groups: {},
14389      /**
14390     * register a Navigation Group
14391     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14392     */
14393     register : function(navgrp)
14394     {
14395         this.groups[navgrp.navId] = navgrp;
14396         
14397     },
14398     /**
14399     * fetch a Navigation Group based on the navigation ID
14400     * if one does not exist , it will get created.
14401     * @param {string} the navgroup to add
14402     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14403     */
14404     get: function(navId) {
14405         if (typeof(this.groups[navId]) == 'undefined') {
14406             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14407         }
14408         return this.groups[navId] ;
14409     }
14410     
14411     
14412     
14413 });
14414
14415  /*
14416  * - LGPL
14417  *
14418  * TabPanel
14419  * 
14420  */
14421
14422 /**
14423  * @class Roo.bootstrap.TabPanel
14424  * @extends Roo.bootstrap.Component
14425  * Bootstrap TabPanel class
14426  * @cfg {Boolean} active panel active
14427  * @cfg {String} html panel content
14428  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14429  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14430  * 
14431  * 
14432  * @constructor
14433  * Create a new TabPanel
14434  * @param {Object} config The config object
14435  */
14436
14437 Roo.bootstrap.TabPanel = function(config){
14438     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14439     this.addEvents({
14440         /**
14441              * @event changed
14442              * Fires when the active status changes
14443              * @param {Roo.bootstrap.TabPanel} this
14444              * @param {Boolean} state the new state
14445             
14446          */
14447         'changed': true,
14448         /**
14449              * @event beforedeactivate
14450              * Fires before a tab is de-activated - can be used to do validation on a form.
14451              * @param {Roo.bootstrap.TabPanel} this
14452              * @return {Boolean} false if there is an error
14453             
14454          */
14455         'beforedeactivate': true
14456      });
14457     
14458     this.tabId = this.tabId || Roo.id();
14459   
14460 };
14461
14462 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14463     
14464     active: false,
14465     html: false,
14466     tabId: false,
14467     navId : false,
14468     
14469     getAutoCreate : function(){
14470         var cfg = {
14471             tag: 'div',
14472             // item is needed for carousel - not sure if it has any effect otherwise
14473             cls: 'tab-pane item',
14474             html: this.html || ''
14475         };
14476         
14477         if(this.active){
14478             cfg.cls += ' active';
14479         }
14480         
14481         if(this.tabId){
14482             cfg.tabId = this.tabId;
14483         }
14484         
14485         
14486         return cfg;
14487     },
14488     
14489     initEvents:  function()
14490     {
14491         Roo.log('-------- init events on tab panel ---------');
14492         
14493         var p = this.parent();
14494         this.navId = this.navId || p.navId;
14495         
14496         if (typeof(this.navId) != 'undefined') {
14497             // not really needed.. but just in case.. parent should be a NavGroup.
14498             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14499             Roo.log(['register', tg, this]);
14500             tg.register(this);
14501         }
14502     },
14503     
14504     
14505     onRender : function(ct, position)
14506     {
14507        // Roo.log("Call onRender: " + this.xtype);
14508         
14509         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14510         
14511         
14512         
14513         
14514         
14515     },
14516     
14517     setActive: function(state)
14518     {
14519         Roo.log("panel - set active " + this.tabId + "=" + state);
14520         
14521         this.active = state;
14522         if (!state) {
14523             this.el.removeClass('active');
14524             
14525         } else  if (!this.el.hasClass('active')) {
14526             this.el.addClass('active');
14527         }
14528         this.fireEvent('changed', this, state);
14529     }
14530     
14531     
14532 });
14533  
14534
14535  
14536
14537  /*
14538  * - LGPL
14539  *
14540  * DateField
14541  * 
14542  */
14543
14544 /**
14545  * @class Roo.bootstrap.DateField
14546  * @extends Roo.bootstrap.Input
14547  * Bootstrap DateField class
14548  * @cfg {Number} weekStart default 0
14549  * @cfg {String} viewMode default empty, (months|years)
14550  * @cfg {String} minViewMode default empty, (months|years)
14551  * @cfg {Number} startDate default -Infinity
14552  * @cfg {Number} endDate default Infinity
14553  * @cfg {Boolean} todayHighlight default false
14554  * @cfg {Boolean} todayBtn default false
14555  * @cfg {Boolean} calendarWeeks default false
14556  * @cfg {Object} daysOfWeekDisabled default empty
14557  * @cfg {Boolean} singleMode default false (true | false)
14558  * 
14559  * @cfg {Boolean} keyboardNavigation default true
14560  * @cfg {String} language default en
14561  * 
14562  * @constructor
14563  * Create a new DateField
14564  * @param {Object} config The config object
14565  */
14566
14567 Roo.bootstrap.DateField = function(config){
14568     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14569      this.addEvents({
14570             /**
14571              * @event show
14572              * Fires when this field show.
14573              * @param {Roo.bootstrap.DateField} this
14574              * @param {Mixed} date The date value
14575              */
14576             show : true,
14577             /**
14578              * @event show
14579              * Fires when this field hide.
14580              * @param {Roo.bootstrap.DateField} this
14581              * @param {Mixed} date The date value
14582              */
14583             hide : true,
14584             /**
14585              * @event select
14586              * Fires when select a date.
14587              * @param {Roo.bootstrap.DateField} this
14588              * @param {Mixed} date The date value
14589              */
14590             select : true
14591         });
14592 };
14593
14594 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14595     
14596     /**
14597      * @cfg {String} format
14598      * The default date format string which can be overriden for localization support.  The format must be
14599      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14600      */
14601     format : "m/d/y",
14602     /**
14603      * @cfg {String} altFormats
14604      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14605      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14606      */
14607     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14608     
14609     weekStart : 0,
14610     
14611     viewMode : '',
14612     
14613     minViewMode : '',
14614     
14615     todayHighlight : false,
14616     
14617     todayBtn: false,
14618     
14619     language: 'en',
14620     
14621     keyboardNavigation: true,
14622     
14623     calendarWeeks: false,
14624     
14625     startDate: -Infinity,
14626     
14627     endDate: Infinity,
14628     
14629     daysOfWeekDisabled: [],
14630     
14631     _events: [],
14632     
14633     singleMode : false,
14634     
14635     UTCDate: function()
14636     {
14637         return new Date(Date.UTC.apply(Date, arguments));
14638     },
14639     
14640     UTCToday: function()
14641     {
14642         var today = new Date();
14643         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14644     },
14645     
14646     getDate: function() {
14647             var d = this.getUTCDate();
14648             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14649     },
14650     
14651     getUTCDate: function() {
14652             return this.date;
14653     },
14654     
14655     setDate: function(d) {
14656             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14657     },
14658     
14659     setUTCDate: function(d) {
14660             this.date = d;
14661             this.setValue(this.formatDate(this.date));
14662     },
14663         
14664     onRender: function(ct, position)
14665     {
14666         
14667         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14668         
14669         this.language = this.language || 'en';
14670         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14671         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14672         
14673         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14674         this.format = this.format || 'm/d/y';
14675         this.isInline = false;
14676         this.isInput = true;
14677         this.component = this.el.select('.add-on', true).first() || false;
14678         this.component = (this.component && this.component.length === 0) ? false : this.component;
14679         this.hasInput = this.component && this.inputEL().length;
14680         
14681         if (typeof(this.minViewMode === 'string')) {
14682             switch (this.minViewMode) {
14683                 case 'months':
14684                     this.minViewMode = 1;
14685                     break;
14686                 case 'years':
14687                     this.minViewMode = 2;
14688                     break;
14689                 default:
14690                     this.minViewMode = 0;
14691                     break;
14692             }
14693         }
14694         
14695         if (typeof(this.viewMode === 'string')) {
14696             switch (this.viewMode) {
14697                 case 'months':
14698                     this.viewMode = 1;
14699                     break;
14700                 case 'years':
14701                     this.viewMode = 2;
14702                     break;
14703                 default:
14704                     this.viewMode = 0;
14705                     break;
14706             }
14707         }
14708                 
14709         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14710         
14711 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14712         
14713         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14714         
14715         this.picker().on('mousedown', this.onMousedown, this);
14716         this.picker().on('click', this.onClick, this);
14717         
14718         this.picker().addClass('datepicker-dropdown');
14719         
14720         this.startViewMode = this.viewMode;
14721         
14722         if(this.singleMode){
14723             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
14724                 v.setVisibilityMode(Roo.Element.DISPLAY)
14725                 v.hide();
14726             })
14727             
14728             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
14729                 v.setStyle('width', '189px');
14730             });
14731         }
14732         
14733         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14734             if(!this.calendarWeeks){
14735                 v.remove();
14736                 return;
14737             };
14738             
14739             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14740             v.attr('colspan', function(i, val){
14741                 return parseInt(val) + 1;
14742             });
14743         })
14744                         
14745         
14746         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14747         
14748         this.setStartDate(this.startDate);
14749         this.setEndDate(this.endDate);
14750         
14751         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14752         
14753         this.fillDow();
14754         this.fillMonths();
14755         this.update();
14756         this.showMode();
14757         
14758         if(this.isInline) {
14759             this.show();
14760         }
14761     },
14762     
14763     picker : function()
14764     {
14765         return this.pickerEl;
14766 //        return this.el.select('.datepicker', true).first();
14767     },
14768     
14769     fillDow: function()
14770     {
14771         var dowCnt = this.weekStart;
14772         
14773         var dow = {
14774             tag: 'tr',
14775             cn: [
14776                 
14777             ]
14778         };
14779         
14780         if(this.calendarWeeks){
14781             dow.cn.push({
14782                 tag: 'th',
14783                 cls: 'cw',
14784                 html: '&nbsp;'
14785             })
14786         }
14787         
14788         while (dowCnt < this.weekStart + 7) {
14789             dow.cn.push({
14790                 tag: 'th',
14791                 cls: 'dow',
14792                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14793             });
14794         }
14795         
14796         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14797     },
14798     
14799     fillMonths: function()
14800     {    
14801         var i = 0
14802         var months = this.picker().select('>.datepicker-months td', true).first();
14803         
14804         months.dom.innerHTML = '';
14805         
14806         while (i < 12) {
14807             var month = {
14808                 tag: 'span',
14809                 cls: 'month',
14810                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14811             }
14812             
14813             months.createChild(month);
14814         }
14815         
14816     },
14817     
14818     update: function()
14819     {
14820         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;
14821         
14822         if (this.date < this.startDate) {
14823             this.viewDate = new Date(this.startDate);
14824         } else if (this.date > this.endDate) {
14825             this.viewDate = new Date(this.endDate);
14826         } else {
14827             this.viewDate = new Date(this.date);
14828         }
14829         
14830         this.fill();
14831     },
14832     
14833     fill: function() 
14834     {
14835         var d = new Date(this.viewDate),
14836                 year = d.getUTCFullYear(),
14837                 month = d.getUTCMonth(),
14838                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14839                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14840                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14841                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14842                 currentDate = this.date && this.date.valueOf(),
14843                 today = this.UTCToday();
14844         
14845         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14846         
14847 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14848         
14849 //        this.picker.select('>tfoot th.today').
14850 //                                              .text(dates[this.language].today)
14851 //                                              .toggle(this.todayBtn !== false);
14852     
14853         this.updateNavArrows();
14854         this.fillMonths();
14855                                                 
14856         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14857         
14858         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14859          
14860         prevMonth.setUTCDate(day);
14861         
14862         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14863         
14864         var nextMonth = new Date(prevMonth);
14865         
14866         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14867         
14868         nextMonth = nextMonth.valueOf();
14869         
14870         var fillMonths = false;
14871         
14872         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14873         
14874         while(prevMonth.valueOf() < nextMonth) {
14875             var clsName = '';
14876             
14877             if (prevMonth.getUTCDay() === this.weekStart) {
14878                 if(fillMonths){
14879                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14880                 }
14881                     
14882                 fillMonths = {
14883                     tag: 'tr',
14884                     cn: []
14885                 };
14886                 
14887                 if(this.calendarWeeks){
14888                     // ISO 8601: First week contains first thursday.
14889                     // ISO also states week starts on Monday, but we can be more abstract here.
14890                     var
14891                     // Start of current week: based on weekstart/current date
14892                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14893                     // Thursday of this week
14894                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14895                     // First Thursday of year, year from thursday
14896                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14897                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14898                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14899                     
14900                     fillMonths.cn.push({
14901                         tag: 'td',
14902                         cls: 'cw',
14903                         html: calWeek
14904                     });
14905                 }
14906             }
14907             
14908             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14909                 clsName += ' old';
14910             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14911                 clsName += ' new';
14912             }
14913             if (this.todayHighlight &&
14914                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14915                 prevMonth.getUTCMonth() == today.getMonth() &&
14916                 prevMonth.getUTCDate() == today.getDate()) {
14917                 clsName += ' today';
14918             }
14919             
14920             if (currentDate && prevMonth.valueOf() === currentDate) {
14921                 clsName += ' active';
14922             }
14923             
14924             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14925                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14926                     clsName += ' disabled';
14927             }
14928             
14929             fillMonths.cn.push({
14930                 tag: 'td',
14931                 cls: 'day ' + clsName,
14932                 html: prevMonth.getDate()
14933             })
14934             
14935             prevMonth.setDate(prevMonth.getDate()+1);
14936         }
14937           
14938         var currentYear = this.date && this.date.getUTCFullYear();
14939         var currentMonth = this.date && this.date.getUTCMonth();
14940         
14941         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14942         
14943         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14944             v.removeClass('active');
14945             
14946             if(currentYear === year && k === currentMonth){
14947                 v.addClass('active');
14948             }
14949             
14950             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14951                 v.addClass('disabled');
14952             }
14953             
14954         });
14955         
14956         
14957         year = parseInt(year/10, 10) * 10;
14958         
14959         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14960         
14961         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14962         
14963         year -= 1;
14964         for (var i = -1; i < 11; i++) {
14965             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14966                 tag: 'span',
14967                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14968                 html: year
14969             })
14970             
14971             year += 1;
14972         }
14973     },
14974     
14975     showMode: function(dir) 
14976     {
14977         if (dir) {
14978             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14979         }
14980         
14981         Roo.each(this.picker().select('>div',true).elements, function(v){
14982             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14983             v.hide();
14984         });
14985         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14986     },
14987     
14988     place: function()
14989     {
14990         if(this.isInline) return;
14991         
14992         this.picker().removeClass(['bottom', 'top']);
14993         
14994         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14995             /*
14996              * place to the top of element!
14997              *
14998              */
14999             
15000             this.picker().addClass('top');
15001             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15002             
15003             return;
15004         }
15005         
15006         this.picker().addClass('bottom');
15007         
15008         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15009     },
15010     
15011     parseDate : function(value)
15012     {
15013         if(!value || value instanceof Date){
15014             return value;
15015         }
15016         var v = Date.parseDate(value, this.format);
15017         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15018             v = Date.parseDate(value, 'Y-m-d');
15019         }
15020         if(!v && this.altFormats){
15021             if(!this.altFormatsArray){
15022                 this.altFormatsArray = this.altFormats.split("|");
15023             }
15024             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15025                 v = Date.parseDate(value, this.altFormatsArray[i]);
15026             }
15027         }
15028         return v;
15029     },
15030     
15031     formatDate : function(date, fmt)
15032     {   
15033         return (!date || !(date instanceof Date)) ?
15034         date : date.dateFormat(fmt || this.format);
15035     },
15036     
15037     onFocus : function()
15038     {
15039         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15040         this.show();
15041     },
15042     
15043     onBlur : function()
15044     {
15045         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15046         
15047         var d = this.inputEl().getValue();
15048         
15049         this.setValue(d);
15050                 
15051         this.hide();
15052     },
15053     
15054     show : function()
15055     {
15056         this.picker().show();
15057         this.update();
15058         this.place();
15059         
15060         this.fireEvent('show', this, this.date);
15061     },
15062     
15063     hide : function()
15064     {
15065         if(this.isInline) return;
15066         this.picker().hide();
15067         this.viewMode = this.startViewMode;
15068         this.showMode();
15069         
15070         this.fireEvent('hide', this, this.date);
15071         
15072     },
15073     
15074     onMousedown: function(e)
15075     {
15076         e.stopPropagation();
15077         e.preventDefault();
15078     },
15079     
15080     keyup: function(e)
15081     {
15082         Roo.bootstrap.DateField.superclass.keyup.call(this);
15083         this.update();
15084     },
15085
15086     setValue: function(v)
15087     {
15088         
15089         // v can be a string or a date..
15090         
15091         
15092         var d = new Date(this.parseDate(v) ).clearTime();
15093         
15094         if(isNaN(d.getTime())){
15095             this.date = this.viewDate = '';
15096             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15097             return;
15098         }
15099         
15100         v = this.formatDate(d);
15101         
15102         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15103         
15104         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15105      
15106         this.update();
15107
15108         this.fireEvent('select', this, this.date);
15109         
15110     },
15111     
15112     getValue: function()
15113     {
15114         return this.formatDate(this.date);
15115     },
15116     
15117     fireKey: function(e)
15118     {
15119         if (!this.picker().isVisible()){
15120             if (e.keyCode == 27) // allow escape to hide and re-show picker
15121                 this.show();
15122             return;
15123         }
15124         
15125         var dateChanged = false,
15126         dir, day, month,
15127         newDate, newViewDate;
15128         
15129         switch(e.keyCode){
15130             case 27: // escape
15131                 this.hide();
15132                 e.preventDefault();
15133                 break;
15134             case 37: // left
15135             case 39: // right
15136                 if (!this.keyboardNavigation) break;
15137                 dir = e.keyCode == 37 ? -1 : 1;
15138                 
15139                 if (e.ctrlKey){
15140                     newDate = this.moveYear(this.date, dir);
15141                     newViewDate = this.moveYear(this.viewDate, dir);
15142                 } else if (e.shiftKey){
15143                     newDate = this.moveMonth(this.date, dir);
15144                     newViewDate = this.moveMonth(this.viewDate, dir);
15145                 } else {
15146                     newDate = new Date(this.date);
15147                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15148                     newViewDate = new Date(this.viewDate);
15149                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15150                 }
15151                 if (this.dateWithinRange(newDate)){
15152                     this.date = newDate;
15153                     this.viewDate = newViewDate;
15154                     this.setValue(this.formatDate(this.date));
15155 //                    this.update();
15156                     e.preventDefault();
15157                     dateChanged = true;
15158                 }
15159                 break;
15160             case 38: // up
15161             case 40: // down
15162                 if (!this.keyboardNavigation) break;
15163                 dir = e.keyCode == 38 ? -1 : 1;
15164                 if (e.ctrlKey){
15165                     newDate = this.moveYear(this.date, dir);
15166                     newViewDate = this.moveYear(this.viewDate, dir);
15167                 } else if (e.shiftKey){
15168                     newDate = this.moveMonth(this.date, dir);
15169                     newViewDate = this.moveMonth(this.viewDate, dir);
15170                 } else {
15171                     newDate = new Date(this.date);
15172                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15173                     newViewDate = new Date(this.viewDate);
15174                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15175                 }
15176                 if (this.dateWithinRange(newDate)){
15177                     this.date = newDate;
15178                     this.viewDate = newViewDate;
15179                     this.setValue(this.formatDate(this.date));
15180 //                    this.update();
15181                     e.preventDefault();
15182                     dateChanged = true;
15183                 }
15184                 break;
15185             case 13: // enter
15186                 this.setValue(this.formatDate(this.date));
15187                 this.hide();
15188                 e.preventDefault();
15189                 break;
15190             case 9: // tab
15191                 this.setValue(this.formatDate(this.date));
15192                 this.hide();
15193                 break;
15194             case 16: // shift
15195             case 17: // ctrl
15196             case 18: // alt
15197                 break;
15198             default :
15199                 this.hide();
15200                 
15201         }
15202     },
15203     
15204     
15205     onClick: function(e) 
15206     {
15207         e.stopPropagation();
15208         e.preventDefault();
15209         
15210         var target = e.getTarget();
15211         
15212         if(target.nodeName.toLowerCase() === 'i'){
15213             target = Roo.get(target).dom.parentNode;
15214         }
15215         
15216         var nodeName = target.nodeName;
15217         var className = target.className;
15218         var html = target.innerHTML;
15219         //Roo.log(nodeName);
15220         
15221         switch(nodeName.toLowerCase()) {
15222             case 'th':
15223                 switch(className) {
15224                     case 'switch':
15225                         this.showMode(1);
15226                         break;
15227                     case 'prev':
15228                     case 'next':
15229                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15230                         switch(this.viewMode){
15231                                 case 0:
15232                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15233                                         break;
15234                                 case 1:
15235                                 case 2:
15236                                         this.viewDate = this.moveYear(this.viewDate, dir);
15237                                         break;
15238                         }
15239                         this.fill();
15240                         break;
15241                     case 'today':
15242                         var date = new Date();
15243                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15244 //                        this.fill()
15245                         this.setValue(this.formatDate(this.date));
15246                         
15247                         this.hide();
15248                         break;
15249                 }
15250                 break;
15251             case 'span':
15252                 if (className.indexOf('disabled') < 0) {
15253                     this.viewDate.setUTCDate(1);
15254                     if (className.indexOf('month') > -1) {
15255                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15256                     } else {
15257                         var year = parseInt(html, 10) || 0;
15258                         this.viewDate.setUTCFullYear(year);
15259                         
15260                     }
15261                     
15262                     if(this.singleMode){
15263                         this.setValue(this.formatDate(this.viewDate));
15264                         this.hide();
15265                         return;
15266                     }
15267                     
15268                     this.showMode(-1);
15269                     this.fill();
15270                 }
15271                 break;
15272                 
15273             case 'td':
15274                 //Roo.log(className);
15275                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15276                     var day = parseInt(html, 10) || 1;
15277                     var year = this.viewDate.getUTCFullYear(),
15278                         month = this.viewDate.getUTCMonth();
15279
15280                     if (className.indexOf('old') > -1) {
15281                         if(month === 0 ){
15282                             month = 11;
15283                             year -= 1;
15284                         }else{
15285                             month -= 1;
15286                         }
15287                     } else if (className.indexOf('new') > -1) {
15288                         if (month == 11) {
15289                             month = 0;
15290                             year += 1;
15291                         } else {
15292                             month += 1;
15293                         }
15294                     }
15295                     //Roo.log([year,month,day]);
15296                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15297                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15298 //                    this.fill();
15299                     //Roo.log(this.formatDate(this.date));
15300                     this.setValue(this.formatDate(this.date));
15301                     this.hide();
15302                 }
15303                 break;
15304         }
15305     },
15306     
15307     setStartDate: function(startDate)
15308     {
15309         this.startDate = startDate || -Infinity;
15310         if (this.startDate !== -Infinity) {
15311             this.startDate = this.parseDate(this.startDate);
15312         }
15313         this.update();
15314         this.updateNavArrows();
15315     },
15316
15317     setEndDate: function(endDate)
15318     {
15319         this.endDate = endDate || Infinity;
15320         if (this.endDate !== Infinity) {
15321             this.endDate = this.parseDate(this.endDate);
15322         }
15323         this.update();
15324         this.updateNavArrows();
15325     },
15326     
15327     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15328     {
15329         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15330         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15331             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15332         }
15333         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15334             return parseInt(d, 10);
15335         });
15336         this.update();
15337         this.updateNavArrows();
15338     },
15339     
15340     updateNavArrows: function() 
15341     {
15342         if(this.singleMode){
15343             return;
15344         }
15345         
15346         var d = new Date(this.viewDate),
15347         year = d.getUTCFullYear(),
15348         month = d.getUTCMonth();
15349         
15350         Roo.each(this.picker().select('.prev', true).elements, function(v){
15351             v.show();
15352             switch (this.viewMode) {
15353                 case 0:
15354
15355                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15356                         v.hide();
15357                     }
15358                     break;
15359                 case 1:
15360                 case 2:
15361                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15362                         v.hide();
15363                     }
15364                     break;
15365             }
15366         });
15367         
15368         Roo.each(this.picker().select('.next', true).elements, function(v){
15369             v.show();
15370             switch (this.viewMode) {
15371                 case 0:
15372
15373                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15374                         v.hide();
15375                     }
15376                     break;
15377                 case 1:
15378                 case 2:
15379                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15380                         v.hide();
15381                     }
15382                     break;
15383             }
15384         })
15385     },
15386     
15387     moveMonth: function(date, dir)
15388     {
15389         if (!dir) return date;
15390         var new_date = new Date(date.valueOf()),
15391         day = new_date.getUTCDate(),
15392         month = new_date.getUTCMonth(),
15393         mag = Math.abs(dir),
15394         new_month, test;
15395         dir = dir > 0 ? 1 : -1;
15396         if (mag == 1){
15397             test = dir == -1
15398             // If going back one month, make sure month is not current month
15399             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15400             ? function(){
15401                 return new_date.getUTCMonth() == month;
15402             }
15403             // If going forward one month, make sure month is as expected
15404             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15405             : function(){
15406                 return new_date.getUTCMonth() != new_month;
15407             };
15408             new_month = month + dir;
15409             new_date.setUTCMonth(new_month);
15410             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15411             if (new_month < 0 || new_month > 11)
15412                 new_month = (new_month + 12) % 12;
15413         } else {
15414             // For magnitudes >1, move one month at a time...
15415             for (var i=0; i<mag; i++)
15416                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15417                 new_date = this.moveMonth(new_date, dir);
15418             // ...then reset the day, keeping it in the new month
15419             new_month = new_date.getUTCMonth();
15420             new_date.setUTCDate(day);
15421             test = function(){
15422                 return new_month != new_date.getUTCMonth();
15423             };
15424         }
15425         // Common date-resetting loop -- if date is beyond end of month, make it
15426         // end of month
15427         while (test()){
15428             new_date.setUTCDate(--day);
15429             new_date.setUTCMonth(new_month);
15430         }
15431         return new_date;
15432     },
15433
15434     moveYear: function(date, dir)
15435     {
15436         return this.moveMonth(date, dir*12);
15437     },
15438
15439     dateWithinRange: function(date)
15440     {
15441         return date >= this.startDate && date <= this.endDate;
15442     },
15443
15444     
15445     remove: function() 
15446     {
15447         this.picker().remove();
15448     }
15449    
15450 });
15451
15452 Roo.apply(Roo.bootstrap.DateField,  {
15453     
15454     head : {
15455         tag: 'thead',
15456         cn: [
15457         {
15458             tag: 'tr',
15459             cn: [
15460             {
15461                 tag: 'th',
15462                 cls: 'prev',
15463                 html: '<i class="fa fa-arrow-left"/>'
15464             },
15465             {
15466                 tag: 'th',
15467                 cls: 'switch',
15468                 colspan: '5'
15469             },
15470             {
15471                 tag: 'th',
15472                 cls: 'next',
15473                 html: '<i class="fa fa-arrow-right"/>'
15474             }
15475
15476             ]
15477         }
15478         ]
15479     },
15480     
15481     content : {
15482         tag: 'tbody',
15483         cn: [
15484         {
15485             tag: 'tr',
15486             cn: [
15487             {
15488                 tag: 'td',
15489                 colspan: '7'
15490             }
15491             ]
15492         }
15493         ]
15494     },
15495     
15496     footer : {
15497         tag: 'tfoot',
15498         cn: [
15499         {
15500             tag: 'tr',
15501             cn: [
15502             {
15503                 tag: 'th',
15504                 colspan: '7',
15505                 cls: 'today'
15506             }
15507                     
15508             ]
15509         }
15510         ]
15511     },
15512     
15513     dates:{
15514         en: {
15515             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15516             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15517             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15518             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15519             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15520             today: "Today"
15521         }
15522     },
15523     
15524     modes: [
15525     {
15526         clsName: 'days',
15527         navFnc: 'Month',
15528         navStep: 1
15529     },
15530     {
15531         clsName: 'months',
15532         navFnc: 'FullYear',
15533         navStep: 1
15534     },
15535     {
15536         clsName: 'years',
15537         navFnc: 'FullYear',
15538         navStep: 10
15539     }]
15540 });
15541
15542 Roo.apply(Roo.bootstrap.DateField,  {
15543   
15544     template : {
15545         tag: 'div',
15546         cls: 'datepicker dropdown-menu roo-dynamic',
15547         cn: [
15548         {
15549             tag: 'div',
15550             cls: 'datepicker-days',
15551             cn: [
15552             {
15553                 tag: 'table',
15554                 cls: 'table-condensed',
15555                 cn:[
15556                 Roo.bootstrap.DateField.head,
15557                 {
15558                     tag: 'tbody'
15559                 },
15560                 Roo.bootstrap.DateField.footer
15561                 ]
15562             }
15563             ]
15564         },
15565         {
15566             tag: 'div',
15567             cls: 'datepicker-months',
15568             cn: [
15569             {
15570                 tag: 'table',
15571                 cls: 'table-condensed',
15572                 cn:[
15573                 Roo.bootstrap.DateField.head,
15574                 Roo.bootstrap.DateField.content,
15575                 Roo.bootstrap.DateField.footer
15576                 ]
15577             }
15578             ]
15579         },
15580         {
15581             tag: 'div',
15582             cls: 'datepicker-years',
15583             cn: [
15584             {
15585                 tag: 'table',
15586                 cls: 'table-condensed',
15587                 cn:[
15588                 Roo.bootstrap.DateField.head,
15589                 Roo.bootstrap.DateField.content,
15590                 Roo.bootstrap.DateField.footer
15591                 ]
15592             }
15593             ]
15594         }
15595         ]
15596     }
15597 });
15598
15599  
15600
15601  /*
15602  * - LGPL
15603  *
15604  * TimeField
15605  * 
15606  */
15607
15608 /**
15609  * @class Roo.bootstrap.TimeField
15610  * @extends Roo.bootstrap.Input
15611  * Bootstrap DateField class
15612  * 
15613  * 
15614  * @constructor
15615  * Create a new TimeField
15616  * @param {Object} config The config object
15617  */
15618
15619 Roo.bootstrap.TimeField = function(config){
15620     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15621     this.addEvents({
15622             /**
15623              * @event show
15624              * Fires when this field show.
15625              * @param {Roo.bootstrap.DateField} this
15626              * @param {Mixed} date The date value
15627              */
15628             show : true,
15629             /**
15630              * @event show
15631              * Fires when this field hide.
15632              * @param {Roo.bootstrap.DateField} this
15633              * @param {Mixed} date The date value
15634              */
15635             hide : true,
15636             /**
15637              * @event select
15638              * Fires when select a date.
15639              * @param {Roo.bootstrap.DateField} this
15640              * @param {Mixed} date The date value
15641              */
15642             select : true
15643         });
15644 };
15645
15646 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15647     
15648     /**
15649      * @cfg {String} format
15650      * The default time format string which can be overriden for localization support.  The format must be
15651      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15652      */
15653     format : "H:i",
15654        
15655     onRender: function(ct, position)
15656     {
15657         
15658         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15659                 
15660         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15661         
15662         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15663         
15664         this.pop = this.picker().select('>.datepicker-time',true).first();
15665         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15666         
15667         this.picker().on('mousedown', this.onMousedown, this);
15668         this.picker().on('click', this.onClick, this);
15669         
15670         this.picker().addClass('datepicker-dropdown');
15671     
15672         this.fillTime();
15673         this.update();
15674             
15675         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15676         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15677         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15678         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15679         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15680         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15681
15682     },
15683     
15684     fireKey: function(e){
15685         if (!this.picker().isVisible()){
15686             if (e.keyCode == 27) // allow escape to hide and re-show picker
15687                 this.show();
15688             return;
15689         }
15690
15691         e.preventDefault();
15692         
15693         switch(e.keyCode){
15694             case 27: // escape
15695                 this.hide();
15696                 break;
15697             case 37: // left
15698             case 39: // right
15699                 this.onTogglePeriod();
15700                 break;
15701             case 38: // up
15702                 this.onIncrementMinutes();
15703                 break;
15704             case 40: // down
15705                 this.onDecrementMinutes();
15706                 break;
15707             case 13: // enter
15708             case 9: // tab
15709                 this.setTime();
15710                 break;
15711         }
15712     },
15713     
15714     onClick: function(e) {
15715         e.stopPropagation();
15716         e.preventDefault();
15717     },
15718     
15719     picker : function()
15720     {
15721         return this.el.select('.datepicker', true).first();
15722     },
15723     
15724     fillTime: function()
15725     {    
15726         var time = this.pop.select('tbody', true).first();
15727         
15728         time.dom.innerHTML = '';
15729         
15730         time.createChild({
15731             tag: 'tr',
15732             cn: [
15733                 {
15734                     tag: 'td',
15735                     cn: [
15736                         {
15737                             tag: 'a',
15738                             href: '#',
15739                             cls: 'btn',
15740                             cn: [
15741                                 {
15742                                     tag: 'span',
15743                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15744                                 }
15745                             ]
15746                         } 
15747                     ]
15748                 },
15749                 {
15750                     tag: 'td',
15751                     cls: 'separator'
15752                 },
15753                 {
15754                     tag: 'td',
15755                     cn: [
15756                         {
15757                             tag: 'a',
15758                             href: '#',
15759                             cls: 'btn',
15760                             cn: [
15761                                 {
15762                                     tag: 'span',
15763                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15764                                 }
15765                             ]
15766                         }
15767                     ]
15768                 },
15769                 {
15770                     tag: 'td',
15771                     cls: 'separator'
15772                 }
15773             ]
15774         });
15775         
15776         time.createChild({
15777             tag: 'tr',
15778             cn: [
15779                 {
15780                     tag: 'td',
15781                     cn: [
15782                         {
15783                             tag: 'span',
15784                             cls: 'timepicker-hour',
15785                             html: '00'
15786                         }  
15787                     ]
15788                 },
15789                 {
15790                     tag: 'td',
15791                     cls: 'separator',
15792                     html: ':'
15793                 },
15794                 {
15795                     tag: 'td',
15796                     cn: [
15797                         {
15798                             tag: 'span',
15799                             cls: 'timepicker-minute',
15800                             html: '00'
15801                         }  
15802                     ]
15803                 },
15804                 {
15805                     tag: 'td',
15806                     cls: 'separator'
15807                 },
15808                 {
15809                     tag: 'td',
15810                     cn: [
15811                         {
15812                             tag: 'button',
15813                             type: 'button',
15814                             cls: 'btn btn-primary period',
15815                             html: 'AM'
15816                             
15817                         }
15818                     ]
15819                 }
15820             ]
15821         });
15822         
15823         time.createChild({
15824             tag: 'tr',
15825             cn: [
15826                 {
15827                     tag: 'td',
15828                     cn: [
15829                         {
15830                             tag: 'a',
15831                             href: '#',
15832                             cls: 'btn',
15833                             cn: [
15834                                 {
15835                                     tag: 'span',
15836                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15837                                 }
15838                             ]
15839                         }
15840                     ]
15841                 },
15842                 {
15843                     tag: 'td',
15844                     cls: 'separator'
15845                 },
15846                 {
15847                     tag: 'td',
15848                     cn: [
15849                         {
15850                             tag: 'a',
15851                             href: '#',
15852                             cls: 'btn',
15853                             cn: [
15854                                 {
15855                                     tag: 'span',
15856                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15857                                 }
15858                             ]
15859                         }
15860                     ]
15861                 },
15862                 {
15863                     tag: 'td',
15864                     cls: 'separator'
15865                 }
15866             ]
15867         });
15868         
15869     },
15870     
15871     update: function()
15872     {
15873         
15874         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15875         
15876         this.fill();
15877     },
15878     
15879     fill: function() 
15880     {
15881         var hours = this.time.getHours();
15882         var minutes = this.time.getMinutes();
15883         var period = 'AM';
15884         
15885         if(hours > 11){
15886             period = 'PM';
15887         }
15888         
15889         if(hours == 0){
15890             hours = 12;
15891         }
15892         
15893         
15894         if(hours > 12){
15895             hours = hours - 12;
15896         }
15897         
15898         if(hours < 10){
15899             hours = '0' + hours;
15900         }
15901         
15902         if(minutes < 10){
15903             minutes = '0' + minutes;
15904         }
15905         
15906         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15907         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15908         this.pop.select('button', true).first().dom.innerHTML = period;
15909         
15910     },
15911     
15912     place: function()
15913     {   
15914         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15915         
15916         var cls = ['bottom'];
15917         
15918         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15919             cls.pop();
15920             cls.push('top');
15921         }
15922         
15923         cls.push('right');
15924         
15925         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15926             cls.pop();
15927             cls.push('left');
15928         }
15929         
15930         this.picker().addClass(cls.join('-'));
15931         
15932         var _this = this;
15933         
15934         Roo.each(cls, function(c){
15935             if(c == 'bottom'){
15936                 _this.picker().setTop(_this.inputEl().getHeight());
15937                 return;
15938             }
15939             if(c == 'top'){
15940                 _this.picker().setTop(0 - _this.picker().getHeight());
15941                 return;
15942             }
15943             
15944             if(c == 'left'){
15945                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15946                 return;
15947             }
15948             if(c == 'right'){
15949                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15950                 return;
15951             }
15952         });
15953         
15954     },
15955   
15956     onFocus : function()
15957     {
15958         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15959         this.show();
15960     },
15961     
15962     onBlur : function()
15963     {
15964         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15965         this.hide();
15966     },
15967     
15968     show : function()
15969     {
15970         this.picker().show();
15971         this.pop.show();
15972         this.update();
15973         this.place();
15974         
15975         this.fireEvent('show', this, this.date);
15976     },
15977     
15978     hide : function()
15979     {
15980         this.picker().hide();
15981         this.pop.hide();
15982         
15983         this.fireEvent('hide', this, this.date);
15984     },
15985     
15986     setTime : function()
15987     {
15988         this.hide();
15989         this.setValue(this.time.format(this.format));
15990         
15991         this.fireEvent('select', this, this.date);
15992         
15993         
15994     },
15995     
15996     onMousedown: function(e){
15997         e.stopPropagation();
15998         e.preventDefault();
15999     },
16000     
16001     onIncrementHours: function()
16002     {
16003         Roo.log('onIncrementHours');
16004         this.time = this.time.add(Date.HOUR, 1);
16005         this.update();
16006         
16007     },
16008     
16009     onDecrementHours: function()
16010     {
16011         Roo.log('onDecrementHours');
16012         this.time = this.time.add(Date.HOUR, -1);
16013         this.update();
16014     },
16015     
16016     onIncrementMinutes: function()
16017     {
16018         Roo.log('onIncrementMinutes');
16019         this.time = this.time.add(Date.MINUTE, 1);
16020         this.update();
16021     },
16022     
16023     onDecrementMinutes: function()
16024     {
16025         Roo.log('onDecrementMinutes');
16026         this.time = this.time.add(Date.MINUTE, -1);
16027         this.update();
16028     },
16029     
16030     onTogglePeriod: function()
16031     {
16032         Roo.log('onTogglePeriod');
16033         this.time = this.time.add(Date.HOUR, 12);
16034         this.update();
16035     }
16036     
16037    
16038 });
16039
16040 Roo.apply(Roo.bootstrap.TimeField,  {
16041     
16042     content : {
16043         tag: 'tbody',
16044         cn: [
16045             {
16046                 tag: 'tr',
16047                 cn: [
16048                 {
16049                     tag: 'td',
16050                     colspan: '7'
16051                 }
16052                 ]
16053             }
16054         ]
16055     },
16056     
16057     footer : {
16058         tag: 'tfoot',
16059         cn: [
16060             {
16061                 tag: 'tr',
16062                 cn: [
16063                 {
16064                     tag: 'th',
16065                     colspan: '7',
16066                     cls: '',
16067                     cn: [
16068                         {
16069                             tag: 'button',
16070                             cls: 'btn btn-info ok',
16071                             html: 'OK'
16072                         }
16073                     ]
16074                 }
16075
16076                 ]
16077             }
16078         ]
16079     }
16080 });
16081
16082 Roo.apply(Roo.bootstrap.TimeField,  {
16083   
16084     template : {
16085         tag: 'div',
16086         cls: 'datepicker dropdown-menu',
16087         cn: [
16088             {
16089                 tag: 'div',
16090                 cls: 'datepicker-time',
16091                 cn: [
16092                 {
16093                     tag: 'table',
16094                     cls: 'table-condensed',
16095                     cn:[
16096                     Roo.bootstrap.TimeField.content,
16097                     Roo.bootstrap.TimeField.footer
16098                     ]
16099                 }
16100                 ]
16101             }
16102         ]
16103     }
16104 });
16105
16106  
16107
16108  /*
16109  * - LGPL
16110  *
16111  * CheckBox
16112  * 
16113  */
16114
16115 /**
16116  * @class Roo.bootstrap.CheckBox
16117  * @extends Roo.bootstrap.Input
16118  * Bootstrap CheckBox class
16119  * 
16120  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
16121  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
16122  * @cfg {String} boxLabel The text that appears beside the checkbox
16123  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
16124  * @cfg {Boolean} checked initnal the element
16125  * @cfg {Boolean} inline inline the element (default false)
16126  * 
16127  * @constructor
16128  * Create a new CheckBox
16129  * @param {Object} config The config object
16130  */
16131
16132 Roo.bootstrap.CheckBox = function(config){
16133     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
16134    
16135         this.addEvents({
16136             /**
16137             * @event check
16138             * Fires when the element is checked or unchecked.
16139             * @param {Roo.bootstrap.CheckBox} this This input
16140             * @param {Boolean} checked The new checked value
16141             */
16142            check : true
16143         });
16144 };
16145
16146 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
16147     
16148     inputType: 'checkbox',
16149     inputValue: 1,
16150     valueOff: 0,
16151     boxLabel: false,
16152     checked: false,
16153     weight : false,
16154     inline: false,
16155     
16156     getAutoCreate : function()
16157     {
16158         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16159         
16160         var id = Roo.id();
16161         
16162         var cfg = {};
16163         
16164         cfg.cls = 'form-group ' + this.inputType //input-group
16165         
16166         if(this.inline){
16167             cfg.cls += ' ' + this.inputType + '-inline';
16168         }
16169         
16170         var input =  {
16171             tag: 'input',
16172             id : id,
16173             type : this.inputType,
16174             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
16175             cls : 'roo-' + this.inputType, //'form-box',
16176             placeholder : this.placeholder || ''
16177             
16178         };
16179         
16180         if (this.weight) { // Validity check?
16181             cfg.cls += " " + this.inputType + "-" + this.weight;
16182         }
16183         
16184         if (this.disabled) {
16185             input.disabled=true;
16186         }
16187         
16188         if(this.checked){
16189             input.checked = this.checked;
16190         }
16191         
16192         if (this.name) {
16193             input.name = this.name;
16194         }
16195         
16196         if (this.size) {
16197             input.cls += ' input-' + this.size;
16198         }
16199         
16200         var settings=this;
16201         ['xs','sm','md','lg'].map(function(size){
16202             if (settings[size]) {
16203                 cfg.cls += ' col-' + size + '-' + settings[size];
16204             }
16205         });
16206         
16207        
16208         
16209         var inputblock = input;
16210         
16211         
16212         
16213         
16214         if (this.before || this.after) {
16215             
16216             inputblock = {
16217                 cls : 'input-group',
16218                 cn :  [] 
16219             };
16220             if (this.before) {
16221                 inputblock.cn.push({
16222                     tag :'span',
16223                     cls : 'input-group-addon',
16224                     html : this.before
16225                 });
16226             }
16227             inputblock.cn.push(input);
16228             if (this.after) {
16229                 inputblock.cn.push({
16230                     tag :'span',
16231                     cls : 'input-group-addon',
16232                     html : this.after
16233                 });
16234             }
16235             
16236         };
16237         
16238         if (align ==='left' && this.fieldLabel.length) {
16239                 Roo.log("left and has label");
16240                 cfg.cn = [
16241                     
16242                     {
16243                         tag: 'label',
16244                         'for' :  id,
16245                         cls : 'control-label col-md-' + this.labelWidth,
16246                         html : this.fieldLabel
16247                         
16248                     },
16249                     {
16250                         cls : "col-md-" + (12 - this.labelWidth), 
16251                         cn: [
16252                             inputblock
16253                         ]
16254                     }
16255                     
16256                 ];
16257         } else if ( this.fieldLabel.length) {
16258                 Roo.log(" label");
16259                 cfg.cn = [
16260                    
16261                     {
16262                         tag: this.boxLabel ? 'span' : 'label',
16263                         'for': id,
16264                         cls: 'control-label box-input-label',
16265                         //cls : 'input-group-addon',
16266                         html : this.fieldLabel
16267                         
16268                     },
16269                     
16270                     inputblock
16271                     
16272                 ];
16273
16274         } else {
16275             
16276                 Roo.log(" no label && no align");
16277                 cfg.cn = [  inputblock ] ;
16278                 
16279                 
16280         };
16281          if(this.boxLabel){
16282              var boxLabelCfg = {
16283                 tag: 'label',
16284                 //'for': id, // box label is handled by onclick - so no for...
16285                 cls: 'box-label',
16286                 html: this.boxLabel
16287             }
16288             
16289             if(this.tooltip){
16290                 boxLabelCfg.tooltip = this.tooltip;
16291             }
16292              
16293             cfg.cn.push(boxLabelCfg);
16294         }
16295         
16296         
16297        
16298         return cfg;
16299         
16300     },
16301     
16302     /**
16303      * return the real input element.
16304      */
16305     inputEl: function ()
16306     {
16307         return this.el.select('input.roo-' + this.inputType,true).first();
16308     },
16309     
16310     labelEl: function()
16311     {
16312         return this.el.select('label.control-label',true).first();
16313     },
16314     /* depricated... */
16315     
16316     label: function()
16317     {
16318         return this.labelEl();
16319     },
16320     
16321     initEvents : function()
16322     {
16323 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16324         
16325         this.inputEl().on('click', this.onClick,  this);
16326         if (this.boxLabel) { 
16327             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
16328         }
16329         
16330     },
16331     
16332     onClick : function()
16333     {   
16334         this.setChecked(!this.checked);
16335     },
16336     
16337     setChecked : function(state,suppressEvent)
16338     {
16339         if(this.inputType == 'radio'){
16340             
16341             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
16342                 e.dom.checked = false;
16343             });
16344             
16345             this.inputEl().dom.checked = true;
16346             
16347             if(suppressEvent !== true){
16348                 this.fireEvent('check', this, true);
16349             }
16350             
16351             this.inputEl().dom.value = this.inputValue;
16352             
16353             return;
16354         }
16355         
16356         this.checked = state;
16357         
16358         if(suppressEvent !== true){
16359             this.fireEvent('check', this, state);
16360         }
16361         
16362         this.inputEl().dom.checked = state;
16363         
16364         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16365         
16366     },
16367     
16368     getValue : function()
16369     {
16370         if(this.inputType == 'radio'){
16371             return this.getGroupValue();
16372         }
16373         
16374         return this.inputEl().getValue();
16375         
16376     },
16377     
16378     getGroupValue : function()
16379     {
16380         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
16381     },
16382     
16383     setValue : function(v,suppressEvent)
16384     {
16385         if(this.inputType == 'radio'){
16386             this.setGroupValue(v, suppressEvent);
16387             return;
16388         }
16389         
16390         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16391     },
16392     
16393     setGroupValue : function(v, suppressEvent)
16394     {
16395         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
16396             e.dom.checked = false;
16397             
16398             if(e.dom.value == v){
16399                 e.dom.checked = true;
16400             }
16401         });
16402         
16403         if(suppressEvent !== true){
16404             this.fireEvent('check', this, true);
16405         }
16406
16407         return;
16408     }
16409     
16410 });
16411
16412  
16413 /*
16414  * - LGPL
16415  *
16416  * Radio
16417  *
16418  *
16419  * not inline
16420  *<div class="radio">
16421   <label>
16422     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
16423     Option one is this and that&mdash;be sure to include why it's great
16424   </label>
16425 </div>
16426  *
16427  *
16428  *inline
16429  *<span>
16430  *<label class="radio-inline">fieldLabel</label>
16431  *<label class="radio-inline">
16432   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
16433 </label>
16434 <span>
16435  * 
16436  * 
16437  */
16438
16439 /**
16440  * @class Roo.bootstrap.Radio
16441  * @extends Roo.bootstrap.CheckBox
16442  * Bootstrap Radio class
16443
16444  * @constructor
16445  * Create a new Radio
16446  * @param {Object} config The config object
16447  */
16448
16449 Roo.bootstrap.Radio = function(config){
16450     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16451    
16452 };
16453
16454 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16455     
16456     inputType: 'radio',
16457     inputValue: '',
16458     valueOff: '',
16459     
16460     getAutoCreate : function()
16461     {
16462         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16463         align = align || 'left'; // default...
16464         
16465         
16466         
16467         var id = Roo.id();
16468         
16469         var cfg = {
16470                 tag : this.inline ? 'span' : 'div',
16471                 cls : '',
16472                 cn : []
16473         };
16474         
16475         var inline = this.inline ? ' radio-inline' : '';
16476         
16477         var lbl = {
16478                 tag: 'label' ,
16479                 // does not need for, as we wrap the input with it..
16480                 'for' : id,
16481                 cls : 'control-label box-label' + inline,
16482                 cn : []
16483         };
16484         
16485         var fieldLabel = {
16486             tag: 'label' ,
16487             cls : 'control-label' + inline,
16488             html : this.fieldLabel
16489         }
16490         
16491  
16492         
16493         
16494         var input =  {
16495             tag: 'input',
16496             id : id,
16497             type : this.inputType,
16498             //value : (!this.checked) ? this.valueOff : this.inputValue,
16499             value : this.inputValue,
16500             cls : 'roo-radio',
16501             placeholder : this.placeholder || '' // ?? needed????
16502             
16503         };
16504         if (this.weight) { // Validity check?
16505             input.cls += " radio-" + this.weight;
16506         }
16507         if (this.disabled) {
16508             input.disabled=true;
16509         }
16510         
16511         if(this.checked){
16512             input.checked = this.checked;
16513         }
16514         
16515         if (this.name) {
16516             input.name = this.name;
16517         }
16518         
16519         if (this.size) {
16520             input.cls += ' input-' + this.size;
16521         }
16522         
16523         //?? can span's inline have a width??
16524         
16525         var settings=this;
16526         ['xs','sm','md','lg'].map(function(size){
16527             if (settings[size]) {
16528                 cfg.cls += ' col-' + size + '-' + settings[size];
16529             }
16530         });
16531         
16532         var inputblock = input;
16533         
16534         if (this.before || this.after) {
16535             
16536             inputblock = {
16537                 cls : 'input-group',
16538                 tag : 'span',
16539                 cn :  [] 
16540             };
16541             if (this.before) {
16542                 inputblock.cn.push({
16543                     tag :'span',
16544                     cls : 'input-group-addon',
16545                     html : this.before
16546                 });
16547             }
16548             inputblock.cn.push(input);
16549             if (this.after) {
16550                 inputblock.cn.push({
16551                     tag :'span',
16552                     cls : 'input-group-addon',
16553                     html : this.after
16554                 });
16555             }
16556             
16557         };
16558         
16559         
16560         if (this.fieldLabel && this.fieldLabel.length) {
16561             cfg.cn.push(fieldLabel);
16562         }
16563        
16564         lbl.cn.push(inputblock);
16565         cfg.cn.push( {
16566             tag: 'span',
16567             cls: 'radio' + inline,
16568             cn: [
16569                 lbl
16570             ]
16571         });
16572         
16573         if(this.boxLabel){
16574             lbl.cn.push({
16575                 tag: 'span',
16576                 html: this.boxLabel
16577             })
16578         }
16579          
16580         
16581         return cfg;
16582         
16583     },
16584     inputEl: function ()
16585     {
16586         return this.el.select('input.roo-radio',true).first();
16587     },
16588     onClick : function()
16589     {   
16590         Roo.log("click");
16591         Roo.log("click");
16592         this.setChecked(true);
16593     },
16594     
16595     setChecked : function(state,suppressEvent)
16596     {
16597         if(state){
16598             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16599                 v.dom.checked = false;
16600             });
16601         }
16602         
16603         this.checked = state;
16604         this.inputEl().dom.checked = state;
16605         
16606         if(suppressEvent !== true){
16607             this.fireEvent('check', this, state);
16608         }
16609         
16610         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16611         
16612     },
16613     
16614     getGroupValue : function()
16615     {
16616         var value = ''
16617         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16618             if(v.dom.checked == true){
16619                 value = v.dom.value;
16620             }
16621         });
16622         
16623         return value;
16624     },
16625     
16626     /**
16627      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16628      * @return {Mixed} value The field value
16629      */
16630     getValue : function(){
16631         return this.getGroupValue();
16632     }
16633     
16634 });
16635
16636  
16637 //<script type="text/javascript">
16638
16639 /*
16640  * Based  Ext JS Library 1.1.1
16641  * Copyright(c) 2006-2007, Ext JS, LLC.
16642  * LGPL
16643  *
16644  */
16645  
16646 /**
16647  * @class Roo.HtmlEditorCore
16648  * @extends Roo.Component
16649  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16650  *
16651  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16652  */
16653
16654 Roo.HtmlEditorCore = function(config){
16655     
16656     
16657     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16658     
16659     
16660     this.addEvents({
16661         /**
16662          * @event initialize
16663          * Fires when the editor is fully initialized (including the iframe)
16664          * @param {Roo.HtmlEditorCore} this
16665          */
16666         initialize: true,
16667         /**
16668          * @event activate
16669          * Fires when the editor is first receives the focus. Any insertion must wait
16670          * until after this event.
16671          * @param {Roo.HtmlEditorCore} this
16672          */
16673         activate: true,
16674          /**
16675          * @event beforesync
16676          * Fires before the textarea is updated with content from the editor iframe. Return false
16677          * to cancel the sync.
16678          * @param {Roo.HtmlEditorCore} this
16679          * @param {String} html
16680          */
16681         beforesync: true,
16682          /**
16683          * @event beforepush
16684          * Fires before the iframe editor is updated with content from the textarea. Return false
16685          * to cancel the push.
16686          * @param {Roo.HtmlEditorCore} this
16687          * @param {String} html
16688          */
16689         beforepush: true,
16690          /**
16691          * @event sync
16692          * Fires when the textarea is updated with content from the editor iframe.
16693          * @param {Roo.HtmlEditorCore} this
16694          * @param {String} html
16695          */
16696         sync: true,
16697          /**
16698          * @event push
16699          * Fires when the iframe editor is updated with content from the textarea.
16700          * @param {Roo.HtmlEditorCore} this
16701          * @param {String} html
16702          */
16703         push: true,
16704         
16705         /**
16706          * @event editorevent
16707          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16708          * @param {Roo.HtmlEditorCore} this
16709          */
16710         editorevent: true
16711         
16712     });
16713     
16714     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16715     
16716     // defaults : white / black...
16717     this.applyBlacklists();
16718     
16719     
16720     
16721 };
16722
16723
16724 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16725
16726
16727      /**
16728      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16729      */
16730     
16731     owner : false,
16732     
16733      /**
16734      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16735      *                        Roo.resizable.
16736      */
16737     resizable : false,
16738      /**
16739      * @cfg {Number} height (in pixels)
16740      */   
16741     height: 300,
16742    /**
16743      * @cfg {Number} width (in pixels)
16744      */   
16745     width: 500,
16746     
16747     /**
16748      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16749      * 
16750      */
16751     stylesheets: false,
16752     
16753     // id of frame..
16754     frameId: false,
16755     
16756     // private properties
16757     validationEvent : false,
16758     deferHeight: true,
16759     initialized : false,
16760     activated : false,
16761     sourceEditMode : false,
16762     onFocus : Roo.emptyFn,
16763     iframePad:3,
16764     hideMode:'offsets',
16765     
16766     clearUp: true,
16767     
16768     // blacklist + whitelisted elements..
16769     black: false,
16770     white: false,
16771      
16772     
16773
16774     /**
16775      * Protected method that will not generally be called directly. It
16776      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16777      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16778      */
16779     getDocMarkup : function(){
16780         // body styles..
16781         var st = '';
16782         
16783         // inherit styels from page...?? 
16784         if (this.stylesheets === false) {
16785             
16786             Roo.get(document.head).select('style').each(function(node) {
16787                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16788             });
16789             
16790             Roo.get(document.head).select('link').each(function(node) { 
16791                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16792             });
16793             
16794         } else if (!this.stylesheets.length) {
16795                 // simple..
16796                 st = '<style type="text/css">' +
16797                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16798                    '</style>';
16799         } else { 
16800             
16801         }
16802         
16803         st +=  '<style type="text/css">' +
16804             'IMG { cursor: pointer } ' +
16805         '</style>';
16806
16807         
16808         return '<html><head>' + st  +
16809             //<style type="text/css">' +
16810             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16811             //'</style>' +
16812             ' </head><body class="roo-htmleditor-body"></body></html>';
16813     },
16814
16815     // private
16816     onRender : function(ct, position)
16817     {
16818         var _t = this;
16819         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16820         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16821         
16822         
16823         this.el.dom.style.border = '0 none';
16824         this.el.dom.setAttribute('tabIndex', -1);
16825         this.el.addClass('x-hidden hide');
16826         
16827         
16828         
16829         if(Roo.isIE){ // fix IE 1px bogus margin
16830             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16831         }
16832        
16833         
16834         this.frameId = Roo.id();
16835         
16836          
16837         
16838         var iframe = this.owner.wrap.createChild({
16839             tag: 'iframe',
16840             cls: 'form-control', // bootstrap..
16841             id: this.frameId,
16842             name: this.frameId,
16843             frameBorder : 'no',
16844             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16845         }, this.el
16846         );
16847         
16848         
16849         this.iframe = iframe.dom;
16850
16851          this.assignDocWin();
16852         
16853         this.doc.designMode = 'on';
16854        
16855         this.doc.open();
16856         this.doc.write(this.getDocMarkup());
16857         this.doc.close();
16858
16859         
16860         var task = { // must defer to wait for browser to be ready
16861             run : function(){
16862                 //console.log("run task?" + this.doc.readyState);
16863                 this.assignDocWin();
16864                 if(this.doc.body || this.doc.readyState == 'complete'){
16865                     try {
16866                         this.doc.designMode="on";
16867                     } catch (e) {
16868                         return;
16869                     }
16870                     Roo.TaskMgr.stop(task);
16871                     this.initEditor.defer(10, this);
16872                 }
16873             },
16874             interval : 10,
16875             duration: 10000,
16876             scope: this
16877         };
16878         Roo.TaskMgr.start(task);
16879
16880     },
16881
16882     // private
16883     onResize : function(w, h)
16884     {
16885          Roo.log('resize: ' +w + ',' + h );
16886         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16887         if(!this.iframe){
16888             return;
16889         }
16890         if(typeof w == 'number'){
16891             
16892             this.iframe.style.width = w + 'px';
16893         }
16894         if(typeof h == 'number'){
16895             
16896             this.iframe.style.height = h + 'px';
16897             if(this.doc){
16898                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16899             }
16900         }
16901         
16902     },
16903
16904     /**
16905      * Toggles the editor between standard and source edit mode.
16906      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16907      */
16908     toggleSourceEdit : function(sourceEditMode){
16909         
16910         this.sourceEditMode = sourceEditMode === true;
16911         
16912         if(this.sourceEditMode){
16913  
16914             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16915             
16916         }else{
16917             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16918             //this.iframe.className = '';
16919             this.deferFocus();
16920         }
16921         //this.setSize(this.owner.wrap.getSize());
16922         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16923     },
16924
16925     
16926   
16927
16928     /**
16929      * Protected method that will not generally be called directly. If you need/want
16930      * custom HTML cleanup, this is the method you should override.
16931      * @param {String} html The HTML to be cleaned
16932      * return {String} The cleaned HTML
16933      */
16934     cleanHtml : function(html){
16935         html = String(html);
16936         if(html.length > 5){
16937             if(Roo.isSafari){ // strip safari nonsense
16938                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16939             }
16940         }
16941         if(html == '&nbsp;'){
16942             html = '';
16943         }
16944         return html;
16945     },
16946
16947     /**
16948      * HTML Editor -> Textarea
16949      * Protected method that will not generally be called directly. Syncs the contents
16950      * of the editor iframe with the textarea.
16951      */
16952     syncValue : function(){
16953         if(this.initialized){
16954             var bd = (this.doc.body || this.doc.documentElement);
16955             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16956             var html = bd.innerHTML;
16957             if(Roo.isSafari){
16958                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16959                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16960                 if(m && m[1]){
16961                     html = '<div style="'+m[0]+'">' + html + '</div>';
16962                 }
16963             }
16964             html = this.cleanHtml(html);
16965             // fix up the special chars.. normaly like back quotes in word...
16966             // however we do not want to do this with chinese..
16967             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16968                 var cc = b.charCodeAt();
16969                 if (
16970                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16971                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16972                     (cc >= 0xf900 && cc < 0xfb00 )
16973                 ) {
16974                         return b;
16975                 }
16976                 return "&#"+cc+";" 
16977             });
16978             if(this.owner.fireEvent('beforesync', this, html) !== false){
16979                 this.el.dom.value = html;
16980                 this.owner.fireEvent('sync', this, html);
16981             }
16982         }
16983     },
16984
16985     /**
16986      * Protected method that will not generally be called directly. Pushes the value of the textarea
16987      * into the iframe editor.
16988      */
16989     pushValue : function(){
16990         if(this.initialized){
16991             var v = this.el.dom.value.trim();
16992             
16993 //            if(v.length < 1){
16994 //                v = '&#160;';
16995 //            }
16996             
16997             if(this.owner.fireEvent('beforepush', this, v) !== false){
16998                 var d = (this.doc.body || this.doc.documentElement);
16999                 d.innerHTML = v;
17000                 this.cleanUpPaste();
17001                 this.el.dom.value = d.innerHTML;
17002                 this.owner.fireEvent('push', this, v);
17003             }
17004         }
17005     },
17006
17007     // private
17008     deferFocus : function(){
17009         this.focus.defer(10, this);
17010     },
17011
17012     // doc'ed in Field
17013     focus : function(){
17014         if(this.win && !this.sourceEditMode){
17015             this.win.focus();
17016         }else{
17017             this.el.focus();
17018         }
17019     },
17020     
17021     assignDocWin: function()
17022     {
17023         var iframe = this.iframe;
17024         
17025          if(Roo.isIE){
17026             this.doc = iframe.contentWindow.document;
17027             this.win = iframe.contentWindow;
17028         } else {
17029 //            if (!Roo.get(this.frameId)) {
17030 //                return;
17031 //            }
17032 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
17033 //            this.win = Roo.get(this.frameId).dom.contentWindow;
17034             
17035             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
17036                 return;
17037             }
17038             
17039             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
17040             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
17041         }
17042     },
17043     
17044     // private
17045     initEditor : function(){
17046         //console.log("INIT EDITOR");
17047         this.assignDocWin();
17048         
17049         
17050         
17051         this.doc.designMode="on";
17052         this.doc.open();
17053         this.doc.write(this.getDocMarkup());
17054         this.doc.close();
17055         
17056         var dbody = (this.doc.body || this.doc.documentElement);
17057         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
17058         // this copies styles from the containing element into thsi one..
17059         // not sure why we need all of this..
17060         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
17061         
17062         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
17063         //ss['background-attachment'] = 'fixed'; // w3c
17064         dbody.bgProperties = 'fixed'; // ie
17065         //Roo.DomHelper.applyStyles(dbody, ss);
17066         Roo.EventManager.on(this.doc, {
17067             //'mousedown': this.onEditorEvent,
17068             'mouseup': this.onEditorEvent,
17069             'dblclick': this.onEditorEvent,
17070             'click': this.onEditorEvent,
17071             'keyup': this.onEditorEvent,
17072             buffer:100,
17073             scope: this
17074         });
17075         if(Roo.isGecko){
17076             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
17077         }
17078         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
17079             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
17080         }
17081         this.initialized = true;
17082
17083         this.owner.fireEvent('initialize', this);
17084         this.pushValue();
17085     },
17086
17087     // private
17088     onDestroy : function(){
17089         
17090         
17091         
17092         if(this.rendered){
17093             
17094             //for (var i =0; i < this.toolbars.length;i++) {
17095             //    // fixme - ask toolbars for heights?
17096             //    this.toolbars[i].onDestroy();
17097            // }
17098             
17099             //this.wrap.dom.innerHTML = '';
17100             //this.wrap.remove();
17101         }
17102     },
17103
17104     // private
17105     onFirstFocus : function(){
17106         
17107         this.assignDocWin();
17108         
17109         
17110         this.activated = true;
17111          
17112     
17113         if(Roo.isGecko){ // prevent silly gecko errors
17114             this.win.focus();
17115             var s = this.win.getSelection();
17116             if(!s.focusNode || s.focusNode.nodeType != 3){
17117                 var r = s.getRangeAt(0);
17118                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
17119                 r.collapse(true);
17120                 this.deferFocus();
17121             }
17122             try{
17123                 this.execCmd('useCSS', true);
17124                 this.execCmd('styleWithCSS', false);
17125             }catch(e){}
17126         }
17127         this.owner.fireEvent('activate', this);
17128     },
17129
17130     // private
17131     adjustFont: function(btn){
17132         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
17133         //if(Roo.isSafari){ // safari
17134         //    adjust *= 2;
17135        // }
17136         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
17137         if(Roo.isSafari){ // safari
17138             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
17139             v =  (v < 10) ? 10 : v;
17140             v =  (v > 48) ? 48 : v;
17141             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
17142             
17143         }
17144         
17145         
17146         v = Math.max(1, v+adjust);
17147         
17148         this.execCmd('FontSize', v  );
17149     },
17150
17151     onEditorEvent : function(e){
17152         this.owner.fireEvent('editorevent', this, e);
17153       //  this.updateToolbar();
17154         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
17155     },
17156
17157     insertTag : function(tg)
17158     {
17159         // could be a bit smarter... -> wrap the current selected tRoo..
17160         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
17161             
17162             range = this.createRange(this.getSelection());
17163             var wrappingNode = this.doc.createElement(tg.toLowerCase());
17164             wrappingNode.appendChild(range.extractContents());
17165             range.insertNode(wrappingNode);
17166
17167             return;
17168             
17169             
17170             
17171         }
17172         this.execCmd("formatblock",   tg);
17173         
17174     },
17175     
17176     insertText : function(txt)
17177     {
17178         
17179         
17180         var range = this.createRange();
17181         range.deleteContents();
17182                //alert(Sender.getAttribute('label'));
17183                
17184         range.insertNode(this.doc.createTextNode(txt));
17185     } ,
17186     
17187      
17188
17189     /**
17190      * Executes a Midas editor command on the editor document and performs necessary focus and
17191      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
17192      * @param {String} cmd The Midas command
17193      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17194      */
17195     relayCmd : function(cmd, value){
17196         this.win.focus();
17197         this.execCmd(cmd, value);
17198         this.owner.fireEvent('editorevent', this);
17199         //this.updateToolbar();
17200         this.owner.deferFocus();
17201     },
17202
17203     /**
17204      * Executes a Midas editor command directly on the editor document.
17205      * For visual commands, you should use {@link #relayCmd} instead.
17206      * <b>This should only be called after the editor is initialized.</b>
17207      * @param {String} cmd The Midas command
17208      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17209      */
17210     execCmd : function(cmd, value){
17211         this.doc.execCommand(cmd, false, value === undefined ? null : value);
17212         this.syncValue();
17213     },
17214  
17215  
17216    
17217     /**
17218      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
17219      * to insert tRoo.
17220      * @param {String} text | dom node.. 
17221      */
17222     insertAtCursor : function(text)
17223     {
17224         
17225         
17226         
17227         if(!this.activated){
17228             return;
17229         }
17230         /*
17231         if(Roo.isIE){
17232             this.win.focus();
17233             var r = this.doc.selection.createRange();
17234             if(r){
17235                 r.collapse(true);
17236                 r.pasteHTML(text);
17237                 this.syncValue();
17238                 this.deferFocus();
17239             
17240             }
17241             return;
17242         }
17243         */
17244         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
17245             this.win.focus();
17246             
17247             
17248             // from jquery ui (MIT licenced)
17249             var range, node;
17250             var win = this.win;
17251             
17252             if (win.getSelection && win.getSelection().getRangeAt) {
17253                 range = win.getSelection().getRangeAt(0);
17254                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
17255                 range.insertNode(node);
17256             } else if (win.document.selection && win.document.selection.createRange) {
17257                 // no firefox support
17258                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17259                 win.document.selection.createRange().pasteHTML(txt);
17260             } else {
17261                 // no firefox support
17262                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17263                 this.execCmd('InsertHTML', txt);
17264             } 
17265             
17266             this.syncValue();
17267             
17268             this.deferFocus();
17269         }
17270     },
17271  // private
17272     mozKeyPress : function(e){
17273         if(e.ctrlKey){
17274             var c = e.getCharCode(), cmd;
17275           
17276             if(c > 0){
17277                 c = String.fromCharCode(c).toLowerCase();
17278                 switch(c){
17279                     case 'b':
17280                         cmd = 'bold';
17281                         break;
17282                     case 'i':
17283                         cmd = 'italic';
17284                         break;
17285                     
17286                     case 'u':
17287                         cmd = 'underline';
17288                         break;
17289                     
17290                     case 'v':
17291                         this.cleanUpPaste.defer(100, this);
17292                         return;
17293                         
17294                 }
17295                 if(cmd){
17296                     this.win.focus();
17297                     this.execCmd(cmd);
17298                     this.deferFocus();
17299                     e.preventDefault();
17300                 }
17301                 
17302             }
17303         }
17304     },
17305
17306     // private
17307     fixKeys : function(){ // load time branching for fastest keydown performance
17308         if(Roo.isIE){
17309             return function(e){
17310                 var k = e.getKey(), r;
17311                 if(k == e.TAB){
17312                     e.stopEvent();
17313                     r = this.doc.selection.createRange();
17314                     if(r){
17315                         r.collapse(true);
17316                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17317                         this.deferFocus();
17318                     }
17319                     return;
17320                 }
17321                 
17322                 if(k == e.ENTER){
17323                     r = this.doc.selection.createRange();
17324                     if(r){
17325                         var target = r.parentElement();
17326                         if(!target || target.tagName.toLowerCase() != 'li'){
17327                             e.stopEvent();
17328                             r.pasteHTML('<br />');
17329                             r.collapse(false);
17330                             r.select();
17331                         }
17332                     }
17333                 }
17334                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17335                     this.cleanUpPaste.defer(100, this);
17336                     return;
17337                 }
17338                 
17339                 
17340             };
17341         }else if(Roo.isOpera){
17342             return function(e){
17343                 var k = e.getKey();
17344                 if(k == e.TAB){
17345                     e.stopEvent();
17346                     this.win.focus();
17347                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17348                     this.deferFocus();
17349                 }
17350                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17351                     this.cleanUpPaste.defer(100, this);
17352                     return;
17353                 }
17354                 
17355             };
17356         }else if(Roo.isSafari){
17357             return function(e){
17358                 var k = e.getKey();
17359                 
17360                 if(k == e.TAB){
17361                     e.stopEvent();
17362                     this.execCmd('InsertText','\t');
17363                     this.deferFocus();
17364                     return;
17365                 }
17366                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17367                     this.cleanUpPaste.defer(100, this);
17368                     return;
17369                 }
17370                 
17371              };
17372         }
17373     }(),
17374     
17375     getAllAncestors: function()
17376     {
17377         var p = this.getSelectedNode();
17378         var a = [];
17379         if (!p) {
17380             a.push(p); // push blank onto stack..
17381             p = this.getParentElement();
17382         }
17383         
17384         
17385         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17386             a.push(p);
17387             p = p.parentNode;
17388         }
17389         a.push(this.doc.body);
17390         return a;
17391     },
17392     lastSel : false,
17393     lastSelNode : false,
17394     
17395     
17396     getSelection : function() 
17397     {
17398         this.assignDocWin();
17399         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17400     },
17401     
17402     getSelectedNode: function() 
17403     {
17404         // this may only work on Gecko!!!
17405         
17406         // should we cache this!!!!
17407         
17408         
17409         
17410          
17411         var range = this.createRange(this.getSelection()).cloneRange();
17412         
17413         if (Roo.isIE) {
17414             var parent = range.parentElement();
17415             while (true) {
17416                 var testRange = range.duplicate();
17417                 testRange.moveToElementText(parent);
17418                 if (testRange.inRange(range)) {
17419                     break;
17420                 }
17421                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17422                     break;
17423                 }
17424                 parent = parent.parentElement;
17425             }
17426             return parent;
17427         }
17428         
17429         // is ancestor a text element.
17430         var ac =  range.commonAncestorContainer;
17431         if (ac.nodeType == 3) {
17432             ac = ac.parentNode;
17433         }
17434         
17435         var ar = ac.childNodes;
17436          
17437         var nodes = [];
17438         var other_nodes = [];
17439         var has_other_nodes = false;
17440         for (var i=0;i<ar.length;i++) {
17441             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17442                 continue;
17443             }
17444             // fullly contained node.
17445             
17446             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17447                 nodes.push(ar[i]);
17448                 continue;
17449             }
17450             
17451             // probably selected..
17452             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17453                 other_nodes.push(ar[i]);
17454                 continue;
17455             }
17456             // outer..
17457             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17458                 continue;
17459             }
17460             
17461             
17462             has_other_nodes = true;
17463         }
17464         if (!nodes.length && other_nodes.length) {
17465             nodes= other_nodes;
17466         }
17467         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17468             return false;
17469         }
17470         
17471         return nodes[0];
17472     },
17473     createRange: function(sel)
17474     {
17475         // this has strange effects when using with 
17476         // top toolbar - not sure if it's a great idea.
17477         //this.editor.contentWindow.focus();
17478         if (typeof sel != "undefined") {
17479             try {
17480                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17481             } catch(e) {
17482                 return this.doc.createRange();
17483             }
17484         } else {
17485             return this.doc.createRange();
17486         }
17487     },
17488     getParentElement: function()
17489     {
17490         
17491         this.assignDocWin();
17492         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17493         
17494         var range = this.createRange(sel);
17495          
17496         try {
17497             var p = range.commonAncestorContainer;
17498             while (p.nodeType == 3) { // text node
17499                 p = p.parentNode;
17500             }
17501             return p;
17502         } catch (e) {
17503             return null;
17504         }
17505     
17506     },
17507     /***
17508      *
17509      * Range intersection.. the hard stuff...
17510      *  '-1' = before
17511      *  '0' = hits..
17512      *  '1' = after.
17513      *         [ -- selected range --- ]
17514      *   [fail]                        [fail]
17515      *
17516      *    basically..
17517      *      if end is before start or  hits it. fail.
17518      *      if start is after end or hits it fail.
17519      *
17520      *   if either hits (but other is outside. - then it's not 
17521      *   
17522      *    
17523      **/
17524     
17525     
17526     // @see http://www.thismuchiknow.co.uk/?p=64.
17527     rangeIntersectsNode : function(range, node)
17528     {
17529         var nodeRange = node.ownerDocument.createRange();
17530         try {
17531             nodeRange.selectNode(node);
17532         } catch (e) {
17533             nodeRange.selectNodeContents(node);
17534         }
17535     
17536         var rangeStartRange = range.cloneRange();
17537         rangeStartRange.collapse(true);
17538     
17539         var rangeEndRange = range.cloneRange();
17540         rangeEndRange.collapse(false);
17541     
17542         var nodeStartRange = nodeRange.cloneRange();
17543         nodeStartRange.collapse(true);
17544     
17545         var nodeEndRange = nodeRange.cloneRange();
17546         nodeEndRange.collapse(false);
17547     
17548         return rangeStartRange.compareBoundaryPoints(
17549                  Range.START_TO_START, nodeEndRange) == -1 &&
17550                rangeEndRange.compareBoundaryPoints(
17551                  Range.START_TO_START, nodeStartRange) == 1;
17552         
17553          
17554     },
17555     rangeCompareNode : function(range, node)
17556     {
17557         var nodeRange = node.ownerDocument.createRange();
17558         try {
17559             nodeRange.selectNode(node);
17560         } catch (e) {
17561             nodeRange.selectNodeContents(node);
17562         }
17563         
17564         
17565         range.collapse(true);
17566     
17567         nodeRange.collapse(true);
17568      
17569         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17570         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17571          
17572         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17573         
17574         var nodeIsBefore   =  ss == 1;
17575         var nodeIsAfter    = ee == -1;
17576         
17577         if (nodeIsBefore && nodeIsAfter)
17578             return 0; // outer
17579         if (!nodeIsBefore && nodeIsAfter)
17580             return 1; //right trailed.
17581         
17582         if (nodeIsBefore && !nodeIsAfter)
17583             return 2;  // left trailed.
17584         // fully contined.
17585         return 3;
17586     },
17587
17588     // private? - in a new class?
17589     cleanUpPaste :  function()
17590     {
17591         // cleans up the whole document..
17592         Roo.log('cleanuppaste');
17593         
17594         this.cleanUpChildren(this.doc.body);
17595         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17596         if (clean != this.doc.body.innerHTML) {
17597             this.doc.body.innerHTML = clean;
17598         }
17599         
17600     },
17601     
17602     cleanWordChars : function(input) {// change the chars to hex code
17603         var he = Roo.HtmlEditorCore;
17604         
17605         var output = input;
17606         Roo.each(he.swapCodes, function(sw) { 
17607             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17608             
17609             output = output.replace(swapper, sw[1]);
17610         });
17611         
17612         return output;
17613     },
17614     
17615     
17616     cleanUpChildren : function (n)
17617     {
17618         if (!n.childNodes.length) {
17619             return;
17620         }
17621         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17622            this.cleanUpChild(n.childNodes[i]);
17623         }
17624     },
17625     
17626     
17627         
17628     
17629     cleanUpChild : function (node)
17630     {
17631         var ed = this;
17632         //console.log(node);
17633         if (node.nodeName == "#text") {
17634             // clean up silly Windows -- stuff?
17635             return; 
17636         }
17637         if (node.nodeName == "#comment") {
17638             node.parentNode.removeChild(node);
17639             // clean up silly Windows -- stuff?
17640             return; 
17641         }
17642         var lcname = node.tagName.toLowerCase();
17643         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17644         // whitelist of tags..
17645         
17646         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17647             // remove node.
17648             node.parentNode.removeChild(node);
17649             return;
17650             
17651         }
17652         
17653         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17654         
17655         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17656         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17657         
17658         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17659         //    remove_keep_children = true;
17660         //}
17661         
17662         if (remove_keep_children) {
17663             this.cleanUpChildren(node);
17664             // inserts everything just before this node...
17665             while (node.childNodes.length) {
17666                 var cn = node.childNodes[0];
17667                 node.removeChild(cn);
17668                 node.parentNode.insertBefore(cn, node);
17669             }
17670             node.parentNode.removeChild(node);
17671             return;
17672         }
17673         
17674         if (!node.attributes || !node.attributes.length) {
17675             this.cleanUpChildren(node);
17676             return;
17677         }
17678         
17679         function cleanAttr(n,v)
17680         {
17681             
17682             if (v.match(/^\./) || v.match(/^\//)) {
17683                 return;
17684             }
17685             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17686                 return;
17687             }
17688             if (v.match(/^#/)) {
17689                 return;
17690             }
17691 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17692             node.removeAttribute(n);
17693             
17694         }
17695         
17696         var cwhite = this.cwhite;
17697         var cblack = this.cblack;
17698             
17699         function cleanStyle(n,v)
17700         {
17701             if (v.match(/expression/)) { //XSS?? should we even bother..
17702                 node.removeAttribute(n);
17703                 return;
17704             }
17705             
17706             var parts = v.split(/;/);
17707             var clean = [];
17708             
17709             Roo.each(parts, function(p) {
17710                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17711                 if (!p.length) {
17712                     return true;
17713                 }
17714                 var l = p.split(':').shift().replace(/\s+/g,'');
17715                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17716                 
17717                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17718 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17719                     //node.removeAttribute(n);
17720                     return true;
17721                 }
17722                 //Roo.log()
17723                 // only allow 'c whitelisted system attributes'
17724                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17725 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17726                     //node.removeAttribute(n);
17727                     return true;
17728                 }
17729                 
17730                 
17731                  
17732                 
17733                 clean.push(p);
17734                 return true;
17735             });
17736             if (clean.length) { 
17737                 node.setAttribute(n, clean.join(';'));
17738             } else {
17739                 node.removeAttribute(n);
17740             }
17741             
17742         }
17743         
17744         
17745         for (var i = node.attributes.length-1; i > -1 ; i--) {
17746             var a = node.attributes[i];
17747             //console.log(a);
17748             
17749             if (a.name.toLowerCase().substr(0,2)=='on')  {
17750                 node.removeAttribute(a.name);
17751                 continue;
17752             }
17753             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17754                 node.removeAttribute(a.name);
17755                 continue;
17756             }
17757             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17758                 cleanAttr(a.name,a.value); // fixme..
17759                 continue;
17760             }
17761             if (a.name == 'style') {
17762                 cleanStyle(a.name,a.value);
17763                 continue;
17764             }
17765             /// clean up MS crap..
17766             // tecnically this should be a list of valid class'es..
17767             
17768             
17769             if (a.name == 'class') {
17770                 if (a.value.match(/^Mso/)) {
17771                     node.className = '';
17772                 }
17773                 
17774                 if (a.value.match(/body/)) {
17775                     node.className = '';
17776                 }
17777                 continue;
17778             }
17779             
17780             // style cleanup!?
17781             // class cleanup?
17782             
17783         }
17784         
17785         
17786         this.cleanUpChildren(node);
17787         
17788         
17789     },
17790     /**
17791      * Clean up MS wordisms...
17792      */
17793     cleanWord : function(node)
17794     {
17795         var _t = this;
17796         var cleanWordChildren = function()
17797         {
17798             if (!node.childNodes.length) {
17799                 return;
17800             }
17801             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17802                _t.cleanWord(node.childNodes[i]);
17803             }
17804         }
17805         
17806         
17807         if (!node) {
17808             this.cleanWord(this.doc.body);
17809             return;
17810         }
17811         if (node.nodeName == "#text") {
17812             // clean up silly Windows -- stuff?
17813             return; 
17814         }
17815         if (node.nodeName == "#comment") {
17816             node.parentNode.removeChild(node);
17817             // clean up silly Windows -- stuff?
17818             return; 
17819         }
17820         
17821         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17822             node.parentNode.removeChild(node);
17823             return;
17824         }
17825         
17826         // remove - but keep children..
17827         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17828             while (node.childNodes.length) {
17829                 var cn = node.childNodes[0];
17830                 node.removeChild(cn);
17831                 node.parentNode.insertBefore(cn, node);
17832             }
17833             node.parentNode.removeChild(node);
17834             cleanWordChildren();
17835             return;
17836         }
17837         // clean styles
17838         if (node.className.length) {
17839             
17840             var cn = node.className.split(/\W+/);
17841             var cna = [];
17842             Roo.each(cn, function(cls) {
17843                 if (cls.match(/Mso[a-zA-Z]+/)) {
17844                     return;
17845                 }
17846                 cna.push(cls);
17847             });
17848             node.className = cna.length ? cna.join(' ') : '';
17849             if (!cna.length) {
17850                 node.removeAttribute("class");
17851             }
17852         }
17853         
17854         if (node.hasAttribute("lang")) {
17855             node.removeAttribute("lang");
17856         }
17857         
17858         if (node.hasAttribute("style")) {
17859             
17860             var styles = node.getAttribute("style").split(";");
17861             var nstyle = [];
17862             Roo.each(styles, function(s) {
17863                 if (!s.match(/:/)) {
17864                     return;
17865                 }
17866                 var kv = s.split(":");
17867                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17868                     return;
17869                 }
17870                 // what ever is left... we allow.
17871                 nstyle.push(s);
17872             });
17873             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17874             if (!nstyle.length) {
17875                 node.removeAttribute('style');
17876             }
17877         }
17878         
17879         cleanWordChildren();
17880         
17881         
17882     },
17883     domToHTML : function(currentElement, depth, nopadtext) {
17884         
17885         depth = depth || 0;
17886         nopadtext = nopadtext || false;
17887     
17888         if (!currentElement) {
17889             return this.domToHTML(this.doc.body);
17890         }
17891         
17892         //Roo.log(currentElement);
17893         var j;
17894         var allText = false;
17895         var nodeName = currentElement.nodeName;
17896         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17897         
17898         if  (nodeName == '#text') {
17899             
17900             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
17901         }
17902         
17903         
17904         var ret = '';
17905         if (nodeName != 'BODY') {
17906              
17907             var i = 0;
17908             // Prints the node tagName, such as <A>, <IMG>, etc
17909             if (tagName) {
17910                 var attr = [];
17911                 for(i = 0; i < currentElement.attributes.length;i++) {
17912                     // quoting?
17913                     var aname = currentElement.attributes.item(i).name;
17914                     if (!currentElement.attributes.item(i).value.length) {
17915                         continue;
17916                     }
17917                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17918                 }
17919                 
17920                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17921             } 
17922             else {
17923                 
17924                 // eack
17925             }
17926         } else {
17927             tagName = false;
17928         }
17929         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17930             return ret;
17931         }
17932         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17933             nopadtext = true;
17934         }
17935         
17936         
17937         // Traverse the tree
17938         i = 0;
17939         var currentElementChild = currentElement.childNodes.item(i);
17940         var allText = true;
17941         var innerHTML  = '';
17942         lastnode = '';
17943         while (currentElementChild) {
17944             // Formatting code (indent the tree so it looks nice on the screen)
17945             var nopad = nopadtext;
17946             if (lastnode == 'SPAN') {
17947                 nopad  = true;
17948             }
17949             // text
17950             if  (currentElementChild.nodeName == '#text') {
17951                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17952                 toadd = nopadtext ? toadd : toadd.trim();
17953                 if (!nopad && toadd.length > 80) {
17954                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17955                 }
17956                 innerHTML  += toadd;
17957                 
17958                 i++;
17959                 currentElementChild = currentElement.childNodes.item(i);
17960                 lastNode = '';
17961                 continue;
17962             }
17963             allText = false;
17964             
17965             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17966                 
17967             // Recursively traverse the tree structure of the child node
17968             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17969             lastnode = currentElementChild.nodeName;
17970             i++;
17971             currentElementChild=currentElement.childNodes.item(i);
17972         }
17973         
17974         ret += innerHTML;
17975         
17976         if (!allText) {
17977                 // The remaining code is mostly for formatting the tree
17978             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17979         }
17980         
17981         
17982         if (tagName) {
17983             ret+= "</"+tagName+">";
17984         }
17985         return ret;
17986         
17987     },
17988         
17989     applyBlacklists : function()
17990     {
17991         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17992         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17993         
17994         this.white = [];
17995         this.black = [];
17996         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17997             if (b.indexOf(tag) > -1) {
17998                 return;
17999             }
18000             this.white.push(tag);
18001             
18002         }, this);
18003         
18004         Roo.each(w, function(tag) {
18005             if (b.indexOf(tag) > -1) {
18006                 return;
18007             }
18008             if (this.white.indexOf(tag) > -1) {
18009                 return;
18010             }
18011             this.white.push(tag);
18012             
18013         }, this);
18014         
18015         
18016         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
18017             if (w.indexOf(tag) > -1) {
18018                 return;
18019             }
18020             this.black.push(tag);
18021             
18022         }, this);
18023         
18024         Roo.each(b, function(tag) {
18025             if (w.indexOf(tag) > -1) {
18026                 return;
18027             }
18028             if (this.black.indexOf(tag) > -1) {
18029                 return;
18030             }
18031             this.black.push(tag);
18032             
18033         }, this);
18034         
18035         
18036         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
18037         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
18038         
18039         this.cwhite = [];
18040         this.cblack = [];
18041         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
18042             if (b.indexOf(tag) > -1) {
18043                 return;
18044             }
18045             this.cwhite.push(tag);
18046             
18047         }, this);
18048         
18049         Roo.each(w, function(tag) {
18050             if (b.indexOf(tag) > -1) {
18051                 return;
18052             }
18053             if (this.cwhite.indexOf(tag) > -1) {
18054                 return;
18055             }
18056             this.cwhite.push(tag);
18057             
18058         }, this);
18059         
18060         
18061         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
18062             if (w.indexOf(tag) > -1) {
18063                 return;
18064             }
18065             this.cblack.push(tag);
18066             
18067         }, this);
18068         
18069         Roo.each(b, function(tag) {
18070             if (w.indexOf(tag) > -1) {
18071                 return;
18072             }
18073             if (this.cblack.indexOf(tag) > -1) {
18074                 return;
18075             }
18076             this.cblack.push(tag);
18077             
18078         }, this);
18079     },
18080     
18081     setStylesheets : function(stylesheets)
18082     {
18083         if(typeof(stylesheets) == 'string'){
18084             Roo.get(this.iframe.contentDocument.head).createChild({
18085                 tag : 'link',
18086                 rel : 'stylesheet',
18087                 type : 'text/css',
18088                 href : stylesheets
18089             });
18090             
18091             return;
18092         }
18093         var _this = this;
18094      
18095         Roo.each(stylesheets, function(s) {
18096             if(!s.length){
18097                 return;
18098             }
18099             
18100             Roo.get(_this.iframe.contentDocument.head).createChild({
18101                 tag : 'link',
18102                 rel : 'stylesheet',
18103                 type : 'text/css',
18104                 href : s
18105             });
18106         });
18107
18108         
18109     },
18110     
18111     removeStylesheets : function()
18112     {
18113         var _this = this;
18114         
18115         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
18116             s.remove();
18117         });
18118     }
18119     
18120     // hide stuff that is not compatible
18121     /**
18122      * @event blur
18123      * @hide
18124      */
18125     /**
18126      * @event change
18127      * @hide
18128      */
18129     /**
18130      * @event focus
18131      * @hide
18132      */
18133     /**
18134      * @event specialkey
18135      * @hide
18136      */
18137     /**
18138      * @cfg {String} fieldClass @hide
18139      */
18140     /**
18141      * @cfg {String} focusClass @hide
18142      */
18143     /**
18144      * @cfg {String} autoCreate @hide
18145      */
18146     /**
18147      * @cfg {String} inputType @hide
18148      */
18149     /**
18150      * @cfg {String} invalidClass @hide
18151      */
18152     /**
18153      * @cfg {String} invalidText @hide
18154      */
18155     /**
18156      * @cfg {String} msgFx @hide
18157      */
18158     /**
18159      * @cfg {String} validateOnBlur @hide
18160      */
18161 });
18162
18163 Roo.HtmlEditorCore.white = [
18164         'area', 'br', 'img', 'input', 'hr', 'wbr',
18165         
18166        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
18167        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
18168        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
18169        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
18170        'table',   'ul',         'xmp', 
18171        
18172        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
18173       'thead',   'tr', 
18174      
18175       'dir', 'menu', 'ol', 'ul', 'dl',
18176        
18177       'embed',  'object'
18178 ];
18179
18180
18181 Roo.HtmlEditorCore.black = [
18182     //    'embed',  'object', // enable - backend responsiblity to clean thiese
18183         'applet', // 
18184         'base',   'basefont', 'bgsound', 'blink',  'body', 
18185         'frame',  'frameset', 'head',    'html',   'ilayer', 
18186         'iframe', 'layer',  'link',     'meta',    'object',   
18187         'script', 'style' ,'title',  'xml' // clean later..
18188 ];
18189 Roo.HtmlEditorCore.clean = [
18190     'script', 'style', 'title', 'xml'
18191 ];
18192 Roo.HtmlEditorCore.remove = [
18193     'font'
18194 ];
18195 // attributes..
18196
18197 Roo.HtmlEditorCore.ablack = [
18198     'on'
18199 ];
18200     
18201 Roo.HtmlEditorCore.aclean = [ 
18202     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
18203 ];
18204
18205 // protocols..
18206 Roo.HtmlEditorCore.pwhite= [
18207         'http',  'https',  'mailto'
18208 ];
18209
18210 // white listed style attributes.
18211 Roo.HtmlEditorCore.cwhite= [
18212       //  'text-align', /// default is to allow most things..
18213       
18214          
18215 //        'font-size'//??
18216 ];
18217
18218 // black listed style attributes.
18219 Roo.HtmlEditorCore.cblack= [
18220       //  'font-size' -- this can be set by the project 
18221 ];
18222
18223
18224 Roo.HtmlEditorCore.swapCodes   =[ 
18225     [    8211, "--" ], 
18226     [    8212, "--" ], 
18227     [    8216,  "'" ],  
18228     [    8217, "'" ],  
18229     [    8220, '"' ],  
18230     [    8221, '"' ],  
18231     [    8226, "*" ],  
18232     [    8230, "..." ]
18233 ]; 
18234
18235     /*
18236  * - LGPL
18237  *
18238  * HtmlEditor
18239  * 
18240  */
18241
18242 /**
18243  * @class Roo.bootstrap.HtmlEditor
18244  * @extends Roo.bootstrap.TextArea
18245  * Bootstrap HtmlEditor class
18246
18247  * @constructor
18248  * Create a new HtmlEditor
18249  * @param {Object} config The config object
18250  */
18251
18252 Roo.bootstrap.HtmlEditor = function(config){
18253     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
18254     if (!this.toolbars) {
18255         this.toolbars = [];
18256     }
18257     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
18258     this.addEvents({
18259             /**
18260              * @event initialize
18261              * Fires when the editor is fully initialized (including the iframe)
18262              * @param {HtmlEditor} this
18263              */
18264             initialize: true,
18265             /**
18266              * @event activate
18267              * Fires when the editor is first receives the focus. Any insertion must wait
18268              * until after this event.
18269              * @param {HtmlEditor} this
18270              */
18271             activate: true,
18272              /**
18273              * @event beforesync
18274              * Fires before the textarea is updated with content from the editor iframe. Return false
18275              * to cancel the sync.
18276              * @param {HtmlEditor} this
18277              * @param {String} html
18278              */
18279             beforesync: true,
18280              /**
18281              * @event beforepush
18282              * Fires before the iframe editor is updated with content from the textarea. Return false
18283              * to cancel the push.
18284              * @param {HtmlEditor} this
18285              * @param {String} html
18286              */
18287             beforepush: true,
18288              /**
18289              * @event sync
18290              * Fires when the textarea is updated with content from the editor iframe.
18291              * @param {HtmlEditor} this
18292              * @param {String} html
18293              */
18294             sync: true,
18295              /**
18296              * @event push
18297              * Fires when the iframe editor is updated with content from the textarea.
18298              * @param {HtmlEditor} this
18299              * @param {String} html
18300              */
18301             push: true,
18302              /**
18303              * @event editmodechange
18304              * Fires when the editor switches edit modes
18305              * @param {HtmlEditor} this
18306              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
18307              */
18308             editmodechange: true,
18309             /**
18310              * @event editorevent
18311              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18312              * @param {HtmlEditor} this
18313              */
18314             editorevent: true,
18315             /**
18316              * @event firstfocus
18317              * Fires when on first focus - needed by toolbars..
18318              * @param {HtmlEditor} this
18319              */
18320             firstfocus: true,
18321             /**
18322              * @event autosave
18323              * Auto save the htmlEditor value as a file into Events
18324              * @param {HtmlEditor} this
18325              */
18326             autosave: true,
18327             /**
18328              * @event savedpreview
18329              * preview the saved version of htmlEditor
18330              * @param {HtmlEditor} this
18331              */
18332             savedpreview: true
18333         });
18334 };
18335
18336
18337 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
18338     
18339     
18340       /**
18341      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18342      */
18343     toolbars : false,
18344    
18345      /**
18346      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18347      *                        Roo.resizable.
18348      */
18349     resizable : false,
18350      /**
18351      * @cfg {Number} height (in pixels)
18352      */   
18353     height: 300,
18354    /**
18355      * @cfg {Number} width (in pixels)
18356      */   
18357     width: false,
18358     
18359     /**
18360      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18361      * 
18362      */
18363     stylesheets: false,
18364     
18365     // id of frame..
18366     frameId: false,
18367     
18368     // private properties
18369     validationEvent : false,
18370     deferHeight: true,
18371     initialized : false,
18372     activated : false,
18373     
18374     onFocus : Roo.emptyFn,
18375     iframePad:3,
18376     hideMode:'offsets',
18377     
18378     
18379     tbContainer : false,
18380     
18381     toolbarContainer :function() {
18382         return this.wrap.select('.x-html-editor-tb',true).first();
18383     },
18384
18385     /**
18386      * Protected method that will not generally be called directly. It
18387      * is called when the editor creates its toolbar. Override this method if you need to
18388      * add custom toolbar buttons.
18389      * @param {HtmlEditor} editor
18390      */
18391     createToolbar : function(){
18392         
18393         Roo.log("create toolbars");
18394         
18395         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18396         this.toolbars[0].render(this.toolbarContainer());
18397         
18398         return;
18399         
18400 //        if (!editor.toolbars || !editor.toolbars.length) {
18401 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18402 //        }
18403 //        
18404 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18405 //            editor.toolbars[i] = Roo.factory(
18406 //                    typeof(editor.toolbars[i]) == 'string' ?
18407 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18408 //                Roo.bootstrap.HtmlEditor);
18409 //            editor.toolbars[i].init(editor);
18410 //        }
18411     },
18412
18413      
18414     // private
18415     onRender : function(ct, position)
18416     {
18417        // Roo.log("Call onRender: " + this.xtype);
18418         var _t = this;
18419         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18420       
18421         this.wrap = this.inputEl().wrap({
18422             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18423         });
18424         
18425         this.editorcore.onRender(ct, position);
18426          
18427         if (this.resizable) {
18428             this.resizeEl = new Roo.Resizable(this.wrap, {
18429                 pinned : true,
18430                 wrap: true,
18431                 dynamic : true,
18432                 minHeight : this.height,
18433                 height: this.height,
18434                 handles : this.resizable,
18435                 width: this.width,
18436                 listeners : {
18437                     resize : function(r, w, h) {
18438                         _t.onResize(w,h); // -something
18439                     }
18440                 }
18441             });
18442             
18443         }
18444         this.createToolbar(this);
18445        
18446         
18447         if(!this.width && this.resizable){
18448             this.setSize(this.wrap.getSize());
18449         }
18450         if (this.resizeEl) {
18451             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18452             // should trigger onReize..
18453         }
18454         
18455     },
18456
18457     // private
18458     onResize : function(w, h)
18459     {
18460         Roo.log('resize: ' +w + ',' + h );
18461         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18462         var ew = false;
18463         var eh = false;
18464         
18465         if(this.inputEl() ){
18466             if(typeof w == 'number'){
18467                 var aw = w - this.wrap.getFrameWidth('lr');
18468                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18469                 ew = aw;
18470             }
18471             if(typeof h == 'number'){
18472                  var tbh = -11;  // fixme it needs to tool bar size!
18473                 for (var i =0; i < this.toolbars.length;i++) {
18474                     // fixme - ask toolbars for heights?
18475                     tbh += this.toolbars[i].el.getHeight();
18476                     //if (this.toolbars[i].footer) {
18477                     //    tbh += this.toolbars[i].footer.el.getHeight();
18478                     //}
18479                 }
18480               
18481                 
18482                 
18483                 
18484                 
18485                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18486                 ah -= 5; // knock a few pixes off for look..
18487                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18488                 var eh = ah;
18489             }
18490         }
18491         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18492         this.editorcore.onResize(ew,eh);
18493         
18494     },
18495
18496     /**
18497      * Toggles the editor between standard and source edit mode.
18498      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18499      */
18500     toggleSourceEdit : function(sourceEditMode)
18501     {
18502         this.editorcore.toggleSourceEdit(sourceEditMode);
18503         
18504         if(this.editorcore.sourceEditMode){
18505             Roo.log('editor - showing textarea');
18506             
18507 //            Roo.log('in');
18508 //            Roo.log(this.syncValue());
18509             this.syncValue();
18510             this.inputEl().removeClass(['hide', 'x-hidden']);
18511             this.inputEl().dom.removeAttribute('tabIndex');
18512             this.inputEl().focus();
18513         }else{
18514             Roo.log('editor - hiding textarea');
18515 //            Roo.log('out')
18516 //            Roo.log(this.pushValue()); 
18517             this.pushValue();
18518             
18519             this.inputEl().addClass(['hide', 'x-hidden']);
18520             this.inputEl().dom.setAttribute('tabIndex', -1);
18521             //this.deferFocus();
18522         }
18523          
18524         if(this.resizable){
18525             this.setSize(this.wrap.getSize());
18526         }
18527         
18528         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18529     },
18530  
18531     // private (for BoxComponent)
18532     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18533
18534     // private (for BoxComponent)
18535     getResizeEl : function(){
18536         return this.wrap;
18537     },
18538
18539     // private (for BoxComponent)
18540     getPositionEl : function(){
18541         return this.wrap;
18542     },
18543
18544     // private
18545     initEvents : function(){
18546         this.originalValue = this.getValue();
18547     },
18548
18549 //    /**
18550 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18551 //     * @method
18552 //     */
18553 //    markInvalid : Roo.emptyFn,
18554 //    /**
18555 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18556 //     * @method
18557 //     */
18558 //    clearInvalid : Roo.emptyFn,
18559
18560     setValue : function(v){
18561         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18562         this.editorcore.pushValue();
18563     },
18564
18565      
18566     // private
18567     deferFocus : function(){
18568         this.focus.defer(10, this);
18569     },
18570
18571     // doc'ed in Field
18572     focus : function(){
18573         this.editorcore.focus();
18574         
18575     },
18576       
18577
18578     // private
18579     onDestroy : function(){
18580         
18581         
18582         
18583         if(this.rendered){
18584             
18585             for (var i =0; i < this.toolbars.length;i++) {
18586                 // fixme - ask toolbars for heights?
18587                 this.toolbars[i].onDestroy();
18588             }
18589             
18590             this.wrap.dom.innerHTML = '';
18591             this.wrap.remove();
18592         }
18593     },
18594
18595     // private
18596     onFirstFocus : function(){
18597         //Roo.log("onFirstFocus");
18598         this.editorcore.onFirstFocus();
18599          for (var i =0; i < this.toolbars.length;i++) {
18600             this.toolbars[i].onFirstFocus();
18601         }
18602         
18603     },
18604     
18605     // private
18606     syncValue : function()
18607     {   
18608         this.editorcore.syncValue();
18609     },
18610     
18611     pushValue : function()
18612     {   
18613         this.editorcore.pushValue();
18614     }
18615      
18616     
18617     // hide stuff that is not compatible
18618     /**
18619      * @event blur
18620      * @hide
18621      */
18622     /**
18623      * @event change
18624      * @hide
18625      */
18626     /**
18627      * @event focus
18628      * @hide
18629      */
18630     /**
18631      * @event specialkey
18632      * @hide
18633      */
18634     /**
18635      * @cfg {String} fieldClass @hide
18636      */
18637     /**
18638      * @cfg {String} focusClass @hide
18639      */
18640     /**
18641      * @cfg {String} autoCreate @hide
18642      */
18643     /**
18644      * @cfg {String} inputType @hide
18645      */
18646     /**
18647      * @cfg {String} invalidClass @hide
18648      */
18649     /**
18650      * @cfg {String} invalidText @hide
18651      */
18652     /**
18653      * @cfg {String} msgFx @hide
18654      */
18655     /**
18656      * @cfg {String} validateOnBlur @hide
18657      */
18658 });
18659  
18660     
18661    
18662    
18663    
18664       
18665 Roo.namespace('Roo.bootstrap.htmleditor');
18666 /**
18667  * @class Roo.bootstrap.HtmlEditorToolbar1
18668  * Basic Toolbar
18669  * 
18670  * Usage:
18671  *
18672  new Roo.bootstrap.HtmlEditor({
18673     ....
18674     toolbars : [
18675         new Roo.bootstrap.HtmlEditorToolbar1({
18676             disable : { fonts: 1 , format: 1, ..., ... , ...],
18677             btns : [ .... ]
18678         })
18679     }
18680      
18681  * 
18682  * @cfg {Object} disable List of elements to disable..
18683  * @cfg {Array} btns List of additional buttons.
18684  * 
18685  * 
18686  * NEEDS Extra CSS? 
18687  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18688  */
18689  
18690 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18691 {
18692     
18693     Roo.apply(this, config);
18694     
18695     // default disabled, based on 'good practice'..
18696     this.disable = this.disable || {};
18697     Roo.applyIf(this.disable, {
18698         fontSize : true,
18699         colors : true,
18700         specialElements : true
18701     });
18702     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18703     
18704     this.editor = config.editor;
18705     this.editorcore = config.editor.editorcore;
18706     
18707     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18708     
18709     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18710     // dont call parent... till later.
18711 }
18712 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18713      
18714     bar : true,
18715     
18716     editor : false,
18717     editorcore : false,
18718     
18719     
18720     formats : [
18721         "p" ,  
18722         "h1","h2","h3","h4","h5","h6", 
18723         "pre", "code", 
18724         "abbr", "acronym", "address", "cite", "samp", "var",
18725         'div','span'
18726     ],
18727     
18728     onRender : function(ct, position)
18729     {
18730        // Roo.log("Call onRender: " + this.xtype);
18731         
18732        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18733        Roo.log(this.el);
18734        this.el.dom.style.marginBottom = '0';
18735        var _this = this;
18736        var editorcore = this.editorcore;
18737        var editor= this.editor;
18738        
18739        var children = [];
18740        var btn = function(id,cmd , toggle, handler){
18741        
18742             var  event = toggle ? 'toggle' : 'click';
18743        
18744             var a = {
18745                 size : 'sm',
18746                 xtype: 'Button',
18747                 xns: Roo.bootstrap,
18748                 glyphicon : id,
18749                 cmd : id || cmd,
18750                 enableToggle:toggle !== false,
18751                 //html : 'submit'
18752                 pressed : toggle ? false : null,
18753                 listeners : {}
18754             }
18755             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18756                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18757             }
18758             children.push(a);
18759             return a;
18760        }
18761         
18762         var style = {
18763                 xtype: 'Button',
18764                 size : 'sm',
18765                 xns: Roo.bootstrap,
18766                 glyphicon : 'font',
18767                 //html : 'submit'
18768                 menu : {
18769                     xtype: 'Menu',
18770                     xns: Roo.bootstrap,
18771                     items:  []
18772                 }
18773         };
18774         Roo.each(this.formats, function(f) {
18775             style.menu.items.push({
18776                 xtype :'MenuItem',
18777                 xns: Roo.bootstrap,
18778                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18779                 tagname : f,
18780                 listeners : {
18781                     click : function()
18782                     {
18783                         editorcore.insertTag(this.tagname);
18784                         editor.focus();
18785                     }
18786                 }
18787                 
18788             });
18789         });
18790          children.push(style);   
18791             
18792             
18793         btn('bold',false,true);
18794         btn('italic',false,true);
18795         btn('align-left', 'justifyleft',true);
18796         btn('align-center', 'justifycenter',true);
18797         btn('align-right' , 'justifyright',true);
18798         btn('link', false, false, function(btn) {
18799             //Roo.log("create link?");
18800             var url = prompt(this.createLinkText, this.defaultLinkValue);
18801             if(url && url != 'http:/'+'/'){
18802                 this.editorcore.relayCmd('createlink', url);
18803             }
18804         }),
18805         btn('list','insertunorderedlist',true);
18806         btn('pencil', false,true, function(btn){
18807                 Roo.log(this);
18808                 
18809                 this.toggleSourceEdit(btn.pressed);
18810         });
18811         /*
18812         var cog = {
18813                 xtype: 'Button',
18814                 size : 'sm',
18815                 xns: Roo.bootstrap,
18816                 glyphicon : 'cog',
18817                 //html : 'submit'
18818                 menu : {
18819                     xtype: 'Menu',
18820                     xns: Roo.bootstrap,
18821                     items:  []
18822                 }
18823         };
18824         
18825         cog.menu.items.push({
18826             xtype :'MenuItem',
18827             xns: Roo.bootstrap,
18828             html : Clean styles,
18829             tagname : f,
18830             listeners : {
18831                 click : function()
18832                 {
18833                     editorcore.insertTag(this.tagname);
18834                     editor.focus();
18835                 }
18836             }
18837             
18838         });
18839        */
18840         
18841          
18842        this.xtype = 'NavSimplebar';
18843         
18844         for(var i=0;i< children.length;i++) {
18845             
18846             this.buttons.add(this.addxtypeChild(children[i]));
18847             
18848         }
18849         
18850         editor.on('editorevent', this.updateToolbar, this);
18851     },
18852     onBtnClick : function(id)
18853     {
18854        this.editorcore.relayCmd(id);
18855        this.editorcore.focus();
18856     },
18857     
18858     /**
18859      * Protected method that will not generally be called directly. It triggers
18860      * a toolbar update by reading the markup state of the current selection in the editor.
18861      */
18862     updateToolbar: function(){
18863
18864         if(!this.editorcore.activated){
18865             this.editor.onFirstFocus(); // is this neeed?
18866             return;
18867         }
18868
18869         var btns = this.buttons; 
18870         var doc = this.editorcore.doc;
18871         btns.get('bold').setActive(doc.queryCommandState('bold'));
18872         btns.get('italic').setActive(doc.queryCommandState('italic'));
18873         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18874         
18875         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18876         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18877         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18878         
18879         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18880         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18881          /*
18882         
18883         var ans = this.editorcore.getAllAncestors();
18884         if (this.formatCombo) {
18885             
18886             
18887             var store = this.formatCombo.store;
18888             this.formatCombo.setValue("");
18889             for (var i =0; i < ans.length;i++) {
18890                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18891                     // select it..
18892                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18893                     break;
18894                 }
18895             }
18896         }
18897         
18898         
18899         
18900         // hides menus... - so this cant be on a menu...
18901         Roo.bootstrap.MenuMgr.hideAll();
18902         */
18903         Roo.bootstrap.MenuMgr.hideAll();
18904         //this.editorsyncValue();
18905     },
18906     onFirstFocus: function() {
18907         this.buttons.each(function(item){
18908            item.enable();
18909         });
18910     },
18911     toggleSourceEdit : function(sourceEditMode){
18912         
18913           
18914         if(sourceEditMode){
18915             Roo.log("disabling buttons");
18916            this.buttons.each( function(item){
18917                 if(item.cmd != 'pencil'){
18918                     item.disable();
18919                 }
18920             });
18921           
18922         }else{
18923             Roo.log("enabling buttons");
18924             if(this.editorcore.initialized){
18925                 this.buttons.each( function(item){
18926                     item.enable();
18927                 });
18928             }
18929             
18930         }
18931         Roo.log("calling toggole on editor");
18932         // tell the editor that it's been pressed..
18933         this.editor.toggleSourceEdit(sourceEditMode);
18934        
18935     }
18936 });
18937
18938
18939
18940
18941
18942 /**
18943  * @class Roo.bootstrap.Table.AbstractSelectionModel
18944  * @extends Roo.util.Observable
18945  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18946  * implemented by descendant classes.  This class should not be directly instantiated.
18947  * @constructor
18948  */
18949 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18950     this.locked = false;
18951     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18952 };
18953
18954
18955 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18956     /** @ignore Called by the grid automatically. Do not call directly. */
18957     init : function(grid){
18958         this.grid = grid;
18959         this.initEvents();
18960     },
18961
18962     /**
18963      * Locks the selections.
18964      */
18965     lock : function(){
18966         this.locked = true;
18967     },
18968
18969     /**
18970      * Unlocks the selections.
18971      */
18972     unlock : function(){
18973         this.locked = false;
18974     },
18975
18976     /**
18977      * Returns true if the selections are locked.
18978      * @return {Boolean}
18979      */
18980     isLocked : function(){
18981         return this.locked;
18982     }
18983 });
18984 /**
18985  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18986  * @class Roo.bootstrap.Table.RowSelectionModel
18987  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18988  * It supports multiple selections and keyboard selection/navigation. 
18989  * @constructor
18990  * @param {Object} config
18991  */
18992
18993 Roo.bootstrap.Table.RowSelectionModel = function(config){
18994     Roo.apply(this, config);
18995     this.selections = new Roo.util.MixedCollection(false, function(o){
18996         return o.id;
18997     });
18998
18999     this.last = false;
19000     this.lastActive = false;
19001
19002     this.addEvents({
19003         /**
19004              * @event selectionchange
19005              * Fires when the selection changes
19006              * @param {SelectionModel} this
19007              */
19008             "selectionchange" : true,
19009         /**
19010              * @event afterselectionchange
19011              * Fires after the selection changes (eg. by key press or clicking)
19012              * @param {SelectionModel} this
19013              */
19014             "afterselectionchange" : true,
19015         /**
19016              * @event beforerowselect
19017              * Fires when a row is selected being selected, return false to cancel.
19018              * @param {SelectionModel} this
19019              * @param {Number} rowIndex The selected index
19020              * @param {Boolean} keepExisting False if other selections will be cleared
19021              */
19022             "beforerowselect" : true,
19023         /**
19024              * @event rowselect
19025              * Fires when a row is selected.
19026              * @param {SelectionModel} this
19027              * @param {Number} rowIndex The selected index
19028              * @param {Roo.data.Record} r The record
19029              */
19030             "rowselect" : true,
19031         /**
19032              * @event rowdeselect
19033              * Fires when a row is deselected.
19034              * @param {SelectionModel} this
19035              * @param {Number} rowIndex The selected index
19036              */
19037         "rowdeselect" : true
19038     });
19039     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
19040     this.locked = false;
19041 };
19042
19043 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
19044     /**
19045      * @cfg {Boolean} singleSelect
19046      * True to allow selection of only one row at a time (defaults to false)
19047      */
19048     singleSelect : false,
19049
19050     // private
19051     initEvents : function(){
19052
19053         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
19054             this.grid.on("mousedown", this.handleMouseDown, this);
19055         }else{ // allow click to work like normal
19056             this.grid.on("rowclick", this.handleDragableRowClick, this);
19057         }
19058
19059         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
19060             "up" : function(e){
19061                 if(!e.shiftKey){
19062                     this.selectPrevious(e.shiftKey);
19063                 }else if(this.last !== false && this.lastActive !== false){
19064                     var last = this.last;
19065                     this.selectRange(this.last,  this.lastActive-1);
19066                     this.grid.getView().focusRow(this.lastActive);
19067                     if(last !== false){
19068                         this.last = last;
19069                     }
19070                 }else{
19071                     this.selectFirstRow();
19072                 }
19073                 this.fireEvent("afterselectionchange", this);
19074             },
19075             "down" : function(e){
19076                 if(!e.shiftKey){
19077                     this.selectNext(e.shiftKey);
19078                 }else if(this.last !== false && this.lastActive !== false){
19079                     var last = this.last;
19080                     this.selectRange(this.last,  this.lastActive+1);
19081                     this.grid.getView().focusRow(this.lastActive);
19082                     if(last !== false){
19083                         this.last = last;
19084                     }
19085                 }else{
19086                     this.selectFirstRow();
19087                 }
19088                 this.fireEvent("afterselectionchange", this);
19089             },
19090             scope: this
19091         });
19092
19093         var view = this.grid.view;
19094         view.on("refresh", this.onRefresh, this);
19095         view.on("rowupdated", this.onRowUpdated, this);
19096         view.on("rowremoved", this.onRemove, this);
19097     },
19098
19099     // private
19100     onRefresh : function(){
19101         var ds = this.grid.dataSource, i, v = this.grid.view;
19102         var s = this.selections;
19103         s.each(function(r){
19104             if((i = ds.indexOfId(r.id)) != -1){
19105                 v.onRowSelect(i);
19106             }else{
19107                 s.remove(r);
19108             }
19109         });
19110     },
19111
19112     // private
19113     onRemove : function(v, index, r){
19114         this.selections.remove(r);
19115     },
19116
19117     // private
19118     onRowUpdated : function(v, index, r){
19119         if(this.isSelected(r)){
19120             v.onRowSelect(index);
19121         }
19122     },
19123
19124     /**
19125      * Select records.
19126      * @param {Array} records The records to select
19127      * @param {Boolean} keepExisting (optional) True to keep existing selections
19128      */
19129     selectRecords : function(records, keepExisting){
19130         if(!keepExisting){
19131             this.clearSelections();
19132         }
19133         var ds = this.grid.dataSource;
19134         for(var i = 0, len = records.length; i < len; i++){
19135             this.selectRow(ds.indexOf(records[i]), true);
19136         }
19137     },
19138
19139     /**
19140      * Gets the number of selected rows.
19141      * @return {Number}
19142      */
19143     getCount : function(){
19144         return this.selections.length;
19145     },
19146
19147     /**
19148      * Selects the first row in the grid.
19149      */
19150     selectFirstRow : function(){
19151         this.selectRow(0);
19152     },
19153
19154     /**
19155      * Select the last row.
19156      * @param {Boolean} keepExisting (optional) True to keep existing selections
19157      */
19158     selectLastRow : function(keepExisting){
19159         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
19160     },
19161
19162     /**
19163      * Selects the row immediately following the last selected row.
19164      * @param {Boolean} keepExisting (optional) True to keep existing selections
19165      */
19166     selectNext : function(keepExisting){
19167         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
19168             this.selectRow(this.last+1, keepExisting);
19169             this.grid.getView().focusRow(this.last);
19170         }
19171     },
19172
19173     /**
19174      * Selects the row that precedes the last selected row.
19175      * @param {Boolean} keepExisting (optional) True to keep existing selections
19176      */
19177     selectPrevious : function(keepExisting){
19178         if(this.last){
19179             this.selectRow(this.last-1, keepExisting);
19180             this.grid.getView().focusRow(this.last);
19181         }
19182     },
19183
19184     /**
19185      * Returns the selected records
19186      * @return {Array} Array of selected records
19187      */
19188     getSelections : function(){
19189         return [].concat(this.selections.items);
19190     },
19191
19192     /**
19193      * Returns the first selected record.
19194      * @return {Record}
19195      */
19196     getSelected : function(){
19197         return this.selections.itemAt(0);
19198     },
19199
19200
19201     /**
19202      * Clears all selections.
19203      */
19204     clearSelections : function(fast){
19205         if(this.locked) return;
19206         if(fast !== true){
19207             var ds = this.grid.dataSource;
19208             var s = this.selections;
19209             s.each(function(r){
19210                 this.deselectRow(ds.indexOfId(r.id));
19211             }, this);
19212             s.clear();
19213         }else{
19214             this.selections.clear();
19215         }
19216         this.last = false;
19217     },
19218
19219
19220     /**
19221      * Selects all rows.
19222      */
19223     selectAll : function(){
19224         if(this.locked) return;
19225         this.selections.clear();
19226         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
19227             this.selectRow(i, true);
19228         }
19229     },
19230
19231     /**
19232      * Returns True if there is a selection.
19233      * @return {Boolean}
19234      */
19235     hasSelection : function(){
19236         return this.selections.length > 0;
19237     },
19238
19239     /**
19240      * Returns True if the specified row is selected.
19241      * @param {Number/Record} record The record or index of the record to check
19242      * @return {Boolean}
19243      */
19244     isSelected : function(index){
19245         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
19246         return (r && this.selections.key(r.id) ? true : false);
19247     },
19248
19249     /**
19250      * Returns True if the specified record id is selected.
19251      * @param {String} id The id of record to check
19252      * @return {Boolean}
19253      */
19254     isIdSelected : function(id){
19255         return (this.selections.key(id) ? true : false);
19256     },
19257
19258     // private
19259     handleMouseDown : function(e, t){
19260         var view = this.grid.getView(), rowIndex;
19261         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
19262             return;
19263         };
19264         if(e.shiftKey && this.last !== false){
19265             var last = this.last;
19266             this.selectRange(last, rowIndex, e.ctrlKey);
19267             this.last = last; // reset the last
19268             view.focusRow(rowIndex);
19269         }else{
19270             var isSelected = this.isSelected(rowIndex);
19271             if(e.button !== 0 && isSelected){
19272                 view.focusRow(rowIndex);
19273             }else if(e.ctrlKey && isSelected){
19274                 this.deselectRow(rowIndex);
19275             }else if(!isSelected){
19276                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
19277                 view.focusRow(rowIndex);
19278             }
19279         }
19280         this.fireEvent("afterselectionchange", this);
19281     },
19282     // private
19283     handleDragableRowClick :  function(grid, rowIndex, e) 
19284     {
19285         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
19286             this.selectRow(rowIndex, false);
19287             grid.view.focusRow(rowIndex);
19288              this.fireEvent("afterselectionchange", this);
19289         }
19290     },
19291     
19292     /**
19293      * Selects multiple rows.
19294      * @param {Array} rows Array of the indexes of the row to select
19295      * @param {Boolean} keepExisting (optional) True to keep existing selections
19296      */
19297     selectRows : function(rows, keepExisting){
19298         if(!keepExisting){
19299             this.clearSelections();
19300         }
19301         for(var i = 0, len = rows.length; i < len; i++){
19302             this.selectRow(rows[i], true);
19303         }
19304     },
19305
19306     /**
19307      * Selects a range of rows. All rows in between startRow and endRow are also selected.
19308      * @param {Number} startRow The index of the first row in the range
19309      * @param {Number} endRow The index of the last row in the range
19310      * @param {Boolean} keepExisting (optional) True to retain existing selections
19311      */
19312     selectRange : function(startRow, endRow, keepExisting){
19313         if(this.locked) return;
19314         if(!keepExisting){
19315             this.clearSelections();
19316         }
19317         if(startRow <= endRow){
19318             for(var i = startRow; i <= endRow; i++){
19319                 this.selectRow(i, true);
19320             }
19321         }else{
19322             for(var i = startRow; i >= endRow; i--){
19323                 this.selectRow(i, true);
19324             }
19325         }
19326     },
19327
19328     /**
19329      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19330      * @param {Number} startRow The index of the first row in the range
19331      * @param {Number} endRow The index of the last row in the range
19332      */
19333     deselectRange : function(startRow, endRow, preventViewNotify){
19334         if(this.locked) return;
19335         for(var i = startRow; i <= endRow; i++){
19336             this.deselectRow(i, preventViewNotify);
19337         }
19338     },
19339
19340     /**
19341      * Selects a row.
19342      * @param {Number} row The index of the row to select
19343      * @param {Boolean} keepExisting (optional) True to keep existing selections
19344      */
19345     selectRow : function(index, keepExisting, preventViewNotify){
19346         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19347         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19348             if(!keepExisting || this.singleSelect){
19349                 this.clearSelections();
19350             }
19351             var r = this.grid.dataSource.getAt(index);
19352             this.selections.add(r);
19353             this.last = this.lastActive = index;
19354             if(!preventViewNotify){
19355                 this.grid.getView().onRowSelect(index);
19356             }
19357             this.fireEvent("rowselect", this, index, r);
19358             this.fireEvent("selectionchange", this);
19359         }
19360     },
19361
19362     /**
19363      * Deselects a row.
19364      * @param {Number} row The index of the row to deselect
19365      */
19366     deselectRow : function(index, preventViewNotify){
19367         if(this.locked) return;
19368         if(this.last == index){
19369             this.last = false;
19370         }
19371         if(this.lastActive == index){
19372             this.lastActive = false;
19373         }
19374         var r = this.grid.dataSource.getAt(index);
19375         this.selections.remove(r);
19376         if(!preventViewNotify){
19377             this.grid.getView().onRowDeselect(index);
19378         }
19379         this.fireEvent("rowdeselect", this, index);
19380         this.fireEvent("selectionchange", this);
19381     },
19382
19383     // private
19384     restoreLast : function(){
19385         if(this._last){
19386             this.last = this._last;
19387         }
19388     },
19389
19390     // private
19391     acceptsNav : function(row, col, cm){
19392         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19393     },
19394
19395     // private
19396     onEditorKey : function(field, e){
19397         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19398         if(k == e.TAB){
19399             e.stopEvent();
19400             ed.completeEdit();
19401             if(e.shiftKey){
19402                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19403             }else{
19404                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19405             }
19406         }else if(k == e.ENTER && !e.ctrlKey){
19407             e.stopEvent();
19408             ed.completeEdit();
19409             if(e.shiftKey){
19410                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19411             }else{
19412                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19413             }
19414         }else if(k == e.ESC){
19415             ed.cancelEdit();
19416         }
19417         if(newCell){
19418             g.startEditing(newCell[0], newCell[1]);
19419         }
19420     }
19421 });/*
19422  * Based on:
19423  * Ext JS Library 1.1.1
19424  * Copyright(c) 2006-2007, Ext JS, LLC.
19425  *
19426  * Originally Released Under LGPL - original licence link has changed is not relivant.
19427  *
19428  * Fork - LGPL
19429  * <script type="text/javascript">
19430  */
19431  
19432 /**
19433  * @class Roo.bootstrap.PagingToolbar
19434  * @extends Roo.Row
19435  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19436  * @constructor
19437  * Create a new PagingToolbar
19438  * @param {Object} config The config object
19439  */
19440 Roo.bootstrap.PagingToolbar = function(config)
19441 {
19442     // old args format still supported... - xtype is prefered..
19443         // created from xtype...
19444     var ds = config.dataSource;
19445     this.toolbarItems = [];
19446     if (config.items) {
19447         this.toolbarItems = config.items;
19448 //        config.items = [];
19449     }
19450     
19451     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19452     this.ds = ds;
19453     this.cursor = 0;
19454     if (ds) { 
19455         this.bind(ds);
19456     }
19457     
19458     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19459     
19460 };
19461
19462 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19463     /**
19464      * @cfg {Roo.data.Store} dataSource
19465      * The underlying data store providing the paged data
19466      */
19467     /**
19468      * @cfg {String/HTMLElement/Element} container
19469      * container The id or element that will contain the toolbar
19470      */
19471     /**
19472      * @cfg {Boolean} displayInfo
19473      * True to display the displayMsg (defaults to false)
19474      */
19475     /**
19476      * @cfg {Number} pageSize
19477      * The number of records to display per page (defaults to 20)
19478      */
19479     pageSize: 20,
19480     /**
19481      * @cfg {String} displayMsg
19482      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19483      */
19484     displayMsg : 'Displaying {0} - {1} of {2}',
19485     /**
19486      * @cfg {String} emptyMsg
19487      * The message to display when no records are found (defaults to "No data to display")
19488      */
19489     emptyMsg : 'No data to display',
19490     /**
19491      * Customizable piece of the default paging text (defaults to "Page")
19492      * @type String
19493      */
19494     beforePageText : "Page",
19495     /**
19496      * Customizable piece of the default paging text (defaults to "of %0")
19497      * @type String
19498      */
19499     afterPageText : "of {0}",
19500     /**
19501      * Customizable piece of the default paging text (defaults to "First Page")
19502      * @type String
19503      */
19504     firstText : "First Page",
19505     /**
19506      * Customizable piece of the default paging text (defaults to "Previous Page")
19507      * @type String
19508      */
19509     prevText : "Previous Page",
19510     /**
19511      * Customizable piece of the default paging text (defaults to "Next Page")
19512      * @type String
19513      */
19514     nextText : "Next Page",
19515     /**
19516      * Customizable piece of the default paging text (defaults to "Last Page")
19517      * @type String
19518      */
19519     lastText : "Last Page",
19520     /**
19521      * Customizable piece of the default paging text (defaults to "Refresh")
19522      * @type String
19523      */
19524     refreshText : "Refresh",
19525
19526     buttons : false,
19527     // private
19528     onRender : function(ct, position) 
19529     {
19530         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19531         this.navgroup.parentId = this.id;
19532         this.navgroup.onRender(this.el, null);
19533         // add the buttons to the navgroup
19534         
19535         if(this.displayInfo){
19536             Roo.log(this.el.select('ul.navbar-nav',true).first());
19537             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19538             this.displayEl = this.el.select('.x-paging-info', true).first();
19539 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19540 //            this.displayEl = navel.el.select('span',true).first();
19541         }
19542         
19543         var _this = this;
19544         
19545         if(this.buttons){
19546             Roo.each(_this.buttons, function(e){
19547                Roo.factory(e).onRender(_this.el, null);
19548             });
19549         }
19550             
19551         Roo.each(_this.toolbarItems, function(e) {
19552             _this.navgroup.addItem(e);
19553         });
19554         
19555         this.first = this.navgroup.addItem({
19556             tooltip: this.firstText,
19557             cls: "prev",
19558             icon : 'fa fa-backward',
19559             disabled: true,
19560             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19561         });
19562         
19563         this.prev =  this.navgroup.addItem({
19564             tooltip: this.prevText,
19565             cls: "prev",
19566             icon : 'fa fa-step-backward',
19567             disabled: true,
19568             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19569         });
19570     //this.addSeparator();
19571         
19572         
19573         var field = this.navgroup.addItem( {
19574             tagtype : 'span',
19575             cls : 'x-paging-position',
19576             
19577             html : this.beforePageText  +
19578                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19579                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19580          } ); //?? escaped?
19581         
19582         this.field = field.el.select('input', true).first();
19583         this.field.on("keydown", this.onPagingKeydown, this);
19584         this.field.on("focus", function(){this.dom.select();});
19585     
19586     
19587         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19588         //this.field.setHeight(18);
19589         //this.addSeparator();
19590         this.next = this.navgroup.addItem({
19591             tooltip: this.nextText,
19592             cls: "next",
19593             html : ' <i class="fa fa-step-forward">',
19594             disabled: true,
19595             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19596         });
19597         this.last = this.navgroup.addItem({
19598             tooltip: this.lastText,
19599             icon : 'fa fa-forward',
19600             cls: "next",
19601             disabled: true,
19602             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19603         });
19604     //this.addSeparator();
19605         this.loading = this.navgroup.addItem({
19606             tooltip: this.refreshText,
19607             icon: 'fa fa-refresh',
19608             
19609             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19610         });
19611
19612     },
19613
19614     // private
19615     updateInfo : function(){
19616         if(this.displayEl){
19617             var count = this.ds.getCount();
19618             var msg = count == 0 ?
19619                 this.emptyMsg :
19620                 String.format(
19621                     this.displayMsg,
19622                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19623                 );
19624             this.displayEl.update(msg);
19625         }
19626     },
19627
19628     // private
19629     onLoad : function(ds, r, o){
19630        this.cursor = o.params ? o.params.start : 0;
19631        var d = this.getPageData(),
19632             ap = d.activePage,
19633             ps = d.pages;
19634         
19635        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19636        this.field.dom.value = ap;
19637        this.first.setDisabled(ap == 1);
19638        this.prev.setDisabled(ap == 1);
19639        this.next.setDisabled(ap == ps);
19640        this.last.setDisabled(ap == ps);
19641        this.loading.enable();
19642        this.updateInfo();
19643     },
19644
19645     // private
19646     getPageData : function(){
19647         var total = this.ds.getTotalCount();
19648         return {
19649             total : total,
19650             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19651             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19652         };
19653     },
19654
19655     // private
19656     onLoadError : function(){
19657         this.loading.enable();
19658     },
19659
19660     // private
19661     onPagingKeydown : function(e){
19662         var k = e.getKey();
19663         var d = this.getPageData();
19664         if(k == e.RETURN){
19665             var v = this.field.dom.value, pageNum;
19666             if(!v || isNaN(pageNum = parseInt(v, 10))){
19667                 this.field.dom.value = d.activePage;
19668                 return;
19669             }
19670             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19671             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19672             e.stopEvent();
19673         }
19674         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))
19675         {
19676           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19677           this.field.dom.value = pageNum;
19678           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19679           e.stopEvent();
19680         }
19681         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19682         {
19683           var v = this.field.dom.value, pageNum; 
19684           var increment = (e.shiftKey) ? 10 : 1;
19685           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19686             increment *= -1;
19687           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19688             this.field.dom.value = d.activePage;
19689             return;
19690           }
19691           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19692           {
19693             this.field.dom.value = parseInt(v, 10) + increment;
19694             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19695             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19696           }
19697           e.stopEvent();
19698         }
19699     },
19700
19701     // private
19702     beforeLoad : function(){
19703         if(this.loading){
19704             this.loading.disable();
19705         }
19706     },
19707
19708     // private
19709     onClick : function(which){
19710         var ds = this.ds;
19711         if (!ds) {
19712             return;
19713         }
19714         switch(which){
19715             case "first":
19716                 ds.load({params:{start: 0, limit: this.pageSize}});
19717             break;
19718             case "prev":
19719                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19720             break;
19721             case "next":
19722                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19723             break;
19724             case "last":
19725                 var total = ds.getTotalCount();
19726                 var extra = total % this.pageSize;
19727                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19728                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19729             break;
19730             case "refresh":
19731                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19732             break;
19733         }
19734     },
19735
19736     /**
19737      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19738      * @param {Roo.data.Store} store The data store to unbind
19739      */
19740     unbind : function(ds){
19741         ds.un("beforeload", this.beforeLoad, this);
19742         ds.un("load", this.onLoad, this);
19743         ds.un("loadexception", this.onLoadError, this);
19744         ds.un("remove", this.updateInfo, this);
19745         ds.un("add", this.updateInfo, this);
19746         this.ds = undefined;
19747     },
19748
19749     /**
19750      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19751      * @param {Roo.data.Store} store The data store to bind
19752      */
19753     bind : function(ds){
19754         ds.on("beforeload", this.beforeLoad, this);
19755         ds.on("load", this.onLoad, this);
19756         ds.on("loadexception", this.onLoadError, this);
19757         ds.on("remove", this.updateInfo, this);
19758         ds.on("add", this.updateInfo, this);
19759         this.ds = ds;
19760     }
19761 });/*
19762  * - LGPL
19763  *
19764  * element
19765  * 
19766  */
19767
19768 /**
19769  * @class Roo.bootstrap.MessageBar
19770  * @extends Roo.bootstrap.Component
19771  * Bootstrap MessageBar class
19772  * @cfg {String} html contents of the MessageBar
19773  * @cfg {String} weight (info | success | warning | danger) default info
19774  * @cfg {String} beforeClass insert the bar before the given class
19775  * @cfg {Boolean} closable (true | false) default false
19776  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19777  * 
19778  * @constructor
19779  * Create a new Element
19780  * @param {Object} config The config object
19781  */
19782
19783 Roo.bootstrap.MessageBar = function(config){
19784     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19785 };
19786
19787 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19788     
19789     html: '',
19790     weight: 'info',
19791     closable: false,
19792     fixed: false,
19793     beforeClass: 'bootstrap-sticky-wrap',
19794     
19795     getAutoCreate : function(){
19796         
19797         var cfg = {
19798             tag: 'div',
19799             cls: 'alert alert-dismissable alert-' + this.weight,
19800             cn: [
19801                 {
19802                     tag: 'span',
19803                     cls: 'message',
19804                     html: this.html || ''
19805                 }
19806             ]
19807         }
19808         
19809         if(this.fixed){
19810             cfg.cls += ' alert-messages-fixed';
19811         }
19812         
19813         if(this.closable){
19814             cfg.cn.push({
19815                 tag: 'button',
19816                 cls: 'close',
19817                 html: 'x'
19818             });
19819         }
19820         
19821         return cfg;
19822     },
19823     
19824     onRender : function(ct, position)
19825     {
19826         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19827         
19828         if(!this.el){
19829             var cfg = Roo.apply({},  this.getAutoCreate());
19830             cfg.id = Roo.id();
19831             
19832             if (this.cls) {
19833                 cfg.cls += ' ' + this.cls;
19834             }
19835             if (this.style) {
19836                 cfg.style = this.style;
19837             }
19838             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19839             
19840             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19841         }
19842         
19843         this.el.select('>button.close').on('click', this.hide, this);
19844         
19845     },
19846     
19847     show : function()
19848     {
19849         if (!this.rendered) {
19850             this.render();
19851         }
19852         
19853         this.el.show();
19854         
19855         this.fireEvent('show', this);
19856         
19857     },
19858     
19859     hide : function()
19860     {
19861         if (!this.rendered) {
19862             this.render();
19863         }
19864         
19865         this.el.hide();
19866         
19867         this.fireEvent('hide', this);
19868     },
19869     
19870     update : function()
19871     {
19872 //        var e = this.el.dom.firstChild;
19873 //        
19874 //        if(this.closable){
19875 //            e = e.nextSibling;
19876 //        }
19877 //        
19878 //        e.data = this.html || '';
19879
19880         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19881     }
19882    
19883 });
19884
19885  
19886
19887      /*
19888  * - LGPL
19889  *
19890  * Graph
19891  * 
19892  */
19893
19894
19895 /**
19896  * @class Roo.bootstrap.Graph
19897  * @extends Roo.bootstrap.Component
19898  * Bootstrap Graph class
19899 > Prameters
19900  -sm {number} sm 4
19901  -md {number} md 5
19902  @cfg {String} graphtype  bar | vbar | pie
19903  @cfg {number} g_x coodinator | centre x (pie)
19904  @cfg {number} g_y coodinator | centre y (pie)
19905  @cfg {number} g_r radius (pie)
19906  @cfg {number} g_height height of the chart (respected by all elements in the set)
19907  @cfg {number} g_width width of the chart (respected by all elements in the set)
19908  @cfg {Object} title The title of the chart
19909     
19910  -{Array}  values
19911  -opts (object) options for the chart 
19912      o {
19913      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19914      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19915      o vgutter (number)
19916      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.
19917      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19918      o to
19919      o stretch (boolean)
19920      o }
19921  -opts (object) options for the pie
19922      o{
19923      o cut
19924      o startAngle (number)
19925      o endAngle (number)
19926      } 
19927  *
19928  * @constructor
19929  * Create a new Input
19930  * @param {Object} config The config object
19931  */
19932
19933 Roo.bootstrap.Graph = function(config){
19934     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19935     
19936     this.addEvents({
19937         // img events
19938         /**
19939          * @event click
19940          * The img click event for the img.
19941          * @param {Roo.EventObject} e
19942          */
19943         "click" : true
19944     });
19945 };
19946
19947 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19948     
19949     sm: 4,
19950     md: 5,
19951     graphtype: 'bar',
19952     g_height: 250,
19953     g_width: 400,
19954     g_x: 50,
19955     g_y: 50,
19956     g_r: 30,
19957     opts:{
19958         //g_colors: this.colors,
19959         g_type: 'soft',
19960         g_gutter: '20%'
19961
19962     },
19963     title : false,
19964
19965     getAutoCreate : function(){
19966         
19967         var cfg = {
19968             tag: 'div',
19969             html : null
19970         }
19971         
19972         
19973         return  cfg;
19974     },
19975
19976     onRender : function(ct,position){
19977         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19978         this.raphael = Raphael(this.el.dom);
19979         
19980                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19981                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19982                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19983                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19984                 /*
19985                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19986                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19987                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19988                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19989                 
19990                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19991                 r.barchart(330, 10, 300, 220, data1);
19992                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19993                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19994                 */
19995                 
19996                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19997                 // r.barchart(30, 30, 560, 250,  xdata, {
19998                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19999                 //     axis : "0 0 1 1",
20000                 //     axisxlabels :  xdata
20001                 //     //yvalues : cols,
20002                    
20003                 // });
20004 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
20005 //        
20006 //        this.load(null,xdata,{
20007 //                axis : "0 0 1 1",
20008 //                axisxlabels :  xdata
20009 //                });
20010
20011     },
20012
20013     load : function(graphtype,xdata,opts){
20014         this.raphael.clear();
20015         if(!graphtype) {
20016             graphtype = this.graphtype;
20017         }
20018         if(!opts){
20019             opts = this.opts;
20020         }
20021         var r = this.raphael,
20022             fin = function () {
20023                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
20024             },
20025             fout = function () {
20026                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
20027             },
20028             pfin = function() {
20029                 this.sector.stop();
20030                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
20031
20032                 if (this.label) {
20033                     this.label[0].stop();
20034                     this.label[0].attr({ r: 7.5 });
20035                     this.label[1].attr({ "font-weight": 800 });
20036                 }
20037             },
20038             pfout = function() {
20039                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
20040
20041                 if (this.label) {
20042                     this.label[0].animate({ r: 5 }, 500, "bounce");
20043                     this.label[1].attr({ "font-weight": 400 });
20044                 }
20045             };
20046
20047         switch(graphtype){
20048             case 'bar':
20049                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
20050                 break;
20051             case 'hbar':
20052                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
20053                 break;
20054             case 'pie':
20055 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
20056 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
20057 //            
20058                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
20059                 
20060                 break;
20061
20062         }
20063         
20064         if(this.title){
20065             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
20066         }
20067         
20068     },
20069     
20070     setTitle: function(o)
20071     {
20072         this.title = o;
20073     },
20074     
20075     initEvents: function() {
20076         
20077         if(!this.href){
20078             this.el.on('click', this.onClick, this);
20079         }
20080     },
20081     
20082     onClick : function(e)
20083     {
20084         Roo.log('img onclick');
20085         this.fireEvent('click', this, e);
20086     }
20087    
20088 });
20089
20090  
20091 /*
20092  * - LGPL
20093  *
20094  * numberBox
20095  * 
20096  */
20097 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20098
20099 /**
20100  * @class Roo.bootstrap.dash.NumberBox
20101  * @extends Roo.bootstrap.Component
20102  * Bootstrap NumberBox class
20103  * @cfg {String} headline Box headline
20104  * @cfg {String} content Box content
20105  * @cfg {String} icon Box icon
20106  * @cfg {String} footer Footer text
20107  * @cfg {String} fhref Footer href
20108  * 
20109  * @constructor
20110  * Create a new NumberBox
20111  * @param {Object} config The config object
20112  */
20113
20114
20115 Roo.bootstrap.dash.NumberBox = function(config){
20116     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
20117     
20118 };
20119
20120 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
20121     
20122     headline : '',
20123     content : '',
20124     icon : '',
20125     footer : '',
20126     fhref : '',
20127     ficon : '',
20128     
20129     getAutoCreate : function(){
20130         
20131         var cfg = {
20132             tag : 'div',
20133             cls : 'small-box ',
20134             cn : [
20135                 {
20136                     tag : 'div',
20137                     cls : 'inner',
20138                     cn :[
20139                         {
20140                             tag : 'h3',
20141                             cls : 'roo-headline',
20142                             html : this.headline
20143                         },
20144                         {
20145                             tag : 'p',
20146                             cls : 'roo-content',
20147                             html : this.content
20148                         }
20149                     ]
20150                 }
20151             ]
20152         }
20153         
20154         if(this.icon){
20155             cfg.cn.push({
20156                 tag : 'div',
20157                 cls : 'icon',
20158                 cn :[
20159                     {
20160                         tag : 'i',
20161                         cls : 'ion ' + this.icon
20162                     }
20163                 ]
20164             });
20165         }
20166         
20167         if(this.footer){
20168             var footer = {
20169                 tag : 'a',
20170                 cls : 'small-box-footer',
20171                 href : this.fhref || '#',
20172                 html : this.footer
20173             };
20174             
20175             cfg.cn.push(footer);
20176             
20177         }
20178         
20179         return  cfg;
20180     },
20181
20182     onRender : function(ct,position){
20183         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
20184
20185
20186        
20187                 
20188     },
20189
20190     setHeadline: function (value)
20191     {
20192         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
20193     },
20194     
20195     setFooter: function (value, href)
20196     {
20197         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
20198         
20199         if(href){
20200             this.el.select('a.small-box-footer',true).first().attr('href', href);
20201         }
20202         
20203     },
20204
20205     setContent: function (value)
20206     {
20207         this.el.select('.roo-content',true).first().dom.innerHTML = value;
20208     },
20209
20210     initEvents: function() 
20211     {   
20212         
20213     }
20214     
20215 });
20216
20217  
20218 /*
20219  * - LGPL
20220  *
20221  * TabBox
20222  * 
20223  */
20224 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20225
20226 /**
20227  * @class Roo.bootstrap.dash.TabBox
20228  * @extends Roo.bootstrap.Component
20229  * Bootstrap TabBox class
20230  * @cfg {String} title Title of the TabBox
20231  * @cfg {String} icon Icon of the TabBox
20232  * @cfg {Boolean} showtabs (true|false) show the tabs default true
20233  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
20234  * 
20235  * @constructor
20236  * Create a new TabBox
20237  * @param {Object} config The config object
20238  */
20239
20240
20241 Roo.bootstrap.dash.TabBox = function(config){
20242     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
20243     this.addEvents({
20244         // raw events
20245         /**
20246          * @event addpane
20247          * When a pane is added
20248          * @param {Roo.bootstrap.dash.TabPane} pane
20249          */
20250         "addpane" : true,
20251         /**
20252          * @event activatepane
20253          * When a pane is activated
20254          * @param {Roo.bootstrap.dash.TabPane} pane
20255          */
20256         "activatepane" : true
20257         
20258          
20259     });
20260     
20261     this.panes = [];
20262 };
20263
20264 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
20265
20266     title : '',
20267     icon : false,
20268     showtabs : true,
20269     tabScrollable : false,
20270     
20271     getChildContainer : function()
20272     {
20273         return this.el.select('.tab-content', true).first();
20274     },
20275     
20276     getAutoCreate : function(){
20277         
20278         var header = {
20279             tag: 'li',
20280             cls: 'pull-left header',
20281             html: this.title,
20282             cn : []
20283         };
20284         
20285         if(this.icon){
20286             header.cn.push({
20287                 tag: 'i',
20288                 cls: 'fa ' + this.icon
20289             });
20290         }
20291         
20292         var h = {
20293             tag: 'ul',
20294             cls: 'nav nav-tabs pull-right',
20295             cn: [
20296                 header
20297             ]
20298         };
20299         
20300         if(this.tabScrollable){
20301             h = {
20302                 tag: 'div',
20303                 cls: 'tab-header',
20304                 cn: [
20305                     {
20306                         tag: 'ul',
20307                         cls: 'nav nav-tabs pull-right',
20308                         cn: [
20309                             header
20310                         ]
20311                     }
20312                 ]
20313             }
20314         }
20315         
20316         var cfg = {
20317             tag: 'div',
20318             cls: 'nav-tabs-custom',
20319             cn: [
20320                 h,
20321                 {
20322                     tag: 'div',
20323                     cls: 'tab-content no-padding',
20324                     cn: []
20325                 }
20326             ]
20327         }
20328
20329         return  cfg;
20330     },
20331     initEvents : function()
20332     {
20333         //Roo.log('add add pane handler');
20334         this.on('addpane', this.onAddPane, this);
20335     },
20336      /**
20337      * Updates the box title
20338      * @param {String} html to set the title to.
20339      */
20340     setTitle : function(value)
20341     {
20342         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20343     },
20344     onAddPane : function(pane)
20345     {
20346         this.panes.push(pane);
20347         //Roo.log('addpane');
20348         //Roo.log(pane);
20349         // tabs are rendere left to right..
20350         if(!this.showtabs){
20351             return;
20352         }
20353         
20354         var ctr = this.el.select('.nav-tabs', true).first();
20355          
20356          
20357         var existing = ctr.select('.nav-tab',true);
20358         var qty = existing.getCount();;
20359         
20360         
20361         var tab = ctr.createChild({
20362             tag : 'li',
20363             cls : 'nav-tab' + (qty ? '' : ' active'),
20364             cn : [
20365                 {
20366                     tag : 'a',
20367                     href:'#',
20368                     html : pane.title
20369                 }
20370             ]
20371         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20372         pane.tab = tab;
20373         
20374         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20375         if (!qty) {
20376             pane.el.addClass('active');
20377         }
20378         
20379                 
20380     },
20381     onTabClick : function(ev,un,ob,pane)
20382     {
20383         //Roo.log('tab - prev default');
20384         ev.preventDefault();
20385         
20386         
20387         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20388         pane.tab.addClass('active');
20389         //Roo.log(pane.title);
20390         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20391         // technically we should have a deactivate event.. but maybe add later.
20392         // and it should not de-activate the selected tab...
20393         this.fireEvent('activatepane', pane);
20394         pane.el.addClass('active');
20395         pane.fireEvent('activate');
20396         
20397         
20398     },
20399     
20400     getActivePane : function()
20401     {
20402         var r = false;
20403         Roo.each(this.panes, function(p) {
20404             if(p.el.hasClass('active')){
20405                 r = p;
20406                 return false;
20407             }
20408             
20409             return;
20410         });
20411         
20412         return r;
20413     }
20414     
20415     
20416 });
20417
20418  
20419 /*
20420  * - LGPL
20421  *
20422  * Tab pane
20423  * 
20424  */
20425 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20426 /**
20427  * @class Roo.bootstrap.TabPane
20428  * @extends Roo.bootstrap.Component
20429  * Bootstrap TabPane class
20430  * @cfg {Boolean} active (false | true) Default false
20431  * @cfg {String} title title of panel
20432
20433  * 
20434  * @constructor
20435  * Create a new TabPane
20436  * @param {Object} config The config object
20437  */
20438
20439 Roo.bootstrap.dash.TabPane = function(config){
20440     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20441     
20442     this.addEvents({
20443         // raw events
20444         /**
20445          * @event activate
20446          * When a pane is activated
20447          * @param {Roo.bootstrap.dash.TabPane} pane
20448          */
20449         "activate" : true
20450          
20451     });
20452 };
20453
20454 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20455     
20456     active : false,
20457     title : '',
20458     
20459     // the tabBox that this is attached to.
20460     tab : false,
20461      
20462     getAutoCreate : function() 
20463     {
20464         var cfg = {
20465             tag: 'div',
20466             cls: 'tab-pane'
20467         }
20468         
20469         if(this.active){
20470             cfg.cls += ' active';
20471         }
20472         
20473         return cfg;
20474     },
20475     initEvents  : function()
20476     {
20477         //Roo.log('trigger add pane handler');
20478         this.parent().fireEvent('addpane', this)
20479     },
20480     
20481      /**
20482      * Updates the tab title 
20483      * @param {String} html to set the title to.
20484      */
20485     setTitle: function(str)
20486     {
20487         if (!this.tab) {
20488             return;
20489         }
20490         this.title = str;
20491         this.tab.select('a', true).first().dom.innerHTML = str;
20492         
20493     }
20494     
20495     
20496     
20497 });
20498
20499  
20500
20501
20502  /*
20503  * - LGPL
20504  *
20505  * menu
20506  * 
20507  */
20508 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20509
20510 /**
20511  * @class Roo.bootstrap.menu.Menu
20512  * @extends Roo.bootstrap.Component
20513  * Bootstrap Menu class - container for Menu
20514  * @cfg {String} html Text of the menu
20515  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20516  * @cfg {String} icon Font awesome icon
20517  * @cfg {String} pos Menu align to (top | bottom) default bottom
20518  * 
20519  * 
20520  * @constructor
20521  * Create a new Menu
20522  * @param {Object} config The config object
20523  */
20524
20525
20526 Roo.bootstrap.menu.Menu = function(config){
20527     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20528     
20529     this.addEvents({
20530         /**
20531          * @event beforeshow
20532          * Fires before this menu is displayed
20533          * @param {Roo.bootstrap.menu.Menu} this
20534          */
20535         beforeshow : true,
20536         /**
20537          * @event beforehide
20538          * Fires before this menu is hidden
20539          * @param {Roo.bootstrap.menu.Menu} this
20540          */
20541         beforehide : true,
20542         /**
20543          * @event show
20544          * Fires after this menu is displayed
20545          * @param {Roo.bootstrap.menu.Menu} this
20546          */
20547         show : true,
20548         /**
20549          * @event hide
20550          * Fires after this menu is hidden
20551          * @param {Roo.bootstrap.menu.Menu} this
20552          */
20553         hide : true,
20554         /**
20555          * @event click
20556          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20557          * @param {Roo.bootstrap.menu.Menu} this
20558          * @param {Roo.EventObject} e
20559          */
20560         click : true
20561     });
20562     
20563 };
20564
20565 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20566     
20567     submenu : false,
20568     html : '',
20569     weight : 'default',
20570     icon : false,
20571     pos : 'bottom',
20572     
20573     
20574     getChildContainer : function() {
20575         if(this.isSubMenu){
20576             return this.el;
20577         }
20578         
20579         return this.el.select('ul.dropdown-menu', true).first();  
20580     },
20581     
20582     getAutoCreate : function()
20583     {
20584         var text = [
20585             {
20586                 tag : 'span',
20587                 cls : 'roo-menu-text',
20588                 html : this.html
20589             }
20590         ];
20591         
20592         if(this.icon){
20593             text.unshift({
20594                 tag : 'i',
20595                 cls : 'fa ' + this.icon
20596             })
20597         }
20598         
20599         
20600         var cfg = {
20601             tag : 'div',
20602             cls : 'btn-group',
20603             cn : [
20604                 {
20605                     tag : 'button',
20606                     cls : 'dropdown-button btn btn-' + this.weight,
20607                     cn : text
20608                 },
20609                 {
20610                     tag : 'button',
20611                     cls : 'dropdown-toggle btn btn-' + this.weight,
20612                     cn : [
20613                         {
20614                             tag : 'span',
20615                             cls : 'caret'
20616                         }
20617                     ]
20618                 },
20619                 {
20620                     tag : 'ul',
20621                     cls : 'dropdown-menu'
20622                 }
20623             ]
20624             
20625         };
20626         
20627         if(this.pos == 'top'){
20628             cfg.cls += ' dropup';
20629         }
20630         
20631         if(this.isSubMenu){
20632             cfg = {
20633                 tag : 'ul',
20634                 cls : 'dropdown-menu'
20635             }
20636         }
20637         
20638         return cfg;
20639     },
20640     
20641     onRender : function(ct, position)
20642     {
20643         this.isSubMenu = ct.hasClass('dropdown-submenu');
20644         
20645         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20646     },
20647     
20648     initEvents : function() 
20649     {
20650         if(this.isSubMenu){
20651             return;
20652         }
20653         
20654         this.hidden = true;
20655         
20656         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20657         this.triggerEl.on('click', this.onTriggerPress, this);
20658         
20659         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20660         this.buttonEl.on('click', this.onClick, this);
20661         
20662     },
20663     
20664     list : function()
20665     {
20666         if(this.isSubMenu){
20667             return this.el;
20668         }
20669         
20670         return this.el.select('ul.dropdown-menu', true).first();
20671     },
20672     
20673     onClick : function(e)
20674     {
20675         this.fireEvent("click", this, e);
20676     },
20677     
20678     onTriggerPress  : function(e)
20679     {   
20680         if (this.isVisible()) {
20681             this.hide();
20682         } else {
20683             this.show();
20684         }
20685     },
20686     
20687     isVisible : function(){
20688         return !this.hidden;
20689     },
20690     
20691     show : function()
20692     {
20693         this.fireEvent("beforeshow", this);
20694         
20695         this.hidden = false;
20696         this.el.addClass('open');
20697         
20698         Roo.get(document).on("mouseup", this.onMouseUp, this);
20699         
20700         this.fireEvent("show", this);
20701         
20702         
20703     },
20704     
20705     hide : function()
20706     {
20707         this.fireEvent("beforehide", this);
20708         
20709         this.hidden = true;
20710         this.el.removeClass('open');
20711         
20712         Roo.get(document).un("mouseup", this.onMouseUp);
20713         
20714         this.fireEvent("hide", this);
20715     },
20716     
20717     onMouseUp : function()
20718     {
20719         this.hide();
20720     }
20721     
20722 });
20723
20724  
20725  /*
20726  * - LGPL
20727  *
20728  * menu item
20729  * 
20730  */
20731 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20732
20733 /**
20734  * @class Roo.bootstrap.menu.Item
20735  * @extends Roo.bootstrap.Component
20736  * Bootstrap MenuItem class
20737  * @cfg {Boolean} submenu (true | false) default false
20738  * @cfg {String} html text of the item
20739  * @cfg {String} href the link
20740  * @cfg {Boolean} disable (true | false) default false
20741  * @cfg {Boolean} preventDefault (true | false) default true
20742  * @cfg {String} icon Font awesome icon
20743  * @cfg {String} pos Submenu align to (left | right) default right 
20744  * 
20745  * 
20746  * @constructor
20747  * Create a new Item
20748  * @param {Object} config The config object
20749  */
20750
20751
20752 Roo.bootstrap.menu.Item = function(config){
20753     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20754     this.addEvents({
20755         /**
20756          * @event mouseover
20757          * Fires when the mouse is hovering over this menu
20758          * @param {Roo.bootstrap.menu.Item} this
20759          * @param {Roo.EventObject} e
20760          */
20761         mouseover : true,
20762         /**
20763          * @event mouseout
20764          * Fires when the mouse exits this menu
20765          * @param {Roo.bootstrap.menu.Item} this
20766          * @param {Roo.EventObject} e
20767          */
20768         mouseout : true,
20769         // raw events
20770         /**
20771          * @event click
20772          * The raw click event for the entire grid.
20773          * @param {Roo.EventObject} e
20774          */
20775         click : true
20776     });
20777 };
20778
20779 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20780     
20781     submenu : false,
20782     href : '',
20783     html : '',
20784     preventDefault: true,
20785     disable : false,
20786     icon : false,
20787     pos : 'right',
20788     
20789     getAutoCreate : function()
20790     {
20791         var text = [
20792             {
20793                 tag : 'span',
20794                 cls : 'roo-menu-item-text',
20795                 html : this.html
20796             }
20797         ];
20798         
20799         if(this.icon){
20800             text.unshift({
20801                 tag : 'i',
20802                 cls : 'fa ' + this.icon
20803             })
20804         }
20805         
20806         var cfg = {
20807             tag : 'li',
20808             cn : [
20809                 {
20810                     tag : 'a',
20811                     href : this.href || '#',
20812                     cn : text
20813                 }
20814             ]
20815         };
20816         
20817         if(this.disable){
20818             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20819         }
20820         
20821         if(this.submenu){
20822             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20823             
20824             if(this.pos == 'left'){
20825                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20826             }
20827         }
20828         
20829         return cfg;
20830     },
20831     
20832     initEvents : function() 
20833     {
20834         this.el.on('mouseover', this.onMouseOver, this);
20835         this.el.on('mouseout', this.onMouseOut, this);
20836         
20837         this.el.select('a', true).first().on('click', this.onClick, this);
20838         
20839     },
20840     
20841     onClick : function(e)
20842     {
20843         if(this.preventDefault){
20844             e.preventDefault();
20845         }
20846         
20847         this.fireEvent("click", this, e);
20848     },
20849     
20850     onMouseOver : function(e)
20851     {
20852         if(this.submenu && this.pos == 'left'){
20853             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20854         }
20855         
20856         this.fireEvent("mouseover", this, e);
20857     },
20858     
20859     onMouseOut : function(e)
20860     {
20861         this.fireEvent("mouseout", this, e);
20862     }
20863 });
20864
20865  
20866
20867  /*
20868  * - LGPL
20869  *
20870  * menu separator
20871  * 
20872  */
20873 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20874
20875 /**
20876  * @class Roo.bootstrap.menu.Separator
20877  * @extends Roo.bootstrap.Component
20878  * Bootstrap Separator class
20879  * 
20880  * @constructor
20881  * Create a new Separator
20882  * @param {Object} config The config object
20883  */
20884
20885
20886 Roo.bootstrap.menu.Separator = function(config){
20887     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20888 };
20889
20890 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20891     
20892     getAutoCreate : function(){
20893         var cfg = {
20894             tag : 'li',
20895             cls: 'divider'
20896         };
20897         
20898         return cfg;
20899     }
20900    
20901 });
20902
20903  
20904
20905  /*
20906  * - LGPL
20907  *
20908  * Tooltip
20909  * 
20910  */
20911
20912 /**
20913  * @class Roo.bootstrap.Tooltip
20914  * Bootstrap Tooltip class
20915  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
20916  * to determine which dom element triggers the tooltip.
20917  * 
20918  * It needs to add support for additional attributes like tooltip-position
20919  * 
20920  * @constructor
20921  * Create a new Toolti
20922  * @param {Object} config The config object
20923  */
20924
20925 Roo.bootstrap.Tooltip = function(config){
20926     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
20927 };
20928
20929 Roo.apply(Roo.bootstrap.Tooltip, {
20930     /**
20931      * @function init initialize tooltip monitoring.
20932      * @static
20933      */
20934     currentEl : false,
20935     currentTip : false,
20936     currentRegion : false,
20937     
20938     //  init : delay?
20939     
20940     init : function()
20941     {
20942         Roo.get(document).on('mouseover', this.enter ,this);
20943         Roo.get(document).on('mouseout', this.leave, this);
20944          
20945         
20946         this.currentTip = new Roo.bootstrap.Tooltip();
20947     },
20948     
20949     enter : function(ev)
20950     {
20951         var dom = ev.getTarget();
20952         //Roo.log(['enter',dom]);
20953         var el = Roo.fly(dom);
20954         if (this.currentEl) {
20955             //Roo.log(dom);
20956             //Roo.log(this.currentEl);
20957             //Roo.log(this.currentEl.contains(dom));
20958             if (this.currentEl == el) {
20959                 return;
20960             }
20961             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
20962                 return;
20963             }
20964
20965         }
20966         
20967         
20968         
20969         if (this.currentTip.el) {
20970             this.currentTip.el.hide(); // force hiding...
20971         }    
20972         //Roo.log(el);
20973         if (!el.attr('tooltip')) { // parents who have tip?
20974             return;
20975         }
20976         this.currentEl = el;
20977         this.currentTip.bind(el);
20978         this.currentRegion = Roo.lib.Region.getRegion(dom);
20979         this.currentTip.enter();
20980         
20981     },
20982     leave : function(ev)
20983     {
20984         var dom = ev.getTarget();
20985         //Roo.log(['leave',dom]);
20986         if (!this.currentEl) {
20987             return;
20988         }
20989         
20990         
20991         if (dom != this.currentEl.dom) {
20992             return;
20993         }
20994         var xy = ev.getXY();
20995         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
20996             return;
20997         }
20998         // only activate leave if mouse cursor is outside... bounding box..
20999         
21000         
21001         
21002         
21003         if (this.currentTip) {
21004             this.currentTip.leave();
21005         }
21006         //Roo.log('clear currentEl');
21007         this.currentEl = false;
21008         
21009         
21010     },
21011     alignment : {
21012         'left' : ['r-l', [-2,0], 'right'],
21013         'right' : ['l-r', [2,0], 'left'],
21014         'bottom' : ['t-b', [0,2], 'top'],
21015         'top' : [ 'b-t', [0,-2], 'bottom']
21016     }
21017     
21018 });
21019
21020
21021 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
21022     
21023     
21024     bindEl : false,
21025     
21026     delay : null, // can be { show : 300 , hide: 500}
21027     
21028     timeout : null,
21029     
21030     hoverState : null, //???
21031     
21032     placement : 'bottom', 
21033     
21034     getAutoCreate : function(){
21035     
21036         var cfg = {
21037            cls : 'tooltip',
21038            role : 'tooltip',
21039            cn : [
21040                 {
21041                     cls : 'tooltip-arrow'
21042                 },
21043                 {
21044                     cls : 'tooltip-inner'
21045                 }
21046            ]
21047         };
21048         
21049         return cfg;
21050     },
21051     bind : function(el)
21052     {
21053         this.bindEl = el;
21054     },
21055       
21056     
21057     enter : function () {
21058        
21059         if (this.timeout != null) {
21060             clearTimeout(this.timeout);
21061         }
21062         
21063         this.hoverState = 'in'
21064          //Roo.log("enter - show");
21065         if (!this.delay || !this.delay.show) {
21066             this.show();
21067             return;
21068         }
21069         var _t = this;
21070         this.timeout = setTimeout(function () {
21071             if (_t.hoverState == 'in') {
21072                 _t.show();
21073             }
21074         }, this.delay.show);
21075     },
21076     leave : function()
21077     {
21078         clearTimeout(this.timeout);
21079     
21080         this.hoverState = 'out'
21081          if (!this.delay || !this.delay.hide) {
21082             this.hide();
21083             return 
21084         }
21085        
21086         var _t = this;
21087         this.timeout = setTimeout(function () {
21088             //Roo.log("leave - timeout");
21089             
21090             if (_t.hoverState == 'out') {
21091                 _t.hide();
21092                 Roo.bootstrap.Tooltip.currentEl = false;
21093             }
21094         }, delay)
21095     },
21096     
21097     show : function ()
21098     {
21099         if (!this.el) {
21100             this.render(document.body);
21101         }
21102         // set content.
21103         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
21104         this.el.select('.tooltip-inner',true).first().dom.innerHTML = this.bindEl.attr('tooltip');
21105         
21106         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
21107         
21108         var placement = typeof this.placement == 'function' ?
21109             this.placement.call(this, this.el, on_el) :
21110             this.placement;
21111             
21112         var autoToken = /\s?auto?\s?/i;
21113         var autoPlace = autoToken.test(placement);
21114         if (autoPlace) {
21115             placement = placement.replace(autoToken, '') || 'top';
21116         }
21117         
21118         //this.el.detach()
21119         //this.el.setXY([0,0]);
21120         this.el.show();
21121         //this.el.dom.style.display='block';
21122         this.el.addClass(placement);
21123         
21124         //this.el.appendTo(on_el);
21125         
21126         var p = this.getPosition();
21127         var box = this.el.getBox();
21128         
21129         if (autoPlace) {
21130             // fixme..
21131         }
21132         var align = Roo.bootstrap.Tooltip.alignment[placement]
21133         this.el.alignTo(this.bindEl, align[0],align[1]);
21134         //var arrow = this.el.select('.arrow',true).first();
21135         //arrow.set(align[2], 
21136         
21137         this.el.addClass('in fade');
21138         this.hoverState = null;
21139         
21140         if (this.el.hasClass('fade')) {
21141             // fade it?
21142         }
21143         
21144     },
21145     hide : function()
21146     {
21147          
21148         if (!this.el) {
21149             return;
21150         }
21151         //this.el.setXY([0,0]);
21152         this.el.removeClass('in');
21153         //this.el.hide();
21154         
21155     }
21156     
21157 });
21158  
21159
21160  /*
21161  * - LGPL
21162  *
21163  * Location Picker
21164  * 
21165  */
21166
21167 /**
21168  * @class Roo.bootstrap.LocationPicker
21169  * @extends Roo.bootstrap.Component
21170  * Bootstrap LocationPicker class
21171  * @cfg {Number} latitude Position when init default 0
21172  * @cfg {Number} longitude Position when init default 0
21173  * @cfg {Number} zoom default 15
21174  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
21175  * @cfg {Boolean} mapTypeControl default false
21176  * @cfg {Boolean} disableDoubleClickZoom default false
21177  * @cfg {Boolean} scrollwheel default true
21178  * @cfg {Boolean} streetViewControl default false
21179  * @cfg {Number} radius default 0
21180  * @cfg {String} locationName
21181  * @cfg {Boolean} draggable default true
21182  * @cfg {Boolean} enableAutocomplete default false
21183  * @cfg {Boolean} enableReverseGeocode default true
21184  * @cfg {String} markerTitle
21185  * 
21186  * @constructor
21187  * Create a new LocationPicker
21188  * @param {Object} config The config object
21189  */
21190
21191
21192 Roo.bootstrap.LocationPicker = function(config){
21193     
21194     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
21195     
21196      this.addEvents({
21197             /**
21198              * @event initial
21199              * Fires when the picker initialized.
21200              * @param {Roo.bootstrap.LocationPicker} this
21201              * @param {Google Location} location
21202              */
21203             initial : true,
21204             /**
21205              * @event positionchanged
21206              * Fires when the picker position changed.
21207              * @param {Roo.bootstrap.LocationPicker} this
21208              * @param {Google Location} location
21209              */
21210             positionchanged : true,
21211             /**
21212              * @event resize
21213              * Fires when the map resize.
21214              * @param {Roo.bootstrap.LocationPicker} this
21215              */
21216             resize : true,
21217             /**
21218              * @event show
21219              * Fires when the map show.
21220              * @param {Roo.bootstrap.LocationPicker} this
21221              */
21222             show : true,
21223             /**
21224              * @event hide
21225              * Fires when the map hide.
21226              * @param {Roo.bootstrap.LocationPicker} this
21227              */
21228             hide : true
21229         });
21230         
21231 };
21232
21233 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
21234     
21235     gMapContext: false,
21236     
21237     latitude: 0,
21238     longitude: 0,
21239     zoom: 15,
21240     mapTypeId: false,
21241     mapTypeControl: false,
21242     disableDoubleClickZoom: false,
21243     scrollwheel: true,
21244     streetViewControl: false,
21245     radius: 0,
21246     locationName: '',
21247     draggable: true,
21248     enableAutocomplete: false,
21249     enableReverseGeocode: true,
21250     markerTitle: '',
21251     
21252     getAutoCreate: function()
21253     {
21254
21255         var cfg = {
21256             tag: 'div',
21257             cls: 'roo-location-picker'
21258         };
21259         
21260         return cfg
21261     },
21262     
21263     initEvents: function(ct, position)
21264     {   
21265         if(!this.mapTypeId){
21266             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
21267         }
21268             
21269         if(!this.el.getWidth() || this.isApplied()){
21270             return;
21271         }
21272         
21273         this.el.setVisibilityMode(Roo.Element.DISPLAY);
21274         
21275         this.initial();
21276     },
21277     
21278     initial: function()
21279     {
21280         this.gMapContext = this.GMapContext();
21281         
21282         var _this = this;
21283         
21284         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
21285             _this.setPosition(_this.gMapContext.marker.position);
21286         });
21287         
21288         this.setPosition(this.gMapContext.location);
21289         
21290         this.fireEvent('initial', this, this.gMapContext.location);
21291     },
21292     
21293     isApplied: function() 
21294     {
21295         return this.getGmapContext() == false ? false : true;
21296     },
21297     
21298     getGmapContext: function() 
21299     {
21300         return this.gMapContext
21301     },
21302     
21303     GMapContext: function() 
21304     {
21305         var _map = new google.maps.Map(this.el.dom, this);
21306         var _marker = new google.maps.Marker({
21307             position: new google.maps.LatLng(this.latitude, this.longitude),
21308             map: _map,
21309             title: this.markerTitle,
21310             draggable: this.draggable
21311         });
21312         
21313         return {
21314             map: _map,
21315             marker: _marker,
21316             circle: null,
21317             location: _marker.position,
21318             radius: this.radius,
21319             locationName: this.locationName,
21320             addressComponents: {
21321                 formatted_address: null,
21322                 addressLine1: null,
21323                 addressLine2: null,
21324                 streetName: null,
21325                 streetNumber: null,
21326                 city: null,
21327                 district: null,
21328                 state: null,
21329                 stateOrProvince: null
21330             },
21331             settings: this,
21332             domContainer: this.el.dom,
21333             geodecoder: new google.maps.Geocoder()
21334         };
21335     },
21336     
21337     drawCircle: function(center, radius, options) 
21338     {
21339         if (this.gMapContext.circle != null) {
21340             this.gMapContext.circle.setMap(null);
21341         }
21342         if (radius > 0) {
21343             radius *= 1;
21344             options = Roo.apply({}, options, {
21345                 strokeColor: "#0000FF",
21346                 strokeOpacity: .35,
21347                 strokeWeight: 2,
21348                 fillColor: "#0000FF",
21349                 fillOpacity: .2
21350             });
21351             
21352             options.map = this.gMapContext.map;
21353             options.radius = radius;
21354             options.center = center;
21355             this.gMapContext.circle = new google.maps.Circle(options);
21356             return this.gMapContext.circle;
21357         }
21358         
21359         return null;
21360     },
21361     
21362     setPosition: function(location) 
21363     {
21364         this.gMapContext.location = location;
21365         this.gMapContext.marker.setPosition(location);
21366         this.gMapContext.map.panTo(location);
21367         this.drawCircle(location, this.gMapContext.radius, {});
21368         
21369         var _this = this;
21370         
21371         if (this.gMapContext.settings.enableReverseGeocode) {
21372             this.gMapContext.geodecoder.geocode({
21373                 latLng: this.gMapContext.location
21374             }, function(results, status) {
21375                 
21376                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
21377                     _this.gMapContext.locationName = results[0].formatted_address;
21378                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
21379                     
21380                     _this.fireEvent('positionchanged', this, location);
21381                 }
21382             });
21383             
21384             return;
21385         }
21386         
21387         this.fireEvent('positionchanged', this, location);
21388     },
21389     
21390     resize: function()
21391     {
21392         google.maps.event.trigger(this.gMapContext.map, "resize");
21393         
21394         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
21395         
21396         this.fireEvent('resize', this);
21397     },
21398     
21399     setPositionByLatLng: function(latitude, longitude)
21400     {
21401         this.setPosition(new google.maps.LatLng(latitude, longitude));
21402     },
21403     
21404     getCurrentPosition: function() 
21405     {
21406         return {
21407             latitude: this.gMapContext.location.lat(),
21408             longitude: this.gMapContext.location.lng()
21409         };
21410     },
21411     
21412     getAddressName: function() 
21413     {
21414         return this.gMapContext.locationName;
21415     },
21416     
21417     getAddressComponents: function() 
21418     {
21419         return this.gMapContext.addressComponents;
21420     },
21421     
21422     address_component_from_google_geocode: function(address_components) 
21423     {
21424         var result = {};
21425         
21426         for (var i = 0; i < address_components.length; i++) {
21427             var component = address_components[i];
21428             if (component.types.indexOf("postal_code") >= 0) {
21429                 result.postalCode = component.short_name;
21430             } else if (component.types.indexOf("street_number") >= 0) {
21431                 result.streetNumber = component.short_name;
21432             } else if (component.types.indexOf("route") >= 0) {
21433                 result.streetName = component.short_name;
21434             } else if (component.types.indexOf("neighborhood") >= 0) {
21435                 result.city = component.short_name;
21436             } else if (component.types.indexOf("locality") >= 0) {
21437                 result.city = component.short_name;
21438             } else if (component.types.indexOf("sublocality") >= 0) {
21439                 result.district = component.short_name;
21440             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
21441                 result.stateOrProvince = component.short_name;
21442             } else if (component.types.indexOf("country") >= 0) {
21443                 result.country = component.short_name;
21444             }
21445         }
21446         
21447         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
21448         result.addressLine2 = "";
21449         return result;
21450     },
21451     
21452     setZoomLevel: function(zoom)
21453     {
21454         this.gMapContext.map.setZoom(zoom);
21455     },
21456     
21457     show: function()
21458     {
21459         if(!this.el){
21460             return;
21461         }
21462         
21463         this.el.show();
21464         
21465         this.resize();
21466         
21467         this.fireEvent('show', this);
21468     },
21469     
21470     hide: function()
21471     {
21472         if(!this.el){
21473             return;
21474         }
21475         
21476         this.el.hide();
21477         
21478         this.fireEvent('hide', this);
21479     }
21480     
21481 });
21482 /*
21483  * - LGPL
21484  *
21485  * Alert
21486  * 
21487  */
21488
21489 /**
21490  * @class Roo.bootstrap.Alert
21491  * @extends Roo.bootstrap.Component
21492  * Bootstrap Alert class
21493  * @cfg {String} title The title of alert
21494  * @cfg {String} html The content of alert
21495  * @cfg {String} weight (  success | info | warning | danger )
21496  * @cfg {String} faicon font-awesomeicon
21497  * 
21498  * @constructor
21499  * Create a new alert
21500  * @param {Object} config The config object
21501  */
21502
21503
21504 Roo.bootstrap.Alert = function(config){
21505     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
21506     
21507 };
21508
21509 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
21510     
21511     title: '',
21512     html: '',
21513     weight: false,
21514     faicon: false,
21515     
21516     getAutoCreate : function()
21517     {
21518         
21519         var cfg = {
21520             tag : 'div',
21521             cls : 'alert',
21522             cn : [
21523                 {
21524                     tag : 'i',
21525                     cls : 'roo-alert-icon'
21526                     
21527                 },
21528                 {
21529                     tag : 'b',
21530                     cls : 'roo-alert-title',
21531                     html : this.title
21532                 },
21533                 {
21534                     tag : 'span',
21535                     cls : 'roo-alert-text',
21536                     html : this.html
21537                 }
21538             ]
21539         };
21540         
21541         if(this.faicon){
21542             cfg.cn[0].cls += ' fa ' + this.faicon;
21543         }
21544         
21545         if(this.weight){
21546             cfg.cls += ' alert-' + this.weight;
21547         }
21548         
21549         return cfg;
21550     },
21551     
21552     initEvents: function() 
21553     {
21554         this.el.setVisibilityMode(Roo.Element.DISPLAY);
21555     },
21556     
21557     setTitle : function(str)
21558     {
21559         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
21560     },
21561     
21562     setText : function(str)
21563     {
21564         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
21565     },
21566     
21567     setWeight : function(weight)
21568     {
21569         if(this.weight){
21570             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
21571         }
21572         
21573         this.weight = weight;
21574         
21575         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
21576     },
21577     
21578     setIcon : function(icon)
21579     {
21580         if(this.faicon){
21581             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
21582         }
21583         
21584         this.faicon = icon
21585         
21586         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
21587     },
21588     
21589     hide: function() 
21590     {
21591         this.el.hide();   
21592     },
21593     
21594     show: function() 
21595     {  
21596         this.el.show();   
21597     }
21598     
21599 });
21600
21601