roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129        
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         this.initEvents();
141         
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         
211         while (true) {
212             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
213             
214             if (!echild) {
215                 break;
216             }
217             
218             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
219                 break;
220             }
221             
222             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
223         }
224         return ret;
225     },
226     
227     addxtypeChild : function (tree, cntr)
228     {
229         Roo.debug && Roo.log('addxtypeChild:' + cntr);
230         var cn = this;
231         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
232         
233         
234         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
235                     (typeof(tree['flexy:foreach']) != 'undefined');
236           
237         
238         
239          skip_children = false;
240         // render the element if it's not BODY.
241         if (tree.xtype != 'Body') {
242            
243             cn = Roo.factory(tree);
244            
245             cn.parentType = this.xtype; //??
246             cn.parentId = this.id;
247             
248             var build_from_html =  Roo.XComponent.build_from_html;
249             
250             
251             // does the container contain child eleemnts with 'xtype' attributes.
252             // that match this xtype..
253             // note - when we render we create these as well..
254             // so we should check to see if body has xtype set.
255             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
256                
257                 var self_cntr_el = Roo.get(this[cntr](false));
258                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
259                 
260                 
261                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
262                 // and are not displayed -this causes this to use up the wrong element when matching.
263                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
264                 
265                 
266                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
267                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
268                   
269                   
270                   
271                     cn.el = echild;
272                   //  Roo.log("GOT");
273                     //echild.dom.removeAttribute('xtype');
274                 } else {
275                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
276                     Roo.debug && Roo.log(self_cntr_el);
277                     Roo.debug && Roo.log(echild);
278                     Roo.debug && Roo.log(cn);
279                 }
280             }
281            
282             
283            
284             // if object has flexy:if - then it may or may not be rendered.
285             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
286                 // skip a flexy if element.
287                 Roo.debug && Roo.log('skipping render');
288                 Roo.debug && Roo.log(tree);
289                 if (!cn.el) {
290                     Roo.debug && Roo.log('skipping all children');
291                     skip_children = true;
292                 }
293                 
294              } else {
295                  
296                 // actually if flexy:foreach is found, we really want to create 
297                 // multiple copies here...
298                 //Roo.log('render');
299                 //Roo.log(this[cntr]());
300                 cn.render(this[cntr](true));
301              }
302             // then add the element..
303         }
304         
305         
306         // handle the kids..
307         
308         var nitems = [];
309         /*
310         if (typeof (tree.menu) != 'undefined') {
311             tree.menu.parentType = cn.xtype;
312             tree.menu.triggerEl = cn.el;
313             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
314             
315         }
316         */
317         if (!tree.items || !tree.items.length) {
318             cn.items = nitems;
319             return cn;
320         }
321         var items = tree.items;
322         delete tree.items;
323         
324         //Roo.log(items.length);
325             // add the items..
326         if (!skip_children) {    
327             for(var i =0;i < items.length;i++) {
328                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
329             }
330         }
331         
332         cn.items = nitems;
333         
334         this.fireEvent('childrenrendered', this);
335         
336         return cn;
337     } 
338     
339     
340 });
341
342  /*
343  * - LGPL
344  *
345  * Body
346  * 
347  */
348
349 /**
350  * @class Roo.bootstrap.Body
351  * @extends Roo.bootstrap.Component
352  * Bootstrap Body class
353  * 
354  * @constructor
355  * Create a new body
356  * @param {Object} config The config object
357  */
358
359 Roo.bootstrap.Body = function(config){
360     Roo.bootstrap.Body.superclass.constructor.call(this, config);
361     this.el = Roo.get(document.body);
362     if (this.cls && this.cls.length) {
363         Roo.get(document.body).addClass(this.cls);
364     }
365 };
366
367 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
368       
369         autoCreate : {
370         cls: 'container'
371     },
372     onRender : function(ct, position)
373     {
374        /* Roo.log("Roo.bootstrap.Body - onRender");
375         if (this.cls && this.cls.length) {
376             Roo.get(document.body).addClass(this.cls);
377         }
378         // style??? xttr???
379         */
380     }
381     
382     
383  
384    
385 });
386
387  /*
388  * - LGPL
389  *
390  * button group
391  * 
392  */
393
394
395 /**
396  * @class Roo.bootstrap.ButtonGroup
397  * @extends Roo.bootstrap.Component
398  * Bootstrap ButtonGroup class
399  * @cfg {String} size lg | sm | xs (default empty normal)
400  * @cfg {String} align vertical | justified  (default none)
401  * @cfg {String} direction up | down (default down)
402  * @cfg {Boolean} toolbar false | true
403  * @cfg {Boolean} btn true | false
404  * 
405  * 
406  * @constructor
407  * Create a new Input
408  * @param {Object} config The config object
409  */
410
411 Roo.bootstrap.ButtonGroup = function(config){
412     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
413 };
414
415 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
416     
417     size: '',
418     align: '',
419     direction: '',
420     toolbar: false,
421     btn: true,
422
423     getAutoCreate : function(){
424         var cfg = {
425             cls: 'btn-group',
426             html : null
427         }
428         
429         cfg.html = this.html || cfg.html;
430         
431         if (this.toolbar) {
432             cfg = {
433                 cls: 'btn-toolbar',
434                 html: null
435             }
436             
437             return cfg;
438         }
439         
440         if (['vertical','justified'].indexOf(this.align)!==-1) {
441             cfg.cls = 'btn-group-' + this.align;
442             
443             if (this.align == 'justified') {
444                 console.log(this.items);
445             }
446         }
447         
448         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
449             cfg.cls += ' btn-group-' + this.size;
450         }
451         
452         if (this.direction == 'up') {
453             cfg.cls += ' dropup' ;
454         }
455         
456         return cfg;
457     }
458    
459 });
460
461  /*
462  * - LGPL
463  *
464  * button
465  * 
466  */
467
468 /**
469  * @class Roo.bootstrap.Button
470  * @extends Roo.bootstrap.Component
471  * Bootstrap Button class
472  * @cfg {String} html The button content
473  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
474  * @cfg {String} size ( lg | sm | xs)
475  * @cfg {String} tag ( a | input | submit)
476  * @cfg {String} href empty or href
477  * @cfg {Boolean} disabled default false;
478  * @cfg {Boolean} isClose default false;
479  * @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)
480  * @cfg {String} badge text for badge
481  * @cfg {String} theme default 
482  * @cfg {Boolean} inverse 
483  * @cfg {Boolean} toggle 
484  * @cfg {String} ontext text for on toggle state
485  * @cfg {String} offtext text for off toggle state
486  * @cfg {Boolean} defaulton 
487  * @cfg {Boolean} preventDefault  default true
488  * @cfg {Boolean} removeClass remove the standard class..
489  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
490  * 
491  * @constructor
492  * Create a new button
493  * @param {Object} config The config object
494  */
495
496
497 Roo.bootstrap.Button = function(config){
498     Roo.bootstrap.Button.superclass.constructor.call(this, config);
499     this.addEvents({
500         // raw events
501         /**
502          * @event click
503          * When a butotn is pressed
504          * @param {Roo.EventObject} e
505          */
506         "click" : true,
507          /**
508          * @event toggle
509          * After the button has been toggles
510          * @param {Roo.EventObject} e
511          * @param {boolean} pressed (also available as button.pressed)
512          */
513         "toggle" : true
514     });
515 };
516
517 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
518     html: false,
519     active: false,
520     weight: '',
521     size: '',
522     tag: 'button',
523     href: '',
524     disabled: false,
525     isClose: false,
526     glyphicon: '',
527     badge: '',
528     theme: 'default',
529     inverse: false,
530     
531     toggle: false,
532     ontext: 'ON',
533     offtext: 'OFF',
534     defaulton: true,
535     preventDefault: true,
536     removeClass: false,
537     name: false,
538     target: false,
539     
540     
541     pressed : null,
542      
543     
544     getAutoCreate : function(){
545         
546         var cfg = {
547             tag : 'button',
548             cls : 'roo-button',
549             html: ''
550         };
551         
552         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
553             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
554             this.tag = 'button';
555         } else {
556             cfg.tag = this.tag;
557         }
558         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
559         
560         if (this.toggle == true) {
561             cfg={
562                 tag: 'div',
563                 cls: 'slider-frame roo-button',
564                 cn: [
565                     {
566                         tag: 'span',
567                         'data-on-text':'ON',
568                         'data-off-text':'OFF',
569                         cls: 'slider-button',
570                         html: this.offtext
571                     }
572                 ]
573             };
574             
575             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
576                 cfg.cls += ' '+this.weight;
577             }
578             
579             return cfg;
580         }
581         
582         if (this.isClose) {
583             cfg.cls += ' close';
584             
585             cfg["aria-hidden"] = true;
586             
587             cfg.html = "&times;";
588             
589             return cfg;
590         }
591         
592          
593         if (this.theme==='default') {
594             cfg.cls = 'btn roo-button';
595             
596             //if (this.parentType != 'Navbar') {
597             this.weight = this.weight.length ?  this.weight : 'default';
598             //}
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 
601                 cfg.cls += ' btn-' + this.weight;
602             }
603         } else if (this.theme==='glow') {
604             
605             cfg.tag = 'a';
606             cfg.cls = 'btn-glow roo-button';
607             
608             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
609                 
610                 cfg.cls += ' ' + this.weight;
611             }
612         }
613    
614         
615         if (this.inverse) {
616             this.cls += ' inverse';
617         }
618         
619         
620         if (this.active) {
621             cfg.cls += ' active';
622         }
623         
624         if (this.disabled) {
625             cfg.disabled = 'disabled';
626         }
627         
628         if (this.items) {
629             Roo.log('changing to ul' );
630             cfg.tag = 'ul';
631             this.glyphicon = 'caret';
632         }
633         
634         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
635          
636         //gsRoo.log(this.parentType);
637         if (this.parentType === 'Navbar' && !this.parent().bar) {
638             Roo.log('changing to li?');
639             
640             cfg.tag = 'li';
641             
642             cfg.cls = '';
643             cfg.cn =  [{
644                 tag : 'a',
645                 cls : 'roo-button',
646                 html : this.html,
647                 href : this.href || '#'
648             }];
649             if (this.menu) {
650                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
651                 cfg.cls += ' dropdown';
652             }   
653             
654             delete cfg.html;
655             
656         }
657         
658        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
659         
660         if (this.glyphicon) {
661             cfg.html = ' ' + cfg.html;
662             
663             cfg.cn = [
664                 {
665                     tag: 'span',
666                     cls: 'glyphicon glyphicon-' + this.glyphicon
667                 }
668             ];
669         }
670         
671         if (this.badge) {
672             cfg.html += ' ';
673             
674             cfg.tag = 'a';
675             
676 //            cfg.cls='btn roo-button';
677             
678             cfg.href=this.href;
679             
680             var value = cfg.html;
681             
682             if(this.glyphicon){
683                 value = {
684                             tag: 'span',
685                             cls: 'glyphicon glyphicon-' + this.glyphicon,
686                             html: this.html
687                         };
688                 
689             }
690             
691             cfg.cn = [
692                 value,
693                 {
694                     tag: 'span',
695                     cls: 'badge',
696                     html: this.badge
697                 }
698             ];
699             
700             cfg.html='';
701         }
702         
703         if (this.menu) {
704             cfg.cls += ' dropdown';
705             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
706         }
707         
708         if (cfg.tag !== 'a' && this.href !== '') {
709             throw "Tag must be a to set href.";
710         } else if (this.href.length > 0) {
711             cfg.href = this.href;
712         }
713         
714         if(this.removeClass){
715             cfg.cls = '';
716         }
717         
718         if(this.target){
719             cfg.target = this.target;
720         }
721         
722         return cfg;
723     },
724     initEvents: function() {
725        // Roo.log('init events?');
726 //        Roo.log(this.el.dom);
727         // add the menu...
728         
729         if (typeof (this.menu) != 'undefined') {
730             this.menu.parentType = this.xtype;
731             this.menu.triggerEl = this.el;
732             this.addxtype(Roo.apply({}, this.menu));
733         }
734
735
736        if (this.el.hasClass('roo-button')) {
737             this.el.on('click', this.onClick, this);
738        } else {
739             this.el.select('.roo-button').on('click', this.onClick, this);
740        }
741        
742        if(this.removeClass){
743            this.el.on('click', this.onClick, this);
744        }
745        
746        this.el.enableDisplayMode();
747         
748     },
749     onClick : function(e)
750     {
751         if (this.disabled) {
752             return;
753         }
754         
755         
756         Roo.log('button on click ');
757         if(this.preventDefault){
758             e.preventDefault();
759         }
760         if (this.pressed === true || this.pressed === false) {
761             this.pressed = !this.pressed;
762             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
763             this.fireEvent('toggle', this, e, this.pressed);
764         }
765         
766         
767         this.fireEvent('click', this, e);
768     },
769     
770     /**
771      * Enables this button
772      */
773     enable : function()
774     {
775         this.disabled = false;
776         this.el.removeClass('disabled');
777     },
778     
779     /**
780      * Disable this button
781      */
782     disable : function()
783     {
784         this.disabled = true;
785         this.el.addClass('disabled');
786     },
787      /**
788      * sets the active state on/off, 
789      * @param {Boolean} state (optional) Force a particular state
790      */
791     setActive : function(v) {
792         
793         this.el[v ? 'addClass' : 'removeClass']('active');
794     },
795      /**
796      * toggles the current active state 
797      */
798     toggleActive : function()
799     {
800        var active = this.el.hasClass('active');
801        this.setActive(!active);
802        
803         
804     },
805     setText : function(str)
806     {
807         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
808     },
809     getText : function()
810     {
811         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
812     },
813     hide: function() {
814        
815      
816         this.el.hide();   
817     },
818     show: function() {
819        
820         this.el.show();   
821     }
822     
823     
824 });
825
826  /*
827  * - LGPL
828  *
829  * column
830  * 
831  */
832
833 /**
834  * @class Roo.bootstrap.Column
835  * @extends Roo.bootstrap.Component
836  * Bootstrap Column class
837  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
838  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
839  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
840  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
841  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
842  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
843  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
844  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
845  *
846  * 
847  * @cfg {Boolean} hidden (true|false) hide the element
848  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
849  * @cfg {String} fa (ban|check|...) font awesome icon
850  * @cfg {Number} fasize (1|2|....) font awsome size
851
852  * @cfg {String} icon (info-sign|check|...) glyphicon name
853
854  * @cfg {String} html content of column.
855  * 
856  * @constructor
857  * Create a new Column
858  * @param {Object} config The config object
859  */
860
861 Roo.bootstrap.Column = function(config){
862     Roo.bootstrap.Column.superclass.constructor.call(this, config);
863 };
864
865 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
866     
867     xs: false,
868     sm: false,
869     md: false,
870     lg: false,
871     xsoff: false,
872     smoff: false,
873     mdoff: false,
874     lgoff: false,
875     html: '',
876     offset: 0,
877     alert: false,
878     fa: false,
879     icon : false,
880     hidden : false,
881     fasize : 1,
882     
883     getAutoCreate : function(){
884         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
885         
886         cfg = {
887             tag: 'div',
888             cls: 'column'
889         };
890         
891         var settings=this;
892         ['xs','sm','md','lg'].map(function(size){
893             //Roo.log( size + ':' + settings[size]);
894             
895             if (settings[size+'off'] !== false) {
896                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
897             }
898             
899             if (settings[size] === false) {
900                 return;
901             }
902             Roo.log(settings[size]);
903             if (!settings[size]) { // 0 = hidden
904                 cfg.cls += ' hidden-' + size;
905                 return;
906             }
907             cfg.cls += ' col-' + size + '-' + settings[size];
908             
909         });
910         
911         if (this.hidden) {
912             cfg.cls += ' hidden';
913         }
914         
915         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
916             cfg.cls +=' alert alert-' + this.alert;
917         }
918         
919         
920         if (this.html.length) {
921             cfg.html = this.html;
922         }
923         if (this.fa) {
924             var fasize = '';
925             if (this.fasize > 1) {
926                 fasize = ' fa-' + this.fasize + 'x';
927             }
928             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
929             
930             
931         }
932         if (this.icon) {
933             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
934         }
935         
936         return cfg;
937     }
938    
939 });
940
941  
942
943  /*
944  * - LGPL
945  *
946  * page container.
947  * 
948  */
949
950
951 /**
952  * @class Roo.bootstrap.Container
953  * @extends Roo.bootstrap.Component
954  * Bootstrap Container class
955  * @cfg {Boolean} jumbotron is it a jumbotron element
956  * @cfg {String} html content of element
957  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
958  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
959  * @cfg {String} header content of header (for panel)
960  * @cfg {String} footer content of footer (for panel)
961  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
962  * @cfg {String} tag (header|aside|section) type of HTML tag.
963  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
964  * @cfg {String} fa (ban|check|...) font awesome icon
965  * @cfg {String} icon (info-sign|check|...) glyphicon name
966  * @cfg {Boolean} hidden (true|false) hide the element
967
968  *     
969  * @constructor
970  * Create a new Container
971  * @param {Object} config The config object
972  */
973
974 Roo.bootstrap.Container = function(config){
975     Roo.bootstrap.Container.superclass.constructor.call(this, config);
976 };
977
978 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
979     
980     jumbotron : false,
981     well: '',
982     panel : '',
983     header: '',
984     footer : '',
985     sticky: '',
986     tag : false,
987     alert : false,
988     fa: false,
989     icon : false,
990   
991      
992     getChildContainer : function() {
993         
994         if(!this.el){
995             return false;
996         }
997         
998         if (this.panel.length) {
999             return this.el.select('.panel-body',true).first();
1000         }
1001         
1002         return this.el;
1003     },
1004     
1005     
1006     getAutoCreate : function(){
1007         
1008         var cfg = {
1009             tag : this.tag || 'div',
1010             html : '',
1011             cls : ''
1012         };
1013         if (this.jumbotron) {
1014             cfg.cls = 'jumbotron';
1015         }
1016         
1017         
1018         
1019         // - this is applied by the parent..
1020         //if (this.cls) {
1021         //    cfg.cls = this.cls + '';
1022         //}
1023         
1024         if (this.sticky.length) {
1025             
1026             var bd = Roo.get(document.body);
1027             if (!bd.hasClass('bootstrap-sticky')) {
1028                 bd.addClass('bootstrap-sticky');
1029                 Roo.select('html',true).setStyle('height', '100%');
1030             }
1031              
1032             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1033         }
1034         
1035         
1036         if (this.well.length) {
1037             switch (this.well) {
1038                 case 'lg':
1039                 case 'sm':
1040                     cfg.cls +=' well well-' +this.well;
1041                     break;
1042                 default:
1043                     cfg.cls +=' well';
1044                     break;
1045             }
1046         }
1047         
1048         if (this.hidden) {
1049             cfg.cls += ' hidden';
1050         }
1051         
1052         
1053         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1054             cfg.cls +=' alert alert-' + this.alert;
1055         }
1056         
1057         var body = cfg;
1058         
1059         if (this.panel.length) {
1060             cfg.cls += ' panel panel-' + this.panel;
1061             cfg.cn = [];
1062             if (this.header.length) {
1063                 cfg.cn.push({
1064                     
1065                     cls : 'panel-heading',
1066                     cn : [{
1067                         tag: 'h3',
1068                         cls : 'panel-title',
1069                         html : this.header
1070                     }]
1071                     
1072                 });
1073             }
1074             body = false;
1075             cfg.cn.push({
1076                 cls : 'panel-body',
1077                 html : this.html
1078             });
1079             
1080             
1081             if (this.footer.length) {
1082                 cfg.cn.push({
1083                     cls : 'panel-footer',
1084                     html : this.footer
1085                     
1086                 });
1087             }
1088             
1089         }
1090         
1091         if (body) {
1092             body.html = this.html || cfg.html;
1093             // prefix with the icons..
1094             if (this.fa) {
1095                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1096             }
1097             if (this.icon) {
1098                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1099             }
1100             
1101             
1102         }
1103         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1104             cfg.cls =  'container';
1105         }
1106         
1107         return cfg;
1108     },
1109     
1110     titleEl : function()
1111     {
1112         if(!this.el || !this.panel.length || !this.header.length){
1113             return;
1114         }
1115         
1116         return this.el.select('.panel-title',true).first();
1117     },
1118     
1119     setTitle : function(v)
1120     {
1121         var titleEl = this.titleEl();
1122         
1123         if(!titleEl){
1124             return;
1125         }
1126         
1127         titleEl.dom.innerHTML = v;
1128     },
1129     
1130     getTitle : function()
1131     {
1132         
1133         var titleEl = this.titleEl();
1134         
1135         if(!titleEl){
1136             return '';
1137         }
1138         
1139         return titleEl.dom.innerHTML;
1140     },
1141     
1142     show : function() {
1143         this.el.removeClass('hidden');
1144     },
1145     hide: function() {
1146         if (!this.el.hasClass('hidden')) {
1147             this.el.addClass('hidden');
1148         }
1149         
1150     }
1151    
1152 });
1153
1154  /*
1155  * - LGPL
1156  *
1157  * image
1158  * 
1159  */
1160
1161
1162 /**
1163  * @class Roo.bootstrap.Img
1164  * @extends Roo.bootstrap.Component
1165  * Bootstrap Img class
1166  * @cfg {Boolean} imgResponsive false | true
1167  * @cfg {String} border rounded | circle | thumbnail
1168  * @cfg {String} src image source
1169  * @cfg {String} alt image alternative text
1170  * @cfg {String} href a tag href
1171  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1172  * 
1173  * @constructor
1174  * Create a new Input
1175  * @param {Object} config The config object
1176  */
1177
1178 Roo.bootstrap.Img = function(config){
1179     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1180     
1181     this.addEvents({
1182         // img events
1183         /**
1184          * @event click
1185          * The img click event for the img.
1186          * @param {Roo.EventObject} e
1187          */
1188         "click" : true
1189     });
1190 };
1191
1192 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1193     
1194     imgResponsive: true,
1195     border: '',
1196     src: '',
1197     href: false,
1198     target: false,
1199
1200     getAutoCreate : function(){
1201         
1202         var cfg = {
1203             tag: 'img',
1204             cls: (this.imgResponsive) ? 'img-responsive' : '',
1205             html : null
1206         }
1207         
1208         cfg.html = this.html || cfg.html;
1209         
1210         cfg.src = this.src || cfg.src;
1211         
1212         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1213             cfg.cls += ' img-' + this.border;
1214         }
1215         
1216         if(this.alt){
1217             cfg.alt = this.alt;
1218         }
1219         
1220         if(this.href){
1221             var a = {
1222                 tag: 'a',
1223                 href: this.href,
1224                 cn: [
1225                     cfg
1226                 ]
1227             }
1228             
1229             if(this.target){
1230                 a.target = this.target;
1231             }
1232             
1233         }
1234         
1235         
1236         return (this.href) ? a : cfg;
1237     },
1238     
1239     initEvents: function() {
1240         
1241         if(!this.href){
1242             this.el.on('click', this.onClick, this);
1243         }
1244     },
1245     
1246     onClick : function(e)
1247     {
1248         Roo.log('img onclick');
1249         this.fireEvent('click', this, e);
1250     }
1251    
1252 });
1253
1254  /*
1255  * - LGPL
1256  *
1257  * image
1258  * 
1259  */
1260
1261
1262 /**
1263  * @class Roo.bootstrap.Link
1264  * @extends Roo.bootstrap.Component
1265  * Bootstrap Link Class
1266  * @cfg {String} alt image alternative text
1267  * @cfg {String} href a tag href
1268  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1269  * @cfg {String} html the content of the link.
1270  * @cfg {String} anchor name for the anchor link
1271
1272  * @cfg {Boolean} preventDefault (true | false) default false
1273
1274  * 
1275  * @constructor
1276  * Create a new Input
1277  * @param {Object} config The config object
1278  */
1279
1280 Roo.bootstrap.Link = function(config){
1281     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1282     
1283     this.addEvents({
1284         // img events
1285         /**
1286          * @event click
1287          * The img click event for the img.
1288          * @param {Roo.EventObject} e
1289          */
1290         "click" : true
1291     });
1292 };
1293
1294 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1295     
1296     href: false,
1297     target: false,
1298     preventDefault: false,
1299     anchor : false,
1300     alt : false,
1301
1302     getAutoCreate : function()
1303     {
1304         
1305         var cfg = {
1306             tag: 'a'
1307         };
1308         // anchor's do not require html/href...
1309         if (this.anchor === false) {
1310             cfg.html = this.html || 'html-missing';
1311             cfg.href = this.href || '#';
1312         } else {
1313             cfg.name = this.anchor;
1314             if (this.html !== false) {
1315                 cfg.html = this.html;
1316             }
1317             if (this.href !== false) {
1318                 cfg.href = this.href;
1319             }
1320         }
1321         
1322         if(this.alt !== false){
1323             cfg.alt = this.alt;
1324         }
1325         
1326         
1327         if(this.target !== false) {
1328             cfg.target = this.target;
1329         }
1330         
1331         return cfg;
1332     },
1333     
1334     initEvents: function() {
1335         
1336         if(!this.href || this.preventDefault){
1337             this.el.on('click', this.onClick, this);
1338         }
1339     },
1340     
1341     onClick : function(e)
1342     {
1343         if(this.preventDefault){
1344             e.preventDefault();
1345         }
1346         //Roo.log('img onclick');
1347         this.fireEvent('click', this, e);
1348     }
1349    
1350 });
1351
1352  /*
1353  * - LGPL
1354  *
1355  * header
1356  * 
1357  */
1358
1359 /**
1360  * @class Roo.bootstrap.Header
1361  * @extends Roo.bootstrap.Component
1362  * Bootstrap Header class
1363  * @cfg {String} html content of header
1364  * @cfg {Number} level (1|2|3|4|5|6) default 1
1365  * 
1366  * @constructor
1367  * Create a new Header
1368  * @param {Object} config The config object
1369  */
1370
1371
1372 Roo.bootstrap.Header  = function(config){
1373     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1374 };
1375
1376 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1377     
1378     //href : false,
1379     html : false,
1380     level : 1,
1381     
1382     
1383     
1384     getAutoCreate : function(){
1385         
1386         
1387         
1388         var cfg = {
1389             tag: 'h' + (1 *this.level),
1390             html: this.html || ''
1391         } ;
1392         
1393         return cfg;
1394     }
1395    
1396 });
1397
1398  
1399
1400  /*
1401  * Based on:
1402  * Ext JS Library 1.1.1
1403  * Copyright(c) 2006-2007, Ext JS, LLC.
1404  *
1405  * Originally Released Under LGPL - original licence link has changed is not relivant.
1406  *
1407  * Fork - LGPL
1408  * <script type="text/javascript">
1409  */
1410  
1411 /**
1412  * @class Roo.bootstrap.MenuMgr
1413  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1414  * @singleton
1415  */
1416 Roo.bootstrap.MenuMgr = function(){
1417    var menus, active, groups = {}, attached = false, lastShow = new Date();
1418
1419    // private - called when first menu is created
1420    function init(){
1421        menus = {};
1422        active = new Roo.util.MixedCollection();
1423        Roo.get(document).addKeyListener(27, function(){
1424            if(active.length > 0){
1425                hideAll();
1426            }
1427        });
1428    }
1429
1430    // private
1431    function hideAll(){
1432        if(active && active.length > 0){
1433            var c = active.clone();
1434            c.each(function(m){
1435                m.hide();
1436            });
1437        }
1438    }
1439
1440    // private
1441    function onHide(m){
1442        active.remove(m);
1443        if(active.length < 1){
1444            Roo.get(document).un("mouseup", onMouseDown);
1445             
1446            attached = false;
1447        }
1448    }
1449
1450    // private
1451    function onShow(m){
1452        var last = active.last();
1453        lastShow = new Date();
1454        active.add(m);
1455        if(!attached){
1456           Roo.get(document).on("mouseup", onMouseDown);
1457            
1458            attached = true;
1459        }
1460        if(m.parentMenu){
1461           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1462           m.parentMenu.activeChild = m;
1463        }else if(last && last.isVisible()){
1464           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1465        }
1466    }
1467
1468    // private
1469    function onBeforeHide(m){
1470        if(m.activeChild){
1471            m.activeChild.hide();
1472        }
1473        if(m.autoHideTimer){
1474            clearTimeout(m.autoHideTimer);
1475            delete m.autoHideTimer;
1476        }
1477    }
1478
1479    // private
1480    function onBeforeShow(m){
1481        var pm = m.parentMenu;
1482        if(!pm && !m.allowOtherMenus){
1483            hideAll();
1484        }else if(pm && pm.activeChild && active != m){
1485            pm.activeChild.hide();
1486        }
1487    }
1488
1489    // private
1490    function onMouseDown(e){
1491         Roo.log("on MouseDown");
1492         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1493            hideAll();
1494         }
1495         
1496         
1497    }
1498
1499    // private
1500    function onBeforeCheck(mi, state){
1501        if(state){
1502            var g = groups[mi.group];
1503            for(var i = 0, l = g.length; i < l; i++){
1504                if(g[i] != mi){
1505                    g[i].setChecked(false);
1506                }
1507            }
1508        }
1509    }
1510
1511    return {
1512
1513        /**
1514         * Hides all menus that are currently visible
1515         */
1516        hideAll : function(){
1517             hideAll();  
1518        },
1519
1520        // private
1521        register : function(menu){
1522            if(!menus){
1523                init();
1524            }
1525            menus[menu.id] = menu;
1526            menu.on("beforehide", onBeforeHide);
1527            menu.on("hide", onHide);
1528            menu.on("beforeshow", onBeforeShow);
1529            menu.on("show", onShow);
1530            var g = menu.group;
1531            if(g && menu.events["checkchange"]){
1532                if(!groups[g]){
1533                    groups[g] = [];
1534                }
1535                groups[g].push(menu);
1536                menu.on("checkchange", onCheck);
1537            }
1538        },
1539
1540         /**
1541          * Returns a {@link Roo.menu.Menu} object
1542          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1543          * be used to generate and return a new Menu instance.
1544          */
1545        get : function(menu){
1546            if(typeof menu == "string"){ // menu id
1547                return menus[menu];
1548            }else if(menu.events){  // menu instance
1549                return menu;
1550            }
1551            /*else if(typeof menu.length == 'number'){ // array of menu items?
1552                return new Roo.bootstrap.Menu({items:menu});
1553            }else{ // otherwise, must be a config
1554                return new Roo.bootstrap.Menu(menu);
1555            }
1556            */
1557            return false;
1558        },
1559
1560        // private
1561        unregister : function(menu){
1562            delete menus[menu.id];
1563            menu.un("beforehide", onBeforeHide);
1564            menu.un("hide", onHide);
1565            menu.un("beforeshow", onBeforeShow);
1566            menu.un("show", onShow);
1567            var g = menu.group;
1568            if(g && menu.events["checkchange"]){
1569                groups[g].remove(menu);
1570                menu.un("checkchange", onCheck);
1571            }
1572        },
1573
1574        // private
1575        registerCheckable : function(menuItem){
1576            var g = menuItem.group;
1577            if(g){
1578                if(!groups[g]){
1579                    groups[g] = [];
1580                }
1581                groups[g].push(menuItem);
1582                menuItem.on("beforecheckchange", onBeforeCheck);
1583            }
1584        },
1585
1586        // private
1587        unregisterCheckable : function(menuItem){
1588            var g = menuItem.group;
1589            if(g){
1590                groups[g].remove(menuItem);
1591                menuItem.un("beforecheckchange", onBeforeCheck);
1592            }
1593        }
1594    };
1595 }();/*
1596  * - LGPL
1597  *
1598  * menu
1599  * 
1600  */
1601
1602 /**
1603  * @class Roo.bootstrap.Menu
1604  * @extends Roo.bootstrap.Component
1605  * Bootstrap Menu class - container for MenuItems
1606  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1607  * 
1608  * @constructor
1609  * Create a new Menu
1610  * @param {Object} config The config object
1611  */
1612
1613
1614 Roo.bootstrap.Menu = function(config){
1615     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1616     if (this.registerMenu) {
1617         Roo.bootstrap.MenuMgr.register(this);
1618     }
1619     this.addEvents({
1620         /**
1621          * @event beforeshow
1622          * Fires before this menu is displayed
1623          * @param {Roo.menu.Menu} this
1624          */
1625         beforeshow : true,
1626         /**
1627          * @event beforehide
1628          * Fires before this menu is hidden
1629          * @param {Roo.menu.Menu} this
1630          */
1631         beforehide : true,
1632         /**
1633          * @event show
1634          * Fires after this menu is displayed
1635          * @param {Roo.menu.Menu} this
1636          */
1637         show : true,
1638         /**
1639          * @event hide
1640          * Fires after this menu is hidden
1641          * @param {Roo.menu.Menu} this
1642          */
1643         hide : true,
1644         /**
1645          * @event click
1646          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1647          * @param {Roo.menu.Menu} this
1648          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1649          * @param {Roo.EventObject} e
1650          */
1651         click : true,
1652         /**
1653          * @event mouseover
1654          * Fires when the mouse is hovering over this menu
1655          * @param {Roo.menu.Menu} this
1656          * @param {Roo.EventObject} e
1657          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1658          */
1659         mouseover : true,
1660         /**
1661          * @event mouseout
1662          * Fires when the mouse exits this menu
1663          * @param {Roo.menu.Menu} this
1664          * @param {Roo.EventObject} e
1665          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1666          */
1667         mouseout : true,
1668         /**
1669          * @event itemclick
1670          * Fires when a menu item contained in this menu is clicked
1671          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1672          * @param {Roo.EventObject} e
1673          */
1674         itemclick: true
1675     });
1676     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1677 };
1678
1679 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1680     
1681    /// html : false,
1682     //align : '',
1683     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1684     type: false,
1685     /**
1686      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1687      */
1688     registerMenu : true,
1689     
1690     menuItems :false, // stores the menu items..
1691     
1692     hidden:true,
1693     
1694     parentMenu : false,
1695     
1696     getChildContainer : function() {
1697         return this.el;  
1698     },
1699     
1700     getAutoCreate : function(){
1701          
1702         //if (['right'].indexOf(this.align)!==-1) {
1703         //    cfg.cn[1].cls += ' pull-right'
1704         //}
1705         
1706         
1707         var cfg = {
1708             tag : 'ul',
1709             cls : 'dropdown-menu' ,
1710             style : 'z-index:1000'
1711             
1712         }
1713         
1714         if (this.type === 'submenu') {
1715             cfg.cls = 'submenu active';
1716         }
1717         if (this.type === 'treeview') {
1718             cfg.cls = 'treeview-menu';
1719         }
1720         
1721         return cfg;
1722     },
1723     initEvents : function() {
1724         
1725        // Roo.log("ADD event");
1726        // Roo.log(this.triggerEl.dom);
1727         this.triggerEl.on('click', this.onTriggerPress, this);
1728         this.triggerEl.addClass('dropdown-toggle');
1729         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1730
1731         this.el.on("mouseover", this.onMouseOver, this);
1732         this.el.on("mouseout", this.onMouseOut, this);
1733         
1734         
1735     },
1736     findTargetItem : function(e){
1737         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1738         if(!t){
1739             return false;
1740         }
1741         //Roo.log(t);         Roo.log(t.id);
1742         if(t && t.id){
1743             //Roo.log(this.menuitems);
1744             return this.menuitems.get(t.id);
1745             
1746             //return this.items.get(t.menuItemId);
1747         }
1748         
1749         return false;
1750     },
1751     onClick : function(e){
1752         Roo.log("menu.onClick");
1753         var t = this.findTargetItem(e);
1754         if(!t || t.isContainer){
1755             return;
1756         }
1757         Roo.log(e);
1758         /*
1759         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1760             if(t == this.activeItem && t.shouldDeactivate(e)){
1761                 this.activeItem.deactivate();
1762                 delete this.activeItem;
1763                 return;
1764             }
1765             if(t.canActivate){
1766                 this.setActiveItem(t, true);
1767             }
1768             return;
1769             
1770             
1771         }
1772         */
1773        
1774         Roo.log('pass click event');
1775         
1776         t.onClick(e);
1777         
1778         this.fireEvent("click", this, t, e);
1779         
1780         this.hide();
1781     },
1782      onMouseOver : function(e){
1783         var t  = this.findTargetItem(e);
1784         //Roo.log(t);
1785         //if(t){
1786         //    if(t.canActivate && !t.disabled){
1787         //        this.setActiveItem(t, true);
1788         //    }
1789         //}
1790         
1791         this.fireEvent("mouseover", this, e, t);
1792     },
1793     isVisible : function(){
1794         return !this.hidden;
1795     },
1796      onMouseOut : function(e){
1797         var t  = this.findTargetItem(e);
1798         
1799         //if(t ){
1800         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1801         //        this.activeItem.deactivate();
1802         //        delete this.activeItem;
1803         //    }
1804         //}
1805         this.fireEvent("mouseout", this, e, t);
1806     },
1807     
1808     
1809     /**
1810      * Displays this menu relative to another element
1811      * @param {String/HTMLElement/Roo.Element} element The element to align to
1812      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1813      * the element (defaults to this.defaultAlign)
1814      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1815      */
1816     show : function(el, pos, parentMenu){
1817         this.parentMenu = parentMenu;
1818         if(!this.el){
1819             this.render();
1820         }
1821         this.fireEvent("beforeshow", this);
1822         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1823     },
1824      /**
1825      * Displays this menu at a specific xy position
1826      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1827      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1828      */
1829     showAt : function(xy, parentMenu, /* private: */_e){
1830         this.parentMenu = parentMenu;
1831         if(!this.el){
1832             this.render();
1833         }
1834         if(_e !== false){
1835             this.fireEvent("beforeshow", this);
1836             
1837             //xy = this.el.adjustForConstraints(xy);
1838         }
1839         //this.el.setXY(xy);
1840         //this.el.show();
1841         this.hideMenuItems();
1842         this.hidden = false;
1843         this.triggerEl.addClass('open');
1844         this.focus();
1845         this.fireEvent("show", this);
1846     },
1847     
1848     focus : function(){
1849         return;
1850         if(!this.hidden){
1851             this.doFocus.defer(50, this);
1852         }
1853     },
1854
1855     doFocus : function(){
1856         if(!this.hidden){
1857             this.focusEl.focus();
1858         }
1859     },
1860
1861     /**
1862      * Hides this menu and optionally all parent menus
1863      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1864      */
1865     hide : function(deep){
1866         
1867         this.hideMenuItems();
1868         if(this.el && this.isVisible()){
1869             this.fireEvent("beforehide", this);
1870             if(this.activeItem){
1871                 this.activeItem.deactivate();
1872                 this.activeItem = null;
1873             }
1874             this.triggerEl.removeClass('open');;
1875             this.hidden = true;
1876             this.fireEvent("hide", this);
1877         }
1878         if(deep === true && this.parentMenu){
1879             this.parentMenu.hide(true);
1880         }
1881     },
1882     
1883     onTriggerPress  : function(e)
1884     {
1885         
1886         Roo.log('trigger press');
1887         //Roo.log(e.getTarget());
1888        // Roo.log(this.triggerEl.dom);
1889         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1890             return;
1891         }
1892         if (this.isVisible()) {
1893             Roo.log('hide');
1894             this.hide();
1895         } else {
1896             this.show(this.triggerEl, false, false);
1897         }
1898         
1899         
1900     },
1901     
1902          
1903        
1904     
1905     hideMenuItems : function()
1906     {
1907         //$(backdrop).remove()
1908         Roo.select('.open',true).each(function(aa) {
1909             
1910             aa.removeClass('open');
1911           //var parent = getParent($(this))
1912           //var relatedTarget = { relatedTarget: this }
1913           
1914            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1915           //if (e.isDefaultPrevented()) return
1916            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1917         })
1918     },
1919     addxtypeChild : function (tree, cntr) {
1920         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1921           
1922         this.menuitems.add(comp);
1923         return comp;
1924
1925     },
1926     getEl : function()
1927     {
1928         Roo.log(this.el);
1929         return this.el;
1930     }
1931 });
1932
1933  
1934  /*
1935  * - LGPL
1936  *
1937  * menu item
1938  * 
1939  */
1940
1941
1942 /**
1943  * @class Roo.bootstrap.MenuItem
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap MenuItem class
1946  * @cfg {String} html the menu label
1947  * @cfg {String} href the link
1948  * @cfg {Boolean} preventDefault (true | false) default true
1949  * @cfg {Boolean} isContainer (true | false) default false
1950  * 
1951  * 
1952  * @constructor
1953  * Create a new MenuItem
1954  * @param {Object} config The config object
1955  */
1956
1957
1958 Roo.bootstrap.MenuItem = function(config){
1959     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1960     this.addEvents({
1961         // raw events
1962         /**
1963          * @event click
1964          * The raw click event for the entire grid.
1965          * @param {Roo.EventObject} e
1966          */
1967         "click" : true
1968     });
1969 };
1970
1971 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1972     
1973     href : false,
1974     html : false,
1975     preventDefault: true,
1976     isContainer : false,
1977     
1978     getAutoCreate : function(){
1979         
1980         if(this.isContainer){
1981             return {
1982                 tag: 'li',
1983                 cls: 'dropdown-menu-item'
1984             };
1985         }
1986         
1987         var cfg= {
1988             tag: 'li',
1989             cls: 'dropdown-menu-item',
1990             cn: [
1991                     {
1992                         tag : 'a',
1993                         href : '#',
1994                         html : 'Link'
1995                     }
1996                 ]
1997         };
1998         if (this.parent().type == 'treeview') {
1999             cfg.cls = 'treeview-menu';
2000         }
2001         
2002         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2003         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2004         return cfg;
2005     },
2006     
2007     initEvents: function() {
2008         
2009         //this.el.select('a').on('click', this.onClick, this);
2010         
2011     },
2012     onClick : function(e)
2013     {
2014         Roo.log('item on click ');
2015         //if(this.preventDefault){
2016         //    e.preventDefault();
2017         //}
2018         //this.parent().hideMenuItems();
2019         
2020         this.fireEvent('click', this, e);
2021     },
2022     getEl : function()
2023     {
2024         return this.el;
2025     }
2026 });
2027
2028  
2029
2030  /*
2031  * - LGPL
2032  *
2033  * menu separator
2034  * 
2035  */
2036
2037
2038 /**
2039  * @class Roo.bootstrap.MenuSeparator
2040  * @extends Roo.bootstrap.Component
2041  * Bootstrap MenuSeparator class
2042  * 
2043  * @constructor
2044  * Create a new MenuItem
2045  * @param {Object} config The config object
2046  */
2047
2048
2049 Roo.bootstrap.MenuSeparator = function(config){
2050     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2051 };
2052
2053 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2054     
2055     getAutoCreate : function(){
2056         var cfg = {
2057             cls: 'divider',
2058             tag : 'li'
2059         };
2060         
2061         return cfg;
2062     }
2063    
2064 });
2065
2066  
2067
2068  
2069 /*
2070 * Licence: LGPL
2071 */
2072
2073 /**
2074  * @class Roo.bootstrap.Modal
2075  * @extends Roo.bootstrap.Component
2076  * Bootstrap Modal class
2077  * @cfg {String} title Title of dialog
2078  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2079  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2080  * @cfg {Boolean} specificTitle default false
2081  * @cfg {Array} buttons Array of buttons or standard button set..
2082  * @cfg {String} buttonPosition (left|right|center) default right
2083  * @cfg {Boolean} animate default true
2084  * @cfg {Boolean} allow_close default true
2085  * 
2086  * @constructor
2087  * Create a new Modal Dialog
2088  * @param {Object} config The config object
2089  */
2090
2091 Roo.bootstrap.Modal = function(config){
2092     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2093     this.addEvents({
2094         // raw events
2095         /**
2096          * @event btnclick
2097          * The raw btnclick event for the button
2098          * @param {Roo.EventObject} e
2099          */
2100         "btnclick" : true
2101     });
2102     this.buttons = this.buttons || [];
2103      
2104     if (this.tmpl) {
2105         this.tmpl = Roo.factory(this.tmpl);
2106     }
2107     
2108 };
2109
2110 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2111     
2112     title : 'test dialog',
2113    
2114     buttons : false,
2115     
2116     // set on load...
2117      
2118     html: false,
2119     
2120     tmp: false,
2121     
2122     specificTitle: false,
2123     
2124     buttonPosition: 'right',
2125     
2126     allow_close : true,
2127     
2128     animate : true,
2129     
2130     
2131      // private
2132     bodyEl:  false,
2133     footerEl:  false,
2134     titleEl:  false,
2135     closeEl:  false,
2136     
2137     
2138     onRender : function(ct, position)
2139     {
2140         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2141      
2142         if(!this.el){
2143             var cfg = Roo.apply({},  this.getAutoCreate());
2144             cfg.id = Roo.id();
2145             //if(!cfg.name){
2146             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2147             //}
2148             //if (!cfg.name.length) {
2149             //    delete cfg.name;
2150            // }
2151             if (this.cls) {
2152                 cfg.cls += ' ' + this.cls;
2153             }
2154             if (this.style) {
2155                 cfg.style = this.style;
2156             }
2157             this.el = Roo.get(document.body).createChild(cfg, position);
2158         }
2159         //var type = this.el.dom.type;
2160         
2161         
2162         
2163         
2164         if(this.tabIndex !== undefined){
2165             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2166         }
2167         
2168         
2169         this.bodyEl = this.el.select('.modal-body',true).first();
2170         this.closeEl = this.el.select('.modal-header .close', true).first();
2171         this.footerEl = this.el.select('.modal-footer',true).first();
2172         this.titleEl = this.el.select('.modal-title',true).first();
2173         
2174         
2175          
2176         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2177         this.maskEl.enableDisplayMode("block");
2178         this.maskEl.hide();
2179         //this.el.addClass("x-dlg-modal");
2180     
2181         if (this.buttons.length) {
2182             Roo.each(this.buttons, function(bb) {
2183                 b = Roo.apply({}, bb);
2184                 b.xns = b.xns || Roo.bootstrap;
2185                 b.xtype = b.xtype || 'Button';
2186                 if (typeof(b.listeners) == 'undefined') {
2187                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2188                 }
2189                 
2190                 var btn = Roo.factory(b);
2191                 
2192                 btn.onRender(this.el.select('.modal-footer div').first());
2193                 
2194             },this);
2195         }
2196         // render the children.
2197         var nitems = [];
2198         
2199         if(typeof(this.items) != 'undefined'){
2200             var items = this.items;
2201             delete this.items;
2202
2203             for(var i =0;i < items.length;i++) {
2204                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2205             }
2206         }
2207         
2208         this.items = nitems;
2209         
2210         // where are these used - they used to be body/close/footer
2211         
2212        
2213         this.initEvents();
2214         //this.el.addClass([this.fieldClass, this.cls]);
2215         
2216     },
2217     getAutoCreate : function(){
2218         
2219         
2220         var bdy = {
2221                 cls : 'modal-body',
2222                 html : this.html || ''
2223         };
2224         
2225         var title = {
2226             tag: 'h4',
2227             cls : 'modal-title',
2228             html : this.title
2229         };
2230         
2231         if(this.specificTitle){
2232             title = this.title;
2233             
2234         };
2235         
2236         var header = [];
2237         if (this.allow_close) {
2238             header.push({
2239                 tag: 'button',
2240                 cls : 'close',
2241                 html : '&times'
2242             });
2243         }
2244         header.push(title);
2245         
2246         var modal = {
2247             cls: "modal",
2248             style : 'display: none',
2249             cn : [
2250                 {
2251                     cls: "modal-dialog",
2252                     cn : [
2253                         {
2254                             cls : "modal-content",
2255                             cn : [
2256                                 {
2257                                     cls : 'modal-header',
2258                                     cn : header
2259                                 },
2260                                 bdy,
2261                                 {
2262                                     cls : 'modal-footer',
2263                                     cn : [
2264                                         {
2265                                             tag: 'div',
2266                                             cls: 'btn-' + this.buttonPosition
2267                                         }
2268                                     ]
2269                                     
2270                                 }
2271                                 
2272                                 
2273                             ]
2274                             
2275                         }
2276                     ]
2277                         
2278                 }
2279             ]
2280         };
2281         
2282         if(this.animate){
2283             modal.cls += ' fade';
2284         }
2285         
2286         return modal;
2287           
2288     },
2289     getChildContainer : function() {
2290          
2291          return this.bodyEl;
2292         
2293     },
2294     getButtonContainer : function() {
2295          return this.el.select('.modal-footer div',true).first();
2296         
2297     },
2298     initEvents : function()
2299     {
2300         if (this.allow_close) {
2301             this.closeEl.on('click', this.hide, this);
2302         }
2303
2304     },
2305     show : function() {
2306         
2307         if (!this.rendered) {
2308             this.render();
2309         }
2310         
2311         this.el.setStyle('display', 'block');
2312         
2313         if(this.animate){
2314             var _this = this;
2315             (function(){ _this.el.addClass('in'); }).defer(50);
2316         }else{
2317             this.el.addClass('in');
2318         }
2319         
2320         // not sure how we can show data in here.. 
2321         //if (this.tmpl) {
2322         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2323         //}
2324         
2325         Roo.get(document.body).addClass("x-body-masked");
2326         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2327         this.maskEl.show();
2328         this.el.setStyle('zIndex', '10001');
2329        
2330         this.fireEvent('show', this);
2331         
2332         
2333     },
2334     hide : function()
2335     {
2336         this.maskEl.hide();
2337         Roo.get(document.body).removeClass("x-body-masked");
2338         this.el.removeClass('in');
2339         
2340         if(this.animate){
2341             var _this = this;
2342             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2343         }else{
2344             this.el.setStyle('display', 'none');
2345         }
2346         
2347         this.fireEvent('hide', this);
2348     },
2349     
2350     addButton : function(str, cb)
2351     {
2352          
2353         
2354         var b = Roo.apply({}, { html : str } );
2355         b.xns = b.xns || Roo.bootstrap;
2356         b.xtype = b.xtype || 'Button';
2357         if (typeof(b.listeners) == 'undefined') {
2358             b.listeners = { click : cb.createDelegate(this)  };
2359         }
2360         
2361         var btn = Roo.factory(b);
2362            
2363         btn.onRender(this.el.select('.modal-footer div').first());
2364         
2365         return btn;   
2366        
2367     },
2368     
2369     setDefaultButton : function(btn)
2370     {
2371         //this.el.select('.modal-footer').()
2372     },
2373     resizeTo: function(w,h)
2374     {
2375         // skip..
2376     },
2377     setContentSize  : function(w, h)
2378     {
2379         
2380     },
2381     onButtonClick: function(btn,e)
2382     {
2383         //Roo.log([a,b,c]);
2384         this.fireEvent('btnclick', btn.name, e);
2385     },
2386      /**
2387      * Set the title of the Dialog
2388      * @param {String} str new Title
2389      */
2390     setTitle: function(str) {
2391         this.titleEl.dom.innerHTML = str;    
2392     },
2393     /**
2394      * Set the body of the Dialog
2395      * @param {String} str new Title
2396      */
2397     setBody: function(str) {
2398         this.bodyEl.dom.innerHTML = str;    
2399     },
2400     /**
2401      * Set the body of the Dialog using the template
2402      * @param {Obj} data - apply this data to the template and replace the body contents.
2403      */
2404     applyBody: function(obj)
2405     {
2406         if (!this.tmpl) {
2407             Roo.log("Error - using apply Body without a template");
2408             //code
2409         }
2410         this.tmpl.overwrite(this.bodyEl, obj);
2411     }
2412     
2413 });
2414
2415
2416 Roo.apply(Roo.bootstrap.Modal,  {
2417     /**
2418          * Button config that displays a single OK button
2419          * @type Object
2420          */
2421         OK :  [{
2422             name : 'ok',
2423             weight : 'primary',
2424             html : 'OK'
2425         }], 
2426         /**
2427          * Button config that displays Yes and No buttons
2428          * @type Object
2429          */
2430         YESNO : [
2431             {
2432                 name  : 'no',
2433                 html : 'No'
2434             },
2435             {
2436                 name  :'yes',
2437                 weight : 'primary',
2438                 html : 'Yes'
2439             }
2440         ],
2441         
2442         /**
2443          * Button config that displays OK and Cancel buttons
2444          * @type Object
2445          */
2446         OKCANCEL : [
2447             {
2448                name : 'cancel',
2449                 html : 'Cancel'
2450             },
2451             {
2452                 name : 'ok',
2453                 weight : 'primary',
2454                 html : 'OK'
2455             }
2456         ],
2457         /**
2458          * Button config that displays Yes, No and Cancel buttons
2459          * @type Object
2460          */
2461         YESNOCANCEL : [
2462             {
2463                 name : 'yes',
2464                 weight : 'primary',
2465                 html : 'Yes'
2466             },
2467             {
2468                 name : 'no',
2469                 html : 'No'
2470             },
2471             {
2472                 name : 'cancel',
2473                 html : 'Cancel'
2474             }
2475         ]
2476 });
2477  
2478  /*
2479  * - LGPL
2480  *
2481  * messagebox - can be used as a replace
2482  * 
2483  */
2484 /**
2485  * @class Roo.MessageBox
2486  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2487  * Example usage:
2488  *<pre><code>
2489 // Basic alert:
2490 Roo.Msg.alert('Status', 'Changes saved successfully.');
2491
2492 // Prompt for user data:
2493 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2494     if (btn == 'ok'){
2495         // process text value...
2496     }
2497 });
2498
2499 // Show a dialog using config options:
2500 Roo.Msg.show({
2501    title:'Save Changes?',
2502    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2503    buttons: Roo.Msg.YESNOCANCEL,
2504    fn: processResult,
2505    animEl: 'elId'
2506 });
2507 </code></pre>
2508  * @singleton
2509  */
2510 Roo.bootstrap.MessageBox = function(){
2511     var dlg, opt, mask, waitTimer;
2512     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2513     var buttons, activeTextEl, bwidth;
2514
2515     
2516     // private
2517     var handleButton = function(button){
2518         dlg.hide();
2519         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2520     };
2521
2522     // private
2523     var handleHide = function(){
2524         if(opt && opt.cls){
2525             dlg.el.removeClass(opt.cls);
2526         }
2527         //if(waitTimer){
2528         //    Roo.TaskMgr.stop(waitTimer);
2529         //    waitTimer = null;
2530         //}
2531     };
2532
2533     // private
2534     var updateButtons = function(b){
2535         var width = 0;
2536         if(!b){
2537             buttons["ok"].hide();
2538             buttons["cancel"].hide();
2539             buttons["yes"].hide();
2540             buttons["no"].hide();
2541             //dlg.footer.dom.style.display = 'none';
2542             return width;
2543         }
2544         dlg.footerEl.dom.style.display = '';
2545         for(var k in buttons){
2546             if(typeof buttons[k] != "function"){
2547                 if(b[k]){
2548                     buttons[k].show();
2549                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2550                     width += buttons[k].el.getWidth()+15;
2551                 }else{
2552                     buttons[k].hide();
2553                 }
2554             }
2555         }
2556         return width;
2557     };
2558
2559     // private
2560     var handleEsc = function(d, k, e){
2561         if(opt && opt.closable !== false){
2562             dlg.hide();
2563         }
2564         if(e){
2565             e.stopEvent();
2566         }
2567     };
2568
2569     return {
2570         /**
2571          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2572          * @return {Roo.BasicDialog} The BasicDialog element
2573          */
2574         getDialog : function(){
2575            if(!dlg){
2576                 dlg = new Roo.bootstrap.Modal( {
2577                     //draggable: true,
2578                     //resizable:false,
2579                     //constraintoviewport:false,
2580                     //fixedcenter:true,
2581                     //collapsible : false,
2582                     //shim:true,
2583                     //modal: true,
2584                   //  width:400,
2585                   //  height:100,
2586                     //buttonAlign:"center",
2587                     closeClick : function(){
2588                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2589                             handleButton("no");
2590                         }else{
2591                             handleButton("cancel");
2592                         }
2593                     }
2594                 });
2595                 dlg.render();
2596                 dlg.on("hide", handleHide);
2597                 mask = dlg.mask;
2598                 //dlg.addKeyListener(27, handleEsc);
2599                 buttons = {};
2600                 this.buttons = buttons;
2601                 var bt = this.buttonText;
2602                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2603                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2604                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2605                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2606                 Roo.log(buttons)
2607                 bodyEl = dlg.bodyEl.createChild({
2608
2609                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2610                         '<textarea class="roo-mb-textarea"></textarea>' +
2611                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2612                 });
2613                 msgEl = bodyEl.dom.firstChild;
2614                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2615                 textboxEl.enableDisplayMode();
2616                 textboxEl.addKeyListener([10,13], function(){
2617                     if(dlg.isVisible() && opt && opt.buttons){
2618                         if(opt.buttons.ok){
2619                             handleButton("ok");
2620                         }else if(opt.buttons.yes){
2621                             handleButton("yes");
2622                         }
2623                     }
2624                 });
2625                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2626                 textareaEl.enableDisplayMode();
2627                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2628                 progressEl.enableDisplayMode();
2629                 var pf = progressEl.dom.firstChild;
2630                 if (pf) {
2631                     pp = Roo.get(pf.firstChild);
2632                     pp.setHeight(pf.offsetHeight);
2633                 }
2634                 
2635             }
2636             return dlg;
2637         },
2638
2639         /**
2640          * Updates the message box body text
2641          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2642          * the XHTML-compliant non-breaking space character '&amp;#160;')
2643          * @return {Roo.MessageBox} This message box
2644          */
2645         updateText : function(text){
2646             if(!dlg.isVisible() && !opt.width){
2647                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2648             }
2649             msgEl.innerHTML = text || '&#160;';
2650       
2651             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2652             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2653             var w = Math.max(
2654                     Math.min(opt.width || cw , this.maxWidth), 
2655                     Math.max(opt.minWidth || this.minWidth, bwidth)
2656             );
2657             if(opt.prompt){
2658                 activeTextEl.setWidth(w);
2659             }
2660             if(dlg.isVisible()){
2661                 dlg.fixedcenter = false;
2662             }
2663             // to big, make it scroll. = But as usual stupid IE does not support
2664             // !important..
2665             
2666             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2667                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2668                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2669             } else {
2670                 bodyEl.dom.style.height = '';
2671                 bodyEl.dom.style.overflowY = '';
2672             }
2673             if (cw > w) {
2674                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2675             } else {
2676                 bodyEl.dom.style.overflowX = '';
2677             }
2678             
2679             dlg.setContentSize(w, bodyEl.getHeight());
2680             if(dlg.isVisible()){
2681                 dlg.fixedcenter = true;
2682             }
2683             return this;
2684         },
2685
2686         /**
2687          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2688          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2689          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2690          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2691          * @return {Roo.MessageBox} This message box
2692          */
2693         updateProgress : function(value, text){
2694             if(text){
2695                 this.updateText(text);
2696             }
2697             if (pp) { // weird bug on my firefox - for some reason this is not defined
2698                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2699             }
2700             return this;
2701         },        
2702
2703         /**
2704          * Returns true if the message box is currently displayed
2705          * @return {Boolean} True if the message box is visible, else false
2706          */
2707         isVisible : function(){
2708             return dlg && dlg.isVisible();  
2709         },
2710
2711         /**
2712          * Hides the message box if it is displayed
2713          */
2714         hide : function(){
2715             if(this.isVisible()){
2716                 dlg.hide();
2717             }  
2718         },
2719
2720         /**
2721          * Displays a new message box, or reinitializes an existing message box, based on the config options
2722          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2723          * The following config object properties are supported:
2724          * <pre>
2725 Property    Type             Description
2726 ----------  ---------------  ------------------------------------------------------------------------------------
2727 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2728                                    closes (defaults to undefined)
2729 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2730                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2731 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2732                                    progress and wait dialogs will ignore this property and always hide the
2733                                    close button as they can only be closed programmatically.
2734 cls               String           A custom CSS class to apply to the message box element
2735 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2736                                    displayed (defaults to 75)
2737 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2738                                    function will be btn (the name of the button that was clicked, if applicable,
2739                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2740                                    Progress and wait dialogs will ignore this option since they do not respond to
2741                                    user actions and can only be closed programmatically, so any required function
2742                                    should be called by the same code after it closes the dialog.
2743 icon              String           A CSS class that provides a background image to be used as an icon for
2744                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2745 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2746 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2747 modal             Boolean          False to allow user interaction with the page while the message box is
2748                                    displayed (defaults to true)
2749 msg               String           A string that will replace the existing message box body text (defaults
2750                                    to the XHTML-compliant non-breaking space character '&#160;')
2751 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2752 progress          Boolean          True to display a progress bar (defaults to false)
2753 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2754 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2755 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2756 title             String           The title text
2757 value             String           The string value to set into the active textbox element if displayed
2758 wait              Boolean          True to display a progress bar (defaults to false)
2759 width             Number           The width of the dialog in pixels
2760 </pre>
2761          *
2762          * Example usage:
2763          * <pre><code>
2764 Roo.Msg.show({
2765    title: 'Address',
2766    msg: 'Please enter your address:',
2767    width: 300,
2768    buttons: Roo.MessageBox.OKCANCEL,
2769    multiline: true,
2770    fn: saveAddress,
2771    animEl: 'addAddressBtn'
2772 });
2773 </code></pre>
2774          * @param {Object} config Configuration options
2775          * @return {Roo.MessageBox} This message box
2776          */
2777         show : function(options)
2778         {
2779             
2780             // this causes nightmares if you show one dialog after another
2781             // especially on callbacks..
2782              
2783             if(this.isVisible()){
2784                 
2785                 this.hide();
2786                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2787                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2788                 Roo.log("New Dialog Message:" +  options.msg )
2789                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2790                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2791                 
2792             }
2793             var d = this.getDialog();
2794             opt = options;
2795             d.setTitle(opt.title || "&#160;");
2796             d.closeEl.setDisplayed(opt.closable !== false);
2797             activeTextEl = textboxEl;
2798             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2799             if(opt.prompt){
2800                 if(opt.multiline){
2801                     textboxEl.hide();
2802                     textareaEl.show();
2803                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2804                         opt.multiline : this.defaultTextHeight);
2805                     activeTextEl = textareaEl;
2806                 }else{
2807                     textboxEl.show();
2808                     textareaEl.hide();
2809                 }
2810             }else{
2811                 textboxEl.hide();
2812                 textareaEl.hide();
2813             }
2814             progressEl.setDisplayed(opt.progress === true);
2815             this.updateProgress(0);
2816             activeTextEl.dom.value = opt.value || "";
2817             if(opt.prompt){
2818                 dlg.setDefaultButton(activeTextEl);
2819             }else{
2820                 var bs = opt.buttons;
2821                 var db = null;
2822                 if(bs && bs.ok){
2823                     db = buttons["ok"];
2824                 }else if(bs && bs.yes){
2825                     db = buttons["yes"];
2826                 }
2827                 dlg.setDefaultButton(db);
2828             }
2829             bwidth = updateButtons(opt.buttons);
2830             this.updateText(opt.msg);
2831             if(opt.cls){
2832                 d.el.addClass(opt.cls);
2833             }
2834             d.proxyDrag = opt.proxyDrag === true;
2835             d.modal = opt.modal !== false;
2836             d.mask = opt.modal !== false ? mask : false;
2837             if(!d.isVisible()){
2838                 // force it to the end of the z-index stack so it gets a cursor in FF
2839                 document.body.appendChild(dlg.el.dom);
2840                 d.animateTarget = null;
2841                 d.show(options.animEl);
2842             }
2843             return this;
2844         },
2845
2846         /**
2847          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2848          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2849          * and closing the message box when the process is complete.
2850          * @param {String} title The title bar text
2851          * @param {String} msg The message box body text
2852          * @return {Roo.MessageBox} This message box
2853          */
2854         progress : function(title, msg){
2855             this.show({
2856                 title : title,
2857                 msg : msg,
2858                 buttons: false,
2859                 progress:true,
2860                 closable:false,
2861                 minWidth: this.minProgressWidth,
2862                 modal : true
2863             });
2864             return this;
2865         },
2866
2867         /**
2868          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2869          * If a callback function is passed it will be called after the user clicks the button, and the
2870          * id of the button that was clicked will be passed as the only parameter to the callback
2871          * (could also be the top-right close button).
2872          * @param {String} title The title bar text
2873          * @param {String} msg The message box body text
2874          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2875          * @param {Object} scope (optional) The scope of the callback function
2876          * @return {Roo.MessageBox} This message box
2877          */
2878         alert : function(title, msg, fn, scope){
2879             this.show({
2880                 title : title,
2881                 msg : msg,
2882                 buttons: this.OK,
2883                 fn: fn,
2884                 scope : scope,
2885                 modal : true
2886             });
2887             return this;
2888         },
2889
2890         /**
2891          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2892          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2893          * You are responsible for closing the message box when the process is complete.
2894          * @param {String} msg The message box body text
2895          * @param {String} title (optional) The title bar text
2896          * @return {Roo.MessageBox} This message box
2897          */
2898         wait : function(msg, title){
2899             this.show({
2900                 title : title,
2901                 msg : msg,
2902                 buttons: false,
2903                 closable:false,
2904                 progress:true,
2905                 modal:true,
2906                 width:300,
2907                 wait:true
2908             });
2909             waitTimer = Roo.TaskMgr.start({
2910                 run: function(i){
2911                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2912                 },
2913                 interval: 1000
2914             });
2915             return this;
2916         },
2917
2918         /**
2919          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2920          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2921          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2922          * @param {String} title The title bar text
2923          * @param {String} msg The message box body text
2924          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2925          * @param {Object} scope (optional) The scope of the callback function
2926          * @return {Roo.MessageBox} This message box
2927          */
2928         confirm : function(title, msg, fn, scope){
2929             this.show({
2930                 title : title,
2931                 msg : msg,
2932                 buttons: this.YESNO,
2933                 fn: fn,
2934                 scope : scope,
2935                 modal : true
2936             });
2937             return this;
2938         },
2939
2940         /**
2941          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2942          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2943          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2944          * (could also be the top-right close button) and the text that was entered will be passed as the two
2945          * parameters to the callback.
2946          * @param {String} title The title bar text
2947          * @param {String} msg The message box body text
2948          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2949          * @param {Object} scope (optional) The scope of the callback function
2950          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2951          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2952          * @return {Roo.MessageBox} This message box
2953          */
2954         prompt : function(title, msg, fn, scope, multiline){
2955             this.show({
2956                 title : title,
2957                 msg : msg,
2958                 buttons: this.OKCANCEL,
2959                 fn: fn,
2960                 minWidth:250,
2961                 scope : scope,
2962                 prompt:true,
2963                 multiline: multiline,
2964                 modal : true
2965             });
2966             return this;
2967         },
2968
2969         /**
2970          * Button config that displays a single OK button
2971          * @type Object
2972          */
2973         OK : {ok:true},
2974         /**
2975          * Button config that displays Yes and No buttons
2976          * @type Object
2977          */
2978         YESNO : {yes:true, no:true},
2979         /**
2980          * Button config that displays OK and Cancel buttons
2981          * @type Object
2982          */
2983         OKCANCEL : {ok:true, cancel:true},
2984         /**
2985          * Button config that displays Yes, No and Cancel buttons
2986          * @type Object
2987          */
2988         YESNOCANCEL : {yes:true, no:true, cancel:true},
2989
2990         /**
2991          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2992          * @type Number
2993          */
2994         defaultTextHeight : 75,
2995         /**
2996          * The maximum width in pixels of the message box (defaults to 600)
2997          * @type Number
2998          */
2999         maxWidth : 600,
3000         /**
3001          * The minimum width in pixels of the message box (defaults to 100)
3002          * @type Number
3003          */
3004         minWidth : 100,
3005         /**
3006          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3007          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3008          * @type Number
3009          */
3010         minProgressWidth : 250,
3011         /**
3012          * An object containing the default button text strings that can be overriden for localized language support.
3013          * Supported properties are: ok, cancel, yes and no.
3014          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3015          * @type Object
3016          */
3017         buttonText : {
3018             ok : "OK",
3019             cancel : "Cancel",
3020             yes : "Yes",
3021             no : "No"
3022         }
3023     };
3024 }();
3025
3026 /**
3027  * Shorthand for {@link Roo.MessageBox}
3028  */
3029 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3030 Roo.Msg = Roo.Msg || Roo.MessageBox;
3031 /*
3032  * - LGPL
3033  *
3034  * navbar
3035  * 
3036  */
3037
3038 /**
3039  * @class Roo.bootstrap.Navbar
3040  * @extends Roo.bootstrap.Component
3041  * Bootstrap Navbar class
3042
3043  * @constructor
3044  * Create a new Navbar
3045  * @param {Object} config The config object
3046  */
3047
3048
3049 Roo.bootstrap.Navbar = function(config){
3050     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3051     
3052 };
3053
3054 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3055     
3056     
3057    
3058     // private
3059     navItems : false,
3060     loadMask : false,
3061     
3062     
3063     getAutoCreate : function(){
3064         
3065         
3066         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3067         
3068     },
3069     
3070     initEvents :function ()
3071     {
3072         //Roo.log(this.el.select('.navbar-toggle',true));
3073         this.el.select('.navbar-toggle',true).on('click', function() {
3074            // Roo.log('click');
3075             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3076         }, this);
3077         
3078         var mark = {
3079             tag: "div",
3080             cls:"x-dlg-mask"
3081         }
3082         
3083         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3084         
3085         var size = this.el.getSize();
3086         this.maskEl.setSize(size.width, size.height);
3087         this.maskEl.enableDisplayMode("block");
3088         this.maskEl.hide();
3089         
3090         if(this.loadMask){
3091             this.maskEl.show();
3092         }
3093     },
3094     
3095     
3096     getChildContainer : function()
3097     {
3098         if (this.el.select('.collapse').getCount()) {
3099             return this.el.select('.collapse',true).first();
3100         }
3101         
3102         return this.el;
3103     },
3104     
3105     mask : function()
3106     {
3107         this.maskEl.show();
3108     },
3109     
3110     unmask : function()
3111     {
3112         this.maskEl.hide();
3113     } 
3114     
3115     
3116     
3117     
3118 });
3119
3120
3121
3122  
3123
3124  /*
3125  * - LGPL
3126  *
3127  * navbar
3128  * 
3129  */
3130
3131 /**
3132  * @class Roo.bootstrap.NavSimplebar
3133  * @extends Roo.bootstrap.Navbar
3134  * Bootstrap Sidebar class
3135  *
3136  * @cfg {Boolean} inverse is inverted color
3137  * 
3138  * @cfg {String} type (nav | pills | tabs)
3139  * @cfg {Boolean} arrangement stacked | justified
3140  * @cfg {String} align (left | right) alignment
3141  * 
3142  * @cfg {Boolean} main (true|false) main nav bar? default false
3143  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3144  * 
3145  * @cfg {String} tag (header|footer|nav|div) default is nav 
3146
3147  * 
3148  * 
3149  * 
3150  * @constructor
3151  * Create a new Sidebar
3152  * @param {Object} config The config object
3153  */
3154
3155
3156 Roo.bootstrap.NavSimplebar = function(config){
3157     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3158 };
3159
3160 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3161     
3162     inverse: false,
3163     
3164     type: false,
3165     arrangement: '',
3166     align : false,
3167     
3168     
3169     
3170     main : false,
3171     
3172     
3173     tag : false,
3174     
3175     
3176     getAutoCreate : function(){
3177         
3178         
3179         var cfg = {
3180             tag : this.tag || 'div',
3181             cls : 'navbar'
3182         };
3183           
3184         
3185         cfg.cn = [
3186             {
3187                 cls: 'nav',
3188                 tag : 'ul'
3189             }
3190         ];
3191         
3192          
3193         this.type = this.type || 'nav';
3194         if (['tabs','pills'].indexOf(this.type)!==-1) {
3195             cfg.cn[0].cls += ' nav-' + this.type
3196         
3197         
3198         } else {
3199             if (this.type!=='nav') {
3200                 Roo.log('nav type must be nav/tabs/pills')
3201             }
3202             cfg.cn[0].cls += ' navbar-nav'
3203         }
3204         
3205         
3206         
3207         
3208         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3209             cfg.cn[0].cls += ' nav-' + this.arrangement;
3210         }
3211         
3212         
3213         if (this.align === 'right') {
3214             cfg.cn[0].cls += ' navbar-right';
3215         }
3216         
3217         if (this.inverse) {
3218             cfg.cls += ' navbar-inverse';
3219             
3220         }
3221         
3222         
3223         return cfg;
3224     
3225         
3226     }
3227     
3228     
3229     
3230 });
3231
3232
3233
3234  
3235
3236  
3237        /*
3238  * - LGPL
3239  *
3240  * navbar
3241  * 
3242  */
3243
3244 /**
3245  * @class Roo.bootstrap.NavHeaderbar
3246  * @extends Roo.bootstrap.NavSimplebar
3247  * Bootstrap Sidebar class
3248  *
3249  * @cfg {String} brand what is brand
3250  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3251  * @cfg {String} brand_href href of the brand
3252  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3253  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3254  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3255  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3256  * 
3257  * @constructor
3258  * Create a new Sidebar
3259  * @param {Object} config The config object
3260  */
3261
3262
3263 Roo.bootstrap.NavHeaderbar = function(config){
3264     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3265       
3266 };
3267
3268 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3269     
3270     position: '',
3271     brand: '',
3272     brand_href: false,
3273     srButton : true,
3274     autohide : false,
3275     desktopCenter : false,
3276    
3277     
3278     getAutoCreate : function(){
3279         
3280         var   cfg = {
3281             tag: this.nav || 'nav',
3282             cls: 'navbar',
3283             role: 'navigation',
3284             cn: []
3285         };
3286         
3287         var cn = cfg.cn;
3288         if (this.desktopCenter) {
3289             cn.push({cls : 'container', cn : []});
3290             cn = cn[0].cn;
3291         }
3292         
3293         if(this.srButton){
3294             cn.push({
3295                 tag: 'div',
3296                 cls: 'navbar-header',
3297                 cn: [
3298                     {
3299                         tag: 'button',
3300                         type: 'button',
3301                         cls: 'navbar-toggle',
3302                         'data-toggle': 'collapse',
3303                         cn: [
3304                             {
3305                                 tag: 'span',
3306                                 cls: 'sr-only',
3307                                 html: 'Toggle navigation'
3308                             },
3309                             {
3310                                 tag: 'span',
3311                                 cls: 'icon-bar'
3312                             },
3313                             {
3314                                 tag: 'span',
3315                                 cls: 'icon-bar'
3316                             },
3317                             {
3318                                 tag: 'span',
3319                                 cls: 'icon-bar'
3320                             }
3321                         ]
3322                     }
3323                 ]
3324             });
3325         }
3326         
3327         cn.push({
3328             tag: 'div',
3329             cls: 'collapse navbar-collapse',
3330             cn : []
3331         });
3332         
3333         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3334         
3335         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3336             cfg.cls += ' navbar-' + this.position;
3337             
3338             // tag can override this..
3339             
3340             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3341         }
3342         
3343         if (this.brand !== '') {
3344             cn[0].cn.push({
3345                 tag: 'a',
3346                 href: this.brand_href ? this.brand_href : '#',
3347                 cls: 'navbar-brand',
3348                 cn: [
3349                 this.brand
3350                 ]
3351             });
3352         }
3353         
3354         if(this.main){
3355             cfg.cls += ' main-nav';
3356         }
3357         
3358         
3359         return cfg;
3360
3361         
3362     },
3363     getHeaderChildContainer : function()
3364     {
3365         if (this.el.select('.navbar-header').getCount()) {
3366             return this.el.select('.navbar-header',true).first();
3367         }
3368         
3369         return this.getChildContainer();
3370     },
3371     
3372     
3373     initEvents : function()
3374     {
3375         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3376         
3377         if (this.autohide) {
3378             
3379             var prevScroll = 0;
3380             var ft = this.el;
3381             
3382             Roo.get(document).on('scroll',function(e) {
3383                 var ns = Roo.get(document).getScroll().top;
3384                 var os = prevScroll;
3385                 prevScroll = ns;
3386                 
3387                 if(ns > os){
3388                     ft.removeClass('slideDown');
3389                     ft.addClass('slideUp');
3390                     return;
3391                 }
3392                 ft.removeClass('slideUp');
3393                 ft.addClass('slideDown');
3394                  
3395               
3396           },this);
3397         }
3398     }    
3399           
3400       
3401     
3402     
3403 });
3404
3405
3406
3407  
3408
3409  /*
3410  * - LGPL
3411  *
3412  * navbar
3413  * 
3414  */
3415
3416 /**
3417  * @class Roo.bootstrap.NavSidebar
3418  * @extends Roo.bootstrap.Navbar
3419  * Bootstrap Sidebar class
3420  * 
3421  * @constructor
3422  * Create a new Sidebar
3423  * @param {Object} config The config object
3424  */
3425
3426
3427 Roo.bootstrap.NavSidebar = function(config){
3428     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3429 };
3430
3431 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3432     
3433     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3434     
3435     getAutoCreate : function(){
3436         
3437         
3438         return  {
3439             tag: 'div',
3440             cls: 'sidebar sidebar-nav'
3441         };
3442     
3443         
3444     }
3445     
3446     
3447     
3448 });
3449
3450
3451
3452  
3453
3454  /*
3455  * - LGPL
3456  *
3457  * nav group
3458  * 
3459  */
3460
3461 /**
3462  * @class Roo.bootstrap.NavGroup
3463  * @extends Roo.bootstrap.Component
3464  * Bootstrap NavGroup class
3465  * @cfg {String} align left | right
3466  * @cfg {Boolean} inverse false | true
3467  * @cfg {String} type (nav|pills|tab) default nav
3468  * @cfg {String} navId - reference Id for navbar.
3469
3470  * 
3471  * @constructor
3472  * Create a new nav group
3473  * @param {Object} config The config object
3474  */
3475
3476 Roo.bootstrap.NavGroup = function(config){
3477     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3478     this.navItems = [];
3479    
3480     Roo.bootstrap.NavGroup.register(this);
3481      this.addEvents({
3482         /**
3483              * @event changed
3484              * Fires when the active item changes
3485              * @param {Roo.bootstrap.NavGroup} this
3486              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3487              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3488          */
3489         'changed': true
3490      });
3491     
3492 };
3493
3494 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3495     
3496     align: '',
3497     inverse: false,
3498     form: false,
3499     type: 'nav',
3500     navId : '',
3501     // private
3502     
3503     navItems : false, 
3504     
3505     getAutoCreate : function()
3506     {
3507         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3508         
3509         cfg = {
3510             tag : 'ul',
3511             cls: 'nav' 
3512         }
3513         
3514         if (['tabs','pills'].indexOf(this.type)!==-1) {
3515             cfg.cls += ' nav-' + this.type
3516         } else {
3517             if (this.type!=='nav') {
3518                 Roo.log('nav type must be nav/tabs/pills')
3519             }
3520             cfg.cls += ' navbar-nav'
3521         }
3522         
3523         if (this.parent().sidebar) {
3524             cfg = {
3525                 tag: 'ul',
3526                 cls: 'dashboard-menu sidebar-menu'
3527             }
3528             
3529             return cfg;
3530         }
3531         
3532         if (this.form === true) {
3533             cfg = {
3534                 tag: 'form',
3535                 cls: 'navbar-form'
3536             }
3537             
3538             if (this.align === 'right') {
3539                 cfg.cls += ' navbar-right';
3540             } else {
3541                 cfg.cls += ' navbar-left';
3542             }
3543         }
3544         
3545         if (this.align === 'right') {
3546             cfg.cls += ' navbar-right';
3547         }
3548         
3549         if (this.inverse) {
3550             cfg.cls += ' navbar-inverse';
3551             
3552         }
3553         
3554         
3555         return cfg;
3556     },
3557     /**
3558     * sets the active Navigation item
3559     * @param {Roo.bootstrap.NavItem} the new current navitem
3560     */
3561     setActiveItem : function(item)
3562     {
3563         var prev = false;
3564         Roo.each(this.navItems, function(v){
3565             if (v == item) {
3566                 return ;
3567             }
3568             if (v.isActive()) {
3569                 v.setActive(false, true);
3570                 prev = v;
3571                 
3572             }
3573             
3574         });
3575
3576         item.setActive(true, true);
3577         this.fireEvent('changed', this, item, prev);
3578         
3579         
3580     },
3581     /**
3582     * gets the active Navigation item
3583     * @return {Roo.bootstrap.NavItem} the current navitem
3584     */
3585     getActive : function()
3586     {
3587         
3588         var prev = false;
3589         Roo.each(this.navItems, function(v){
3590             
3591             if (v.isActive()) {
3592                 prev = v;
3593                 
3594             }
3595             
3596         });
3597         return prev;
3598     },
3599     
3600     indexOfNav : function()
3601     {
3602         
3603         var prev = false;
3604         Roo.each(this.navItems, function(v,i){
3605             
3606             if (v.isActive()) {
3607                 prev = i;
3608                 
3609             }
3610             
3611         });
3612         return prev;
3613     },
3614     /**
3615     * adds a Navigation item
3616     * @param {Roo.bootstrap.NavItem} the navitem to add
3617     */
3618     addItem : function(cfg)
3619     {
3620         var cn = new Roo.bootstrap.NavItem(cfg);
3621         this.register(cn);
3622         cn.parentId = this.id;
3623         cn.onRender(this.el, null);
3624         return cn;
3625     },
3626     /**
3627     * register a Navigation item
3628     * @param {Roo.bootstrap.NavItem} the navitem to add
3629     */
3630     register : function(item)
3631     {
3632         this.navItems.push( item);
3633         item.navId = this.navId;
3634     
3635     },
3636     
3637     /**
3638     * clear all the Navigation item
3639     */
3640    
3641     clearAll : function()
3642     {
3643         this.navItems = [];
3644         this.el.dom.innerHTML = '';
3645     },
3646     
3647     getNavItem: function(tabId)
3648     {
3649         var ret = false;
3650         Roo.each(this.navItems, function(e) {
3651             if (e.tabId == tabId) {
3652                ret =  e;
3653                return false;
3654             }
3655             return true;
3656             
3657         });
3658         return ret;
3659     },
3660     
3661     setActiveNext : function()
3662     {
3663         var i = this.indexOfNav(this.getActive());
3664         if (i > this.navItems.length) {
3665             return;
3666         }
3667         this.setActiveItem(this.navItems[i+1]);
3668     },
3669     setActivePrev : function()
3670     {
3671         var i = this.indexOfNav(this.getActive());
3672         if (i  < 1) {
3673             return;
3674         }
3675         this.setActiveItem(this.navItems[i-1]);
3676     },
3677     clearWasActive : function(except) {
3678         Roo.each(this.navItems, function(e) {
3679             if (e.tabId != except.tabId && e.was_active) {
3680                e.was_active = false;
3681                return false;
3682             }
3683             return true;
3684             
3685         });
3686     },
3687     getWasActive : function ()
3688     {
3689         var r = false;
3690         Roo.each(this.navItems, function(e) {
3691             if (e.was_active) {
3692                r = e;
3693                return false;
3694             }
3695             return true;
3696             
3697         });
3698         return r;
3699     }
3700     
3701     
3702 });
3703
3704  
3705 Roo.apply(Roo.bootstrap.NavGroup, {
3706     
3707     groups: {},
3708      /**
3709     * register a Navigation Group
3710     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3711     */
3712     register : function(navgrp)
3713     {
3714         this.groups[navgrp.navId] = navgrp;
3715         
3716     },
3717     /**
3718     * fetch a Navigation Group based on the navigation ID
3719     * @param {string} the navgroup to add
3720     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3721     */
3722     get: function(navId) {
3723         if (typeof(this.groups[navId]) == 'undefined') {
3724             return false;
3725             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3726         }
3727         return this.groups[navId] ;
3728     }
3729     
3730     
3731     
3732 });
3733
3734  /*
3735  * - LGPL
3736  *
3737  * row
3738  * 
3739  */
3740
3741 /**
3742  * @class Roo.bootstrap.NavItem
3743  * @extends Roo.bootstrap.Component
3744  * Bootstrap Navbar.NavItem class
3745  * @cfg {String} href  link to
3746  * @cfg {String} html content of button
3747  * @cfg {String} badge text inside badge
3748  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3749  * @cfg {String} glyphicon name of glyphicon
3750  * @cfg {String} icon name of font awesome icon
3751  * @cfg {Boolean} active Is item active
3752  * @cfg {Boolean} disabled Is item disabled
3753  
3754  * @cfg {Boolean} preventDefault (true | false) default false
3755  * @cfg {String} tabId the tab that this item activates.
3756  * @cfg {String} tagtype (a|span) render as a href or span?
3757   
3758  * @constructor
3759  * Create a new Navbar Item
3760  * @param {Object} config The config object
3761  */
3762 Roo.bootstrap.NavItem = function(config){
3763     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3764     this.addEvents({
3765         // raw events
3766         /**
3767          * @event click
3768          * The raw click event for the entire grid.
3769          * @param {Roo.EventObject} e
3770          */
3771         "click" : true,
3772          /**
3773             * @event changed
3774             * Fires when the active item active state changes
3775             * @param {Roo.bootstrap.NavItem} this
3776             * @param {boolean} state the new state
3777              
3778          */
3779         'changed': true
3780     });
3781    
3782 };
3783
3784 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3785     
3786     href: false,
3787     html: '',
3788     badge: '',
3789     icon: false,
3790     glyphicon: false,
3791     active: false,
3792     preventDefault : false,
3793     tabId : false,
3794     tagtype : 'a',
3795     disabled : false,
3796     
3797     was_active : false,
3798     
3799     getAutoCreate : function(){
3800          
3801         var cfg = {
3802             tag: 'li',
3803             cls: 'nav-item'
3804             
3805         }
3806         if (this.active) {
3807             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3808         }
3809         if (this.disabled) {
3810             cfg.cls += ' disabled';
3811         }
3812         
3813         if (this.href || this.html || this.glyphicon || this.icon) {
3814             cfg.cn = [
3815                 {
3816                     tag: this.tagtype,
3817                     href : this.href || "#",
3818                     html: this.html || ''
3819                 }
3820             ];
3821             
3822             if (this.icon) {
3823                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3824             }
3825
3826             if(this.glyphicon) {
3827                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3828             }
3829             
3830             if (this.menu) {
3831                 
3832                 cfg.cn[0].html += " <span class='caret'></span>";
3833              
3834             }
3835             
3836             if (this.badge !== '') {
3837                  
3838                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3839             }
3840         }
3841         
3842         
3843         
3844         return cfg;
3845     },
3846     initEvents: function() 
3847     {
3848         if (typeof (this.menu) != 'undefined') {
3849             this.menu.parentType = this.xtype;
3850             this.menu.triggerEl = this.el;
3851             this.menu = this.addxtype(Roo.apply({}, this.menu));
3852         }
3853         
3854         this.el.select('a',true).on('click', this.onClick, this);
3855         
3856         if(this.tagtype == 'span'){
3857             this.el.select('span',true).on('click', this.onClick, this);
3858         }
3859        
3860         // at this point parent should be available..
3861         this.parent().register(this);
3862     },
3863     
3864     onClick : function(e)
3865     {
3866         if(this.preventDefault || this.href == '#'){
3867             e.preventDefault();
3868         }
3869         
3870         if (this.disabled) {
3871             return;
3872         }
3873         
3874         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3875         if (tg && tg.transition) {
3876             Roo.log("waiting for the transitionend");
3877             return;
3878         }
3879         
3880         Roo.log("fire event clicked");
3881         if(this.fireEvent('click', this, e) === false){
3882             return;
3883         };
3884         
3885         if(this.tagtype == 'span'){
3886             return;
3887         }
3888         
3889         var p = this.parent();
3890         if (['tabs','pills'].indexOf(p.type)!==-1) {
3891             if (typeof(p.setActiveItem) !== 'undefined') {
3892                 p.setActiveItem(this);
3893             }
3894         }
3895         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3896         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3897             // remove the collapsed menu expand...
3898             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3899         }
3900         
3901     },
3902     
3903     isActive: function () {
3904         return this.active
3905     },
3906     setActive : function(state, fire, is_was_active)
3907     {
3908         if (this.active && !state & this.navId) {
3909             this.was_active = true;
3910             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3911             if (nv) {
3912                 nv.clearWasActive(this);
3913             }
3914             
3915         }
3916         this.active = state;
3917         
3918         if (!state ) {
3919             this.el.removeClass('active');
3920         } else if (!this.el.hasClass('active')) {
3921             this.el.addClass('active');
3922         }
3923         if (fire) {
3924             this.fireEvent('changed', this, state);
3925         }
3926         
3927         // show a panel if it's registered and related..
3928         
3929         if (!this.navId || !this.tabId || !state || is_was_active) {
3930             return;
3931         }
3932         
3933         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3934         if (!tg) {
3935             return;
3936         }
3937         var pan = tg.getPanelByName(this.tabId);
3938         if (!pan) {
3939             return;
3940         }
3941         // if we can not flip to new panel - go back to old nav highlight..
3942         if (false == tg.showPanel(pan)) {
3943             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3944             if (nv) {
3945                 var onav = nv.getWasActive();
3946                 if (onav) {
3947                     onav.setActive(true, false, true);
3948                 }
3949             }
3950             
3951         }
3952         
3953         
3954         
3955     },
3956      // this should not be here...
3957     setDisabled : function(state)
3958     {
3959         this.disabled = state;
3960         if (!state ) {
3961             this.el.removeClass('disabled');
3962         } else if (!this.el.hasClass('disabled')) {
3963             this.el.addClass('disabled');
3964         }
3965         
3966     },
3967     
3968     /**
3969      * Fetch the element to display the tooltip on.
3970      * @return {Roo.Element} defaults to this.el
3971      */
3972     tooltipEl : function()
3973     {
3974         return this.el.select('' + this.tagtype + '', true).first();
3975     }
3976 });
3977  
3978
3979  /*
3980  * - LGPL
3981  *
3982  * sidebar item
3983  *
3984  *  li
3985  *    <span> icon </span>
3986  *    <span> text </span>
3987  *    <span>badge </span>
3988  */
3989
3990 /**
3991  * @class Roo.bootstrap.NavSidebarItem
3992  * @extends Roo.bootstrap.NavItem
3993  * Bootstrap Navbar.NavSidebarItem class
3994  * @constructor
3995  * Create a new Navbar Button
3996  * @param {Object} config The config object
3997  */
3998 Roo.bootstrap.NavSidebarItem = function(config){
3999     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4000     this.addEvents({
4001         // raw events
4002         /**
4003          * @event click
4004          * The raw click event for the entire grid.
4005          * @param {Roo.EventObject} e
4006          */
4007         "click" : true,
4008          /**
4009             * @event changed
4010             * Fires when the active item active state changes
4011             * @param {Roo.bootstrap.NavSidebarItem} this
4012             * @param {boolean} state the new state
4013              
4014          */
4015         'changed': true
4016     });
4017    
4018 };
4019
4020 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4021     
4022     
4023     getAutoCreate : function(){
4024         
4025         
4026         var a = {
4027                 tag: 'a',
4028                 href : this.href || '#',
4029                 cls: '',
4030                 html : '',
4031                 cn : []
4032         };
4033         var cfg = {
4034             tag: 'li',
4035             cls: '',
4036             cn: [ a ]
4037         }
4038         var span = {
4039             tag: 'span',
4040             html : this.html || ''
4041         }
4042         
4043         
4044         if (this.active) {
4045             cfg.cls += ' active';
4046         }
4047         
4048         // left icon..
4049         if (this.glyphicon || this.icon) {
4050             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4051             a.cn.push({ tag : 'i', cls : c }) ;
4052         }
4053         // html..
4054         a.cn.push(span);
4055         // then badge..
4056         if (this.badge !== '') {
4057             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4058         }
4059         // fi
4060         if (this.menu) {
4061             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4062             a.cls += 'dropdown-toggle treeview' ;
4063             
4064         }
4065         
4066         
4067         
4068         return cfg;
4069          
4070            
4071     }
4072    
4073      
4074  
4075 });
4076  
4077
4078  /*
4079  * - LGPL
4080  *
4081  * row
4082  * 
4083  */
4084
4085 /**
4086  * @class Roo.bootstrap.Row
4087  * @extends Roo.bootstrap.Component
4088  * Bootstrap Row class (contains columns...)
4089  * 
4090  * @constructor
4091  * Create a new Row
4092  * @param {Object} config The config object
4093  */
4094
4095 Roo.bootstrap.Row = function(config){
4096     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4097 };
4098
4099 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4100     
4101     getAutoCreate : function(){
4102        return {
4103             cls: 'row clearfix'
4104        };
4105     }
4106     
4107     
4108 });
4109
4110  
4111
4112  /*
4113  * - LGPL
4114  *
4115  * element
4116  * 
4117  */
4118
4119 /**
4120  * @class Roo.bootstrap.Element
4121  * @extends Roo.bootstrap.Component
4122  * Bootstrap Element class
4123  * @cfg {String} html contents of the element
4124  * @cfg {String} tag tag of the element
4125  * @cfg {String} cls class of the element
4126  * 
4127  * @constructor
4128  * Create a new Element
4129  * @param {Object} config The config object
4130  */
4131
4132 Roo.bootstrap.Element = function(config){
4133     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4134 };
4135
4136 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4137     
4138     tag: 'div',
4139     cls: '',
4140     html: '',
4141      
4142     
4143     getAutoCreate : function(){
4144         
4145         var cfg = {
4146             tag: this.tag,
4147             cls: this.cls,
4148             html: this.html
4149         }
4150         
4151         
4152         
4153         return cfg;
4154     }
4155    
4156 });
4157
4158  
4159
4160  /*
4161  * - LGPL
4162  *
4163  * pagination
4164  * 
4165  */
4166
4167 /**
4168  * @class Roo.bootstrap.Pagination
4169  * @extends Roo.bootstrap.Component
4170  * Bootstrap Pagination class
4171  * @cfg {String} size xs | sm | md | lg
4172  * @cfg {Boolean} inverse false | true
4173  * 
4174  * @constructor
4175  * Create a new Pagination
4176  * @param {Object} config The config object
4177  */
4178
4179 Roo.bootstrap.Pagination = function(config){
4180     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4181 };
4182
4183 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4184     
4185     cls: false,
4186     size: false,
4187     inverse: false,
4188     
4189     getAutoCreate : function(){
4190         var cfg = {
4191             tag: 'ul',
4192                 cls: 'pagination'
4193         };
4194         if (this.inverse) {
4195             cfg.cls += ' inverse';
4196         }
4197         if (this.html) {
4198             cfg.html=this.html;
4199         }
4200         if (this.cls) {
4201             cfg.cls += " " + this.cls;
4202         }
4203         return cfg;
4204     }
4205    
4206 });
4207
4208  
4209
4210  /*
4211  * - LGPL
4212  *
4213  * Pagination item
4214  * 
4215  */
4216
4217
4218 /**
4219  * @class Roo.bootstrap.PaginationItem
4220  * @extends Roo.bootstrap.Component
4221  * Bootstrap PaginationItem class
4222  * @cfg {String} html text
4223  * @cfg {String} href the link
4224  * @cfg {Boolean} preventDefault (true | false) default true
4225  * @cfg {Boolean} active (true | false) default false
4226  * @cfg {Boolean} disabled default false
4227  * 
4228  * 
4229  * @constructor
4230  * Create a new PaginationItem
4231  * @param {Object} config The config object
4232  */
4233
4234
4235 Roo.bootstrap.PaginationItem = function(config){
4236     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4237     this.addEvents({
4238         // raw events
4239         /**
4240          * @event click
4241          * The raw click event for the entire grid.
4242          * @param {Roo.EventObject} e
4243          */
4244         "click" : true
4245     });
4246 };
4247
4248 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4249     
4250     href : false,
4251     html : false,
4252     preventDefault: true,
4253     active : false,
4254     cls : false,
4255     disabled: false,
4256     
4257     getAutoCreate : function(){
4258         var cfg= {
4259             tag: 'li',
4260             cn: [
4261                 {
4262                     tag : 'a',
4263                     href : this.href ? this.href : '#',
4264                     html : this.html ? this.html : ''
4265                 }
4266             ]
4267         };
4268         
4269         if(this.cls){
4270             cfg.cls = this.cls;
4271         }
4272         
4273         if(this.disabled){
4274             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4275         }
4276         
4277         if(this.active){
4278             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4279         }
4280         
4281         return cfg;
4282     },
4283     
4284     initEvents: function() {
4285         
4286         this.el.on('click', this.onClick, this);
4287         
4288     },
4289     onClick : function(e)
4290     {
4291         Roo.log('PaginationItem on click ');
4292         if(this.preventDefault){
4293             e.preventDefault();
4294         }
4295         
4296         if(this.disabled){
4297             return;
4298         }
4299         
4300         this.fireEvent('click', this, e);
4301     }
4302    
4303 });
4304
4305  
4306
4307  /*
4308  * - LGPL
4309  *
4310  * slider
4311  * 
4312  */
4313
4314
4315 /**
4316  * @class Roo.bootstrap.Slider
4317  * @extends Roo.bootstrap.Component
4318  * Bootstrap Slider class
4319  *    
4320  * @constructor
4321  * Create a new Slider
4322  * @param {Object} config The config object
4323  */
4324
4325 Roo.bootstrap.Slider = function(config){
4326     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4327 };
4328
4329 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4330     
4331     getAutoCreate : function(){
4332         
4333         var cfg = {
4334             tag: 'div',
4335             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4336             cn: [
4337                 {
4338                     tag: 'a',
4339                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4340                 }
4341             ]
4342         }
4343         
4344         return cfg;
4345     }
4346    
4347 });
4348
4349  /*
4350  * Based on:
4351  * Ext JS Library 1.1.1
4352  * Copyright(c) 2006-2007, Ext JS, LLC.
4353  *
4354  * Originally Released Under LGPL - original licence link has changed is not relivant.
4355  *
4356  * Fork - LGPL
4357  * <script type="text/javascript">
4358  */
4359  
4360
4361 /**
4362  * @class Roo.grid.ColumnModel
4363  * @extends Roo.util.Observable
4364  * This is the default implementation of a ColumnModel used by the Grid. It defines
4365  * the columns in the grid.
4366  * <br>Usage:<br>
4367  <pre><code>
4368  var colModel = new Roo.grid.ColumnModel([
4369         {header: "Ticker", width: 60, sortable: true, locked: true},
4370         {header: "Company Name", width: 150, sortable: true},
4371         {header: "Market Cap.", width: 100, sortable: true},
4372         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4373         {header: "Employees", width: 100, sortable: true, resizable: false}
4374  ]);
4375  </code></pre>
4376  * <p>
4377  
4378  * The config options listed for this class are options which may appear in each
4379  * individual column definition.
4380  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4381  * @constructor
4382  * @param {Object} config An Array of column config objects. See this class's
4383  * config objects for details.
4384 */
4385 Roo.grid.ColumnModel = function(config){
4386         /**
4387      * The config passed into the constructor
4388      */
4389     this.config = config;
4390     this.lookup = {};
4391
4392     // if no id, create one
4393     // if the column does not have a dataIndex mapping,
4394     // map it to the order it is in the config
4395     for(var i = 0, len = config.length; i < len; i++){
4396         var c = config[i];
4397         if(typeof c.dataIndex == "undefined"){
4398             c.dataIndex = i;
4399         }
4400         if(typeof c.renderer == "string"){
4401             c.renderer = Roo.util.Format[c.renderer];
4402         }
4403         if(typeof c.id == "undefined"){
4404             c.id = Roo.id();
4405         }
4406         if(c.editor && c.editor.xtype){
4407             c.editor  = Roo.factory(c.editor, Roo.grid);
4408         }
4409         if(c.editor && c.editor.isFormField){
4410             c.editor = new Roo.grid.GridEditor(c.editor);
4411         }
4412         this.lookup[c.id] = c;
4413     }
4414
4415     /**
4416      * The width of columns which have no width specified (defaults to 100)
4417      * @type Number
4418      */
4419     this.defaultWidth = 100;
4420
4421     /**
4422      * Default sortable of columns which have no sortable specified (defaults to false)
4423      * @type Boolean
4424      */
4425     this.defaultSortable = false;
4426
4427     this.addEvents({
4428         /**
4429              * @event widthchange
4430              * Fires when the width of a column changes.
4431              * @param {ColumnModel} this
4432              * @param {Number} columnIndex The column index
4433              * @param {Number} newWidth The new width
4434              */
4435             "widthchange": true,
4436         /**
4437              * @event headerchange
4438              * Fires when the text of a header changes.
4439              * @param {ColumnModel} this
4440              * @param {Number} columnIndex The column index
4441              * @param {Number} newText The new header text
4442              */
4443             "headerchange": true,
4444         /**
4445              * @event hiddenchange
4446              * Fires when a column is hidden or "unhidden".
4447              * @param {ColumnModel} this
4448              * @param {Number} columnIndex The column index
4449              * @param {Boolean} hidden true if hidden, false otherwise
4450              */
4451             "hiddenchange": true,
4452             /**
4453          * @event columnmoved
4454          * Fires when a column is moved.
4455          * @param {ColumnModel} this
4456          * @param {Number} oldIndex
4457          * @param {Number} newIndex
4458          */
4459         "columnmoved" : true,
4460         /**
4461          * @event columlockchange
4462          * Fires when a column's locked state is changed
4463          * @param {ColumnModel} this
4464          * @param {Number} colIndex
4465          * @param {Boolean} locked true if locked
4466          */
4467         "columnlockchange" : true
4468     });
4469     Roo.grid.ColumnModel.superclass.constructor.call(this);
4470 };
4471 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4472     /**
4473      * @cfg {String} header The header text to display in the Grid view.
4474      */
4475     /**
4476      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4477      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4478      * specified, the column's index is used as an index into the Record's data Array.
4479      */
4480     /**
4481      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4482      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4483      */
4484     /**
4485      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4486      * Defaults to the value of the {@link #defaultSortable} property.
4487      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4488      */
4489     /**
4490      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4491      */
4492     /**
4493      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4494      */
4495     /**
4496      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4497      */
4498     /**
4499      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4500      */
4501     /**
4502      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4503      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4504      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4505      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4506      */
4507        /**
4508      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4509      */
4510     /**
4511      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4512      */
4513     /**
4514      * @cfg {String} cursor (Optional)
4515      */
4516     /**
4517      * @cfg {String} tooltip (Optional)
4518      */
4519     /**
4520      * Returns the id of the column at the specified index.
4521      * @param {Number} index The column index
4522      * @return {String} the id
4523      */
4524     getColumnId : function(index){
4525         return this.config[index].id;
4526     },
4527
4528     /**
4529      * Returns the column for a specified id.
4530      * @param {String} id The column id
4531      * @return {Object} the column
4532      */
4533     getColumnById : function(id){
4534         return this.lookup[id];
4535     },
4536
4537     
4538     /**
4539      * Returns the column for a specified dataIndex.
4540      * @param {String} dataIndex The column dataIndex
4541      * @return {Object|Boolean} the column or false if not found
4542      */
4543     getColumnByDataIndex: function(dataIndex){
4544         var index = this.findColumnIndex(dataIndex);
4545         return index > -1 ? this.config[index] : false;
4546     },
4547     
4548     /**
4549      * Returns the index for a specified column id.
4550      * @param {String} id The column id
4551      * @return {Number} the index, or -1 if not found
4552      */
4553     getIndexById : function(id){
4554         for(var i = 0, len = this.config.length; i < len; i++){
4555             if(this.config[i].id == id){
4556                 return i;
4557             }
4558         }
4559         return -1;
4560     },
4561     
4562     /**
4563      * Returns the index for a specified column dataIndex.
4564      * @param {String} dataIndex The column dataIndex
4565      * @return {Number} the index, or -1 if not found
4566      */
4567     
4568     findColumnIndex : function(dataIndex){
4569         for(var i = 0, len = this.config.length; i < len; i++){
4570             if(this.config[i].dataIndex == dataIndex){
4571                 return i;
4572             }
4573         }
4574         return -1;
4575     },
4576     
4577     
4578     moveColumn : function(oldIndex, newIndex){
4579         var c = this.config[oldIndex];
4580         this.config.splice(oldIndex, 1);
4581         this.config.splice(newIndex, 0, c);
4582         this.dataMap = null;
4583         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4584     },
4585
4586     isLocked : function(colIndex){
4587         return this.config[colIndex].locked === true;
4588     },
4589
4590     setLocked : function(colIndex, value, suppressEvent){
4591         if(this.isLocked(colIndex) == value){
4592             return;
4593         }
4594         this.config[colIndex].locked = value;
4595         if(!suppressEvent){
4596             this.fireEvent("columnlockchange", this, colIndex, value);
4597         }
4598     },
4599
4600     getTotalLockedWidth : function(){
4601         var totalWidth = 0;
4602         for(var i = 0; i < this.config.length; i++){
4603             if(this.isLocked(i) && !this.isHidden(i)){
4604                 this.totalWidth += this.getColumnWidth(i);
4605             }
4606         }
4607         return totalWidth;
4608     },
4609
4610     getLockedCount : function(){
4611         for(var i = 0, len = this.config.length; i < len; i++){
4612             if(!this.isLocked(i)){
4613                 return i;
4614             }
4615         }
4616     },
4617
4618     /**
4619      * Returns the number of columns.
4620      * @return {Number}
4621      */
4622     getColumnCount : function(visibleOnly){
4623         if(visibleOnly === true){
4624             var c = 0;
4625             for(var i = 0, len = this.config.length; i < len; i++){
4626                 if(!this.isHidden(i)){
4627                     c++;
4628                 }
4629             }
4630             return c;
4631         }
4632         return this.config.length;
4633     },
4634
4635     /**
4636      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4637      * @param {Function} fn
4638      * @param {Object} scope (optional)
4639      * @return {Array} result
4640      */
4641     getColumnsBy : function(fn, scope){
4642         var r = [];
4643         for(var i = 0, len = this.config.length; i < len; i++){
4644             var c = this.config[i];
4645             if(fn.call(scope||this, c, i) === true){
4646                 r[r.length] = c;
4647             }
4648         }
4649         return r;
4650     },
4651
4652     /**
4653      * Returns true if the specified column is sortable.
4654      * @param {Number} col The column index
4655      * @return {Boolean}
4656      */
4657     isSortable : function(col){
4658         if(typeof this.config[col].sortable == "undefined"){
4659             return this.defaultSortable;
4660         }
4661         return this.config[col].sortable;
4662     },
4663
4664     /**
4665      * Returns the rendering (formatting) function defined for the column.
4666      * @param {Number} col The column index.
4667      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4668      */
4669     getRenderer : function(col){
4670         if(!this.config[col].renderer){
4671             return Roo.grid.ColumnModel.defaultRenderer;
4672         }
4673         return this.config[col].renderer;
4674     },
4675
4676     /**
4677      * Sets the rendering (formatting) function for a column.
4678      * @param {Number} col The column index
4679      * @param {Function} fn The function to use to process the cell's raw data
4680      * to return HTML markup for the grid view. The render function is called with
4681      * the following parameters:<ul>
4682      * <li>Data value.</li>
4683      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4684      * <li>css A CSS style string to apply to the table cell.</li>
4685      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4686      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4687      * <li>Row index</li>
4688      * <li>Column index</li>
4689      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4690      */
4691     setRenderer : function(col, fn){
4692         this.config[col].renderer = fn;
4693     },
4694
4695     /**
4696      * Returns the width for the specified column.
4697      * @param {Number} col The column index
4698      * @return {Number}
4699      */
4700     getColumnWidth : function(col){
4701         return this.config[col].width * 1 || this.defaultWidth;
4702     },
4703
4704     /**
4705      * Sets the width for a column.
4706      * @param {Number} col The column index
4707      * @param {Number} width The new width
4708      */
4709     setColumnWidth : function(col, width, suppressEvent){
4710         this.config[col].width = width;
4711         this.totalWidth = null;
4712         if(!suppressEvent){
4713              this.fireEvent("widthchange", this, col, width);
4714         }
4715     },
4716
4717     /**
4718      * Returns the total width of all columns.
4719      * @param {Boolean} includeHidden True to include hidden column widths
4720      * @return {Number}
4721      */
4722     getTotalWidth : function(includeHidden){
4723         if(!this.totalWidth){
4724             this.totalWidth = 0;
4725             for(var i = 0, len = this.config.length; i < len; i++){
4726                 if(includeHidden || !this.isHidden(i)){
4727                     this.totalWidth += this.getColumnWidth(i);
4728                 }
4729             }
4730         }
4731         return this.totalWidth;
4732     },
4733
4734     /**
4735      * Returns the header for the specified column.
4736      * @param {Number} col The column index
4737      * @return {String}
4738      */
4739     getColumnHeader : function(col){
4740         return this.config[col].header;
4741     },
4742
4743     /**
4744      * Sets the header for a column.
4745      * @param {Number} col The column index
4746      * @param {String} header The new header
4747      */
4748     setColumnHeader : function(col, header){
4749         this.config[col].header = header;
4750         this.fireEvent("headerchange", this, col, header);
4751     },
4752
4753     /**
4754      * Returns the tooltip for the specified column.
4755      * @param {Number} col The column index
4756      * @return {String}
4757      */
4758     getColumnTooltip : function(col){
4759             return this.config[col].tooltip;
4760     },
4761     /**
4762      * Sets the tooltip for a column.
4763      * @param {Number} col The column index
4764      * @param {String} tooltip The new tooltip
4765      */
4766     setColumnTooltip : function(col, tooltip){
4767             this.config[col].tooltip = tooltip;
4768     },
4769
4770     /**
4771      * Returns the dataIndex for the specified column.
4772      * @param {Number} col The column index
4773      * @return {Number}
4774      */
4775     getDataIndex : function(col){
4776         return this.config[col].dataIndex;
4777     },
4778
4779     /**
4780      * Sets the dataIndex for a column.
4781      * @param {Number} col The column index
4782      * @param {Number} dataIndex The new dataIndex
4783      */
4784     setDataIndex : function(col, dataIndex){
4785         this.config[col].dataIndex = dataIndex;
4786     },
4787
4788     
4789     
4790     /**
4791      * Returns true if the cell is editable.
4792      * @param {Number} colIndex The column index
4793      * @param {Number} rowIndex The row index
4794      * @return {Boolean}
4795      */
4796     isCellEditable : function(colIndex, rowIndex){
4797         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4798     },
4799
4800     /**
4801      * Returns the editor defined for the cell/column.
4802      * return false or null to disable editing.
4803      * @param {Number} colIndex The column index
4804      * @param {Number} rowIndex The row index
4805      * @return {Object}
4806      */
4807     getCellEditor : function(colIndex, rowIndex){
4808         return this.config[colIndex].editor;
4809     },
4810
4811     /**
4812      * Sets if a column is editable.
4813      * @param {Number} col The column index
4814      * @param {Boolean} editable True if the column is editable
4815      */
4816     setEditable : function(col, editable){
4817         this.config[col].editable = editable;
4818     },
4819
4820
4821     /**
4822      * Returns true if the column is hidden.
4823      * @param {Number} colIndex The column index
4824      * @return {Boolean}
4825      */
4826     isHidden : function(colIndex){
4827         return this.config[colIndex].hidden;
4828     },
4829
4830
4831     /**
4832      * Returns true if the column width cannot be changed
4833      */
4834     isFixed : function(colIndex){
4835         return this.config[colIndex].fixed;
4836     },
4837
4838     /**
4839      * Returns true if the column can be resized
4840      * @return {Boolean}
4841      */
4842     isResizable : function(colIndex){
4843         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4844     },
4845     /**
4846      * Sets if a column is hidden.
4847      * @param {Number} colIndex The column index
4848      * @param {Boolean} hidden True if the column is hidden
4849      */
4850     setHidden : function(colIndex, hidden){
4851         this.config[colIndex].hidden = hidden;
4852         this.totalWidth = null;
4853         this.fireEvent("hiddenchange", this, colIndex, hidden);
4854     },
4855
4856     /**
4857      * Sets the editor for a column.
4858      * @param {Number} col The column index
4859      * @param {Object} editor The editor object
4860      */
4861     setEditor : function(col, editor){
4862         this.config[col].editor = editor;
4863     }
4864 });
4865
4866 Roo.grid.ColumnModel.defaultRenderer = function(value){
4867         if(typeof value == "string" && value.length < 1){
4868             return "&#160;";
4869         }
4870         return value;
4871 };
4872
4873 // Alias for backwards compatibility
4874 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4875 /*
4876  * Based on:
4877  * Ext JS Library 1.1.1
4878  * Copyright(c) 2006-2007, Ext JS, LLC.
4879  *
4880  * Originally Released Under LGPL - original licence link has changed is not relivant.
4881  *
4882  * Fork - LGPL
4883  * <script type="text/javascript">
4884  */
4885  
4886 /**
4887  * @class Roo.LoadMask
4888  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4889  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4890  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4891  * element's UpdateManager load indicator and will be destroyed after the initial load.
4892  * @constructor
4893  * Create a new LoadMask
4894  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4895  * @param {Object} config The config object
4896  */
4897 Roo.LoadMask = function(el, config){
4898     this.el = Roo.get(el);
4899     Roo.apply(this, config);
4900     if(this.store){
4901         this.store.on('beforeload', this.onBeforeLoad, this);
4902         this.store.on('load', this.onLoad, this);
4903         this.store.on('loadexception', this.onLoadException, this);
4904         this.removeMask = false;
4905     }else{
4906         var um = this.el.getUpdateManager();
4907         um.showLoadIndicator = false; // disable the default indicator
4908         um.on('beforeupdate', this.onBeforeLoad, this);
4909         um.on('update', this.onLoad, this);
4910         um.on('failure', this.onLoad, this);
4911         this.removeMask = true;
4912     }
4913 };
4914
4915 Roo.LoadMask.prototype = {
4916     /**
4917      * @cfg {Boolean} removeMask
4918      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4919      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4920      */
4921     /**
4922      * @cfg {String} msg
4923      * The text to display in a centered loading message box (defaults to 'Loading...')
4924      */
4925     msg : 'Loading...',
4926     /**
4927      * @cfg {String} msgCls
4928      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4929      */
4930     msgCls : 'x-mask-loading',
4931
4932     /**
4933      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4934      * @type Boolean
4935      */
4936     disabled: false,
4937
4938     /**
4939      * Disables the mask to prevent it from being displayed
4940      */
4941     disable : function(){
4942        this.disabled = true;
4943     },
4944
4945     /**
4946      * Enables the mask so that it can be displayed
4947      */
4948     enable : function(){
4949         this.disabled = false;
4950     },
4951     
4952     onLoadException : function()
4953     {
4954         Roo.log(arguments);
4955         
4956         if (typeof(arguments[3]) != 'undefined') {
4957             Roo.MessageBox.alert("Error loading",arguments[3]);
4958         } 
4959         /*
4960         try {
4961             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4962                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4963             }   
4964         } catch(e) {
4965             
4966         }
4967         */
4968     
4969         
4970         
4971         this.el.unmask(this.removeMask);
4972     },
4973     // private
4974     onLoad : function()
4975     {
4976         this.el.unmask(this.removeMask);
4977     },
4978
4979     // private
4980     onBeforeLoad : function(){
4981         if(!this.disabled){
4982             this.el.mask(this.msg, this.msgCls);
4983         }
4984     },
4985
4986     // private
4987     destroy : function(){
4988         if(this.store){
4989             this.store.un('beforeload', this.onBeforeLoad, this);
4990             this.store.un('load', this.onLoad, this);
4991             this.store.un('loadexception', this.onLoadException, this);
4992         }else{
4993             var um = this.el.getUpdateManager();
4994             um.un('beforeupdate', this.onBeforeLoad, this);
4995             um.un('update', this.onLoad, this);
4996             um.un('failure', this.onLoad, this);
4997         }
4998     }
4999 };/*
5000  * - LGPL
5001  *
5002  * table
5003  * 
5004  */
5005
5006 /**
5007  * @class Roo.bootstrap.Table
5008  * @extends Roo.bootstrap.Component
5009  * Bootstrap Table class
5010  * @cfg {String} cls table class
5011  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5012  * @cfg {String} bgcolor Specifies the background color for a table
5013  * @cfg {Number} border Specifies whether the table cells should have borders or not
5014  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5015  * @cfg {Number} cellspacing Specifies the space between cells
5016  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5017  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5018  * @cfg {String} sortable Specifies that the table should be sortable
5019  * @cfg {String} summary Specifies a summary of the content of a table
5020  * @cfg {Number} width Specifies the width of a table
5021  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5022  * 
5023  * @cfg {boolean} striped Should the rows be alternative striped
5024  * @cfg {boolean} bordered Add borders to the table
5025  * @cfg {boolean} hover Add hover highlighting
5026  * @cfg {boolean} condensed Format condensed
5027  * @cfg {boolean} responsive Format condensed
5028  * @cfg {Boolean} loadMask (true|false) default false
5029  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5030  * @cfg {Boolean} thead (true|false) generate thead, default true
5031  * @cfg {Boolean} RowSelection (true|false) default false
5032  * @cfg {Boolean} CellSelection (true|false) default false
5033  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5034  
5035  * 
5036  * @constructor
5037  * Create a new Table
5038  * @param {Object} config The config object
5039  */
5040
5041 Roo.bootstrap.Table = function(config){
5042     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5043     
5044     if (this.sm) {
5045         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5046         this.sm = this.selModel;
5047         this.sm.xmodule = this.xmodule || false;
5048     }
5049     if (this.cm && typeof(this.cm.config) == 'undefined') {
5050         this.colModel = new Roo.grid.ColumnModel(this.cm);
5051         this.cm = this.colModel;
5052         this.cm.xmodule = this.xmodule || false;
5053     }
5054     if (this.store) {
5055         this.store= Roo.factory(this.store, Roo.data);
5056         this.ds = this.store;
5057         this.ds.xmodule = this.xmodule || false;
5058          
5059     }
5060     if (this.footer && this.store) {
5061         this.footer.dataSource = this.ds;
5062         this.footer = Roo.factory(this.footer);
5063     }
5064     
5065     /** @private */
5066     this.addEvents({
5067         /**
5068          * @event cellclick
5069          * Fires when a cell is clicked
5070          * @param {Roo.bootstrap.Table} this
5071          * @param {Roo.Element} el
5072          * @param {Number} rowIndex
5073          * @param {Number} columnIndex
5074          * @param {Roo.EventObject} e
5075          */
5076         "cellclick" : true,
5077         /**
5078          * @event celldblclick
5079          * Fires when a cell is double clicked
5080          * @param {Roo.bootstrap.Table} this
5081          * @param {Roo.Element} el
5082          * @param {Number} rowIndex
5083          * @param {Number} columnIndex
5084          * @param {Roo.EventObject} e
5085          */
5086         "celldblclick" : true,
5087         /**
5088          * @event rowclick
5089          * Fires when a row is clicked
5090          * @param {Roo.bootstrap.Table} this
5091          * @param {Roo.Element} el
5092          * @param {Number} rowIndex
5093          * @param {Roo.EventObject} e
5094          */
5095         "rowclick" : true,
5096         /**
5097          * @event rowdblclick
5098          * Fires when a row is double clicked
5099          * @param {Roo.bootstrap.Table} this
5100          * @param {Roo.Element} el
5101          * @param {Number} rowIndex
5102          * @param {Roo.EventObject} e
5103          */
5104         "rowdblclick" : true,
5105         /**
5106          * @event mouseover
5107          * Fires when a mouseover occur
5108          * @param {Roo.bootstrap.Table} this
5109          * @param {Roo.Element} el
5110          * @param {Number} rowIndex
5111          * @param {Number} columnIndex
5112          * @param {Roo.EventObject} e
5113          */
5114         "mouseover" : true,
5115         /**
5116          * @event mouseout
5117          * Fires when a mouseout occur
5118          * @param {Roo.bootstrap.Table} this
5119          * @param {Roo.Element} el
5120          * @param {Number} rowIndex
5121          * @param {Number} columnIndex
5122          * @param {Roo.EventObject} e
5123          */
5124         "mouseout" : true,
5125         /**
5126          * @event rowclass
5127          * Fires when a row is rendered, so you can change add a style to it.
5128          * @param {Roo.bootstrap.Table} this
5129          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5130          */
5131         'rowclass' : true,
5132           /**
5133          * @event rowsrendered
5134          * Fires when all the  rows have been rendered
5135          * @param {Roo.bootstrap.Table} this
5136          */
5137         'rowsrendered' : true
5138         
5139     });
5140 };
5141
5142 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5143     
5144     cls: false,
5145     align: false,
5146     bgcolor: false,
5147     border: false,
5148     cellpadding: false,
5149     cellspacing: false,
5150     frame: false,
5151     rules: false,
5152     sortable: false,
5153     summary: false,
5154     width: false,
5155     striped : false,
5156     bordered: false,
5157     hover:  false,
5158     condensed : false,
5159     responsive : false,
5160     sm : false,
5161     cm : false,
5162     store : false,
5163     loadMask : false,
5164     tfoot : true,
5165     thead : true,
5166     RowSelection : false,
5167     CellSelection : false,
5168     layout : false,
5169     
5170     // Roo.Element - the tbody
5171     mainBody: false, 
5172     
5173     getAutoCreate : function(){
5174         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5175         
5176         cfg = {
5177             tag: 'table',
5178             cls : 'table',
5179             cn : []
5180         }
5181             
5182         if (this.striped) {
5183             cfg.cls += ' table-striped';
5184         }
5185         
5186         if (this.hover) {
5187             cfg.cls += ' table-hover';
5188         }
5189         if (this.bordered) {
5190             cfg.cls += ' table-bordered';
5191         }
5192         if (this.condensed) {
5193             cfg.cls += ' table-condensed';
5194         }
5195         if (this.responsive) {
5196             cfg.cls += ' table-responsive';
5197         }
5198         
5199         if (this.cls) {
5200             cfg.cls+=  ' ' +this.cls;
5201         }
5202         
5203         // this lot should be simplifed...
5204         
5205         if (this.align) {
5206             cfg.align=this.align;
5207         }
5208         if (this.bgcolor) {
5209             cfg.bgcolor=this.bgcolor;
5210         }
5211         if (this.border) {
5212             cfg.border=this.border;
5213         }
5214         if (this.cellpadding) {
5215             cfg.cellpadding=this.cellpadding;
5216         }
5217         if (this.cellspacing) {
5218             cfg.cellspacing=this.cellspacing;
5219         }
5220         if (this.frame) {
5221             cfg.frame=this.frame;
5222         }
5223         if (this.rules) {
5224             cfg.rules=this.rules;
5225         }
5226         if (this.sortable) {
5227             cfg.sortable=this.sortable;
5228         }
5229         if (this.summary) {
5230             cfg.summary=this.summary;
5231         }
5232         if (this.width) {
5233             cfg.width=this.width;
5234         }
5235         if (this.layout) {
5236             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5237         }
5238         
5239         if(this.store || this.cm){
5240             if(this.thead){
5241                 cfg.cn.push(this.renderHeader());
5242             }
5243             
5244             cfg.cn.push(this.renderBody());
5245             
5246             if(this.tfoot){
5247                 cfg.cn.push(this.renderFooter());
5248             }
5249             
5250             cfg.cls+=  ' TableGrid';
5251         }
5252         
5253         return { cn : [ cfg ] };
5254     },
5255     
5256     initEvents : function()
5257     {   
5258         if(!this.store || !this.cm){
5259             return;
5260         }
5261         
5262         //Roo.log('initEvents with ds!!!!');
5263         
5264         this.mainBody = this.el.select('tbody', true).first();
5265         
5266         
5267         var _this = this;
5268         
5269         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5270             e.on('click', _this.sort, _this);
5271         });
5272         
5273         this.el.on("click", this.onClick, this);
5274         this.el.on("dblclick", this.onDblClick, this);
5275         
5276         // why is this done????? = it breaks dialogs??
5277         //this.parent().el.setStyle('position', 'relative');
5278         
5279         
5280         if (this.footer) {
5281             this.footer.parentId = this.id;
5282             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5283         }
5284         
5285         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5286         
5287         this.store.on('load', this.onLoad, this);
5288         this.store.on('beforeload', this.onBeforeLoad, this);
5289         this.store.on('update', this.onUpdate, this);
5290         this.store.on('add', this.onAdd, this);
5291         
5292     },
5293     
5294     onMouseover : function(e, el)
5295     {
5296         var cell = Roo.get(el);
5297         
5298         if(!cell){
5299             return;
5300         }
5301         
5302         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5303             cell = cell.findParent('td', false, true);
5304         }
5305         
5306         var row = cell.findParent('tr', false, true);
5307         var cellIndex = cell.dom.cellIndex;
5308         var rowIndex = row.dom.rowIndex - 1; // start from 0
5309         
5310         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5311         
5312     },
5313     
5314     onMouseout : function(e, el)
5315     {
5316         var cell = Roo.get(el);
5317         
5318         if(!cell){
5319             return;
5320         }
5321         
5322         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5323             cell = cell.findParent('td', false, true);
5324         }
5325         
5326         var row = cell.findParent('tr', false, true);
5327         var cellIndex = cell.dom.cellIndex;
5328         var rowIndex = row.dom.rowIndex - 1; // start from 0
5329         
5330         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5331         
5332     },
5333     
5334     onClick : function(e, el)
5335     {
5336         var cell = Roo.get(el);
5337         
5338         if(!cell || (!this.CellSelection && !this.RowSelection)){
5339             return;
5340         }
5341         
5342         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5343             cell = cell.findParent('td', false, true);
5344         }
5345         
5346         if(!cell || typeof(cell) == 'undefined'){
5347             return;
5348         }
5349         
5350         var row = cell.findParent('tr', false, true);
5351         
5352         if(!row || typeof(row) == 'undefined'){
5353             return;
5354         }
5355         
5356         var cellIndex = cell.dom.cellIndex;
5357         var rowIndex = this.getRowIndex(row);
5358         
5359         if(this.CellSelection){
5360             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5361         }
5362         
5363         if(this.RowSelection){
5364             this.fireEvent('rowclick', this, row, rowIndex, e);
5365         }
5366         
5367         
5368     },
5369     
5370     onDblClick : function(e,el)
5371     {
5372         var cell = Roo.get(el);
5373         
5374         if(!cell || (!this.CellSelection && !this.RowSelection)){
5375             return;
5376         }
5377         
5378         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5379             cell = cell.findParent('td', false, true);
5380         }
5381         
5382         if(!cell || typeof(cell) == 'undefined'){
5383             return;
5384         }
5385         
5386         var row = cell.findParent('tr', false, true);
5387         
5388         if(!row || typeof(row) == 'undefined'){
5389             return;
5390         }
5391         
5392         var cellIndex = cell.dom.cellIndex;
5393         var rowIndex = this.getRowIndex(row);
5394         
5395         if(this.CellSelection){
5396             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5397         }
5398         
5399         if(this.RowSelection){
5400             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5401         }
5402     },
5403     
5404     sort : function(e,el)
5405     {
5406         var col = Roo.get(el);
5407         
5408         if(!col.hasClass('sortable')){
5409             return;
5410         }
5411         
5412         var sort = col.attr('sort');
5413         var dir = 'ASC';
5414         
5415         if(col.hasClass('glyphicon-arrow-up')){
5416             dir = 'DESC';
5417         }
5418         
5419         this.store.sortInfo = {field : sort, direction : dir};
5420         
5421         if (this.footer) {
5422             Roo.log("calling footer first");
5423             this.footer.onClick('first');
5424         } else {
5425         
5426             this.store.load({ params : { start : 0 } });
5427         }
5428     },
5429     
5430     renderHeader : function()
5431     {
5432         var header = {
5433             tag: 'thead',
5434             cn : []
5435         };
5436         
5437         var cm = this.cm;
5438         
5439         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5440             
5441             var config = cm.config[i];
5442                     
5443             var c = {
5444                 tag: 'th',
5445                 style : '',
5446                 html: cm.getColumnHeader(i)
5447             };
5448             
5449             if(typeof(config.tooltip) != 'undefined'){
5450                 c.tooltip = config.tooltip;
5451             }
5452             
5453             if(typeof(config.colspan) != 'undefined'){
5454                 c.colspan = config.colspan;
5455             }
5456             
5457             if(typeof(config.hidden) != 'undefined' && config.hidden){
5458                 c.style += ' display:none;';
5459             }
5460             
5461             if(typeof(config.dataIndex) != 'undefined'){
5462                 c.sort = config.dataIndex;
5463             }
5464             
5465             if(typeof(config.sortable) != 'undefined' && config.sortable){
5466                 c.cls = 'sortable';
5467             }
5468             
5469             if(typeof(config.align) != 'undefined' && config.align.length){
5470                 c.style += ' text-align:' + config.align + ';';
5471             }
5472             
5473             if(typeof(config.width) != 'undefined'){
5474                 c.style += ' width:' + config.width + 'px;';
5475             }
5476             
5477             header.cn.push(c)
5478         }
5479         
5480         return header;
5481     },
5482     
5483     renderBody : function()
5484     {
5485         var body = {
5486             tag: 'tbody',
5487             cn : [
5488                 {
5489                     tag: 'tr',
5490                     cn : [
5491                         {
5492                             tag : 'td',
5493                             colspan :  this.cm.getColumnCount()
5494                         }
5495                     ]
5496                 }
5497             ]
5498         };
5499         
5500         return body;
5501     },
5502     
5503     renderFooter : function()
5504     {
5505         var footer = {
5506             tag: 'tfoot',
5507             cn : [
5508                 {
5509                     tag: 'tr',
5510                     cn : [
5511                         {
5512                             tag : 'td',
5513                             colspan :  this.cm.getColumnCount()
5514                         }
5515                     ]
5516                 }
5517             ]
5518         };
5519         
5520         return footer;
5521     },
5522     
5523     
5524     
5525     onLoad : function()
5526     {
5527         Roo.log('ds onload');
5528         this.clear();
5529         
5530         var _this = this;
5531         var cm = this.cm;
5532         var ds = this.store;
5533         
5534         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5535             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5536             
5537             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5538                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5539             }
5540             
5541             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5542                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5543             }
5544         });
5545         
5546         var tbody =  this.mainBody;
5547               
5548         if(ds.getCount() > 0){
5549             ds.data.each(function(d,rowIndex){
5550                 var row =  this.renderRow(cm, ds, rowIndex);
5551                 
5552                 tbody.createChild(row);
5553                 
5554                 var _this = this;
5555                 
5556                 if(row.cellObjects.length){
5557                     Roo.each(row.cellObjects, function(r){
5558                         _this.renderCellObject(r);
5559                     })
5560                 }
5561                 
5562             }, this);
5563         }
5564         
5565         Roo.each(this.el.select('tbody td', true).elements, function(e){
5566             e.on('mouseover', _this.onMouseover, _this);
5567         });
5568         
5569         Roo.each(this.el.select('tbody td', true).elements, function(e){
5570             e.on('mouseout', _this.onMouseout, _this);
5571         });
5572         this.fireEvent('rowsrendered', this);
5573         //if(this.loadMask){
5574         //    this.maskEl.hide();
5575         //}
5576     },
5577     
5578     
5579     onUpdate : function(ds,record)
5580     {
5581         this.refreshRow(record);
5582     },
5583     
5584     onRemove : function(ds, record, index, isUpdate){
5585         if(isUpdate !== true){
5586             this.fireEvent("beforerowremoved", this, index, record);
5587         }
5588         var bt = this.mainBody.dom;
5589         
5590         var rows = this.el.select('tbody > tr', true).elements;
5591         
5592         if(typeof(rows[index]) != 'undefined'){
5593             bt.removeChild(rows[index].dom);
5594         }
5595         
5596 //        if(bt.rows[index]){
5597 //            bt.removeChild(bt.rows[index]);
5598 //        }
5599         
5600         if(isUpdate !== true){
5601             //this.stripeRows(index);
5602             //this.syncRowHeights(index, index);
5603             //this.layout();
5604             this.fireEvent("rowremoved", this, index, record);
5605         }
5606     },
5607     
5608     onAdd : function(ds, records, rowIndex)
5609     {
5610         //Roo.log('on Add called');
5611         // - note this does not handle multiple adding very well..
5612         var bt = this.mainBody.dom;
5613         for (var i =0 ; i < records.length;i++) {
5614             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5615             //Roo.log(records[i]);
5616             //Roo.log(this.store.getAt(rowIndex+i));
5617             this.insertRow(this.store, rowIndex + i, false);
5618             return;
5619         }
5620         
5621     },
5622     
5623     
5624     refreshRow : function(record){
5625         var ds = this.store, index;
5626         if(typeof record == 'number'){
5627             index = record;
5628             record = ds.getAt(index);
5629         }else{
5630             index = ds.indexOf(record);
5631         }
5632         this.insertRow(ds, index, true);
5633         this.onRemove(ds, record, index+1, true);
5634         //this.syncRowHeights(index, index);
5635         //this.layout();
5636         this.fireEvent("rowupdated", this, index, record);
5637     },
5638     
5639     insertRow : function(dm, rowIndex, isUpdate){
5640         
5641         if(!isUpdate){
5642             this.fireEvent("beforerowsinserted", this, rowIndex);
5643         }
5644             //var s = this.getScrollState();
5645         var row = this.renderRow(this.cm, this.store, rowIndex);
5646         // insert before rowIndex..
5647         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5648         
5649         var _this = this;
5650                 
5651         if(row.cellObjects.length){
5652             Roo.each(row.cellObjects, function(r){
5653                 _this.renderCellObject(r);
5654             })
5655         }
5656             
5657         if(!isUpdate){
5658             this.fireEvent("rowsinserted", this, rowIndex);
5659             //this.syncRowHeights(firstRow, lastRow);
5660             //this.stripeRows(firstRow);
5661             //this.layout();
5662         }
5663         
5664     },
5665     
5666     
5667     getRowDom : function(rowIndex)
5668     {
5669         var rows = this.el.select('tbody > tr', true).elements;
5670         
5671         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5672         
5673     },
5674     // returns the object tree for a tr..
5675   
5676     
5677     renderRow : function(cm, ds, rowIndex) 
5678     {
5679         
5680         var d = ds.getAt(rowIndex);
5681         
5682         var row = {
5683             tag : 'tr',
5684             cn : []
5685         };
5686             
5687         var cellObjects = [];
5688         
5689         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5690             var config = cm.config[i];
5691             
5692             var renderer = cm.getRenderer(i);
5693             var value = '';
5694             var id = false;
5695             
5696             if(typeof(renderer) !== 'undefined'){
5697                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5698             }
5699             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5700             // and are rendered into the cells after the row is rendered - using the id for the element.
5701             
5702             if(typeof(value) === 'object'){
5703                 id = Roo.id();
5704                 cellObjects.push({
5705                     container : id,
5706                     cfg : value 
5707                 })
5708             }
5709             
5710             var rowcfg = {
5711                 record: d,
5712                 rowIndex : rowIndex,
5713                 colIndex : i,
5714                 rowClass : ''
5715             }
5716
5717             this.fireEvent('rowclass', this, rowcfg);
5718             
5719             var td = {
5720                 tag: 'td',
5721                 cls : rowcfg.rowClass,
5722                 style: '',
5723                 html: (typeof(value) === 'object') ? '' : value
5724             };
5725             
5726             if (id) {
5727                 td.id = id;
5728             }
5729             
5730             if(typeof(config.colspan) != 'undefined'){
5731                 td.colspan = config.colspan;
5732             }
5733             
5734             if(typeof(config.hidden) != 'undefined' && config.hidden){
5735                 td.style += ' display:none;';
5736             }
5737             
5738             if(typeof(config.align) != 'undefined' && config.align.length){
5739                 td.style += ' text-align:' + config.align + ';';
5740             }
5741             
5742             if(typeof(config.width) != 'undefined'){
5743                 td.style += ' width:' +  config.width + 'px;';
5744             }
5745             
5746             if(typeof(config.cursor) != 'undefined'){
5747                 td.style += ' cursor:' +  config.cursor + ';';
5748             }
5749              
5750             row.cn.push(td);
5751            
5752         }
5753         
5754         row.cellObjects = cellObjects;
5755         
5756         return row;
5757           
5758     },
5759     
5760     
5761     
5762     onBeforeLoad : function()
5763     {
5764         //Roo.log('ds onBeforeLoad');
5765         
5766         //this.clear();
5767         
5768         //if(this.loadMask){
5769         //    this.maskEl.show();
5770         //}
5771     },
5772      /**
5773      * Remove all rows
5774      */
5775     clear : function()
5776     {
5777         this.el.select('tbody', true).first().dom.innerHTML = '';
5778     },
5779     /**
5780      * Show or hide a row.
5781      * @param {Number} rowIndex to show or hide
5782      * @param {Boolean} state hide
5783      */
5784     setRowVisibility : function(rowIndex, state)
5785     {
5786         var bt = this.mainBody.dom;
5787         
5788         var rows = this.el.select('tbody > tr', true).elements;
5789         
5790         if(typeof(rows[rowIndex]) == 'undefined'){
5791             return;
5792         }
5793         rows[rowIndex].dom.style.display = state ? '' : 'none';
5794     },
5795     
5796     
5797     getSelectionModel : function(){
5798         if(!this.selModel){
5799             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5800         }
5801         return this.selModel;
5802     },
5803     /*
5804      * Render the Roo.bootstrap object from renderder
5805      */
5806     renderCellObject : function(r)
5807     {
5808         var _this = this;
5809         
5810         var t = r.cfg.render(r.container);
5811         
5812         if(r.cfg.cn){
5813             Roo.each(r.cfg.cn, function(c){
5814                 var child = {
5815                     container: t.getChildContainer(),
5816                     cfg: c
5817                 }
5818                 _this.renderCellObject(child);
5819             })
5820         }
5821     },
5822     
5823     getRowIndex : function(row)
5824     {
5825         var rowIndex = -1;
5826         
5827         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
5828             if(el != row){
5829                 return;
5830             }
5831             
5832             rowIndex = index;
5833         });
5834         
5835         return rowIndex;
5836     }
5837    
5838 });
5839
5840  
5841
5842  /*
5843  * - LGPL
5844  *
5845  * table cell
5846  * 
5847  */
5848
5849 /**
5850  * @class Roo.bootstrap.TableCell
5851  * @extends Roo.bootstrap.Component
5852  * Bootstrap TableCell class
5853  * @cfg {String} html cell contain text
5854  * @cfg {String} cls cell class
5855  * @cfg {String} tag cell tag (td|th) default td
5856  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5857  * @cfg {String} align Aligns the content in a cell
5858  * @cfg {String} axis Categorizes cells
5859  * @cfg {String} bgcolor Specifies the background color of a cell
5860  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5861  * @cfg {Number} colspan Specifies the number of columns a cell should span
5862  * @cfg {String} headers Specifies one or more header cells a cell is related to
5863  * @cfg {Number} height Sets the height of a cell
5864  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5865  * @cfg {Number} rowspan Sets the number of rows a cell should span
5866  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5867  * @cfg {String} valign Vertical aligns the content in a cell
5868  * @cfg {Number} width Specifies the width of a cell
5869  * 
5870  * @constructor
5871  * Create a new TableCell
5872  * @param {Object} config The config object
5873  */
5874
5875 Roo.bootstrap.TableCell = function(config){
5876     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5877 };
5878
5879 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5880     
5881     html: false,
5882     cls: false,
5883     tag: false,
5884     abbr: false,
5885     align: false,
5886     axis: false,
5887     bgcolor: false,
5888     charoff: false,
5889     colspan: false,
5890     headers: false,
5891     height: false,
5892     nowrap: false,
5893     rowspan: false,
5894     scope: false,
5895     valign: false,
5896     width: false,
5897     
5898     
5899     getAutoCreate : function(){
5900         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5901         
5902         cfg = {
5903             tag: 'td'
5904         }
5905         
5906         if(this.tag){
5907             cfg.tag = this.tag;
5908         }
5909         
5910         if (this.html) {
5911             cfg.html=this.html
5912         }
5913         if (this.cls) {
5914             cfg.cls=this.cls
5915         }
5916         if (this.abbr) {
5917             cfg.abbr=this.abbr
5918         }
5919         if (this.align) {
5920             cfg.align=this.align
5921         }
5922         if (this.axis) {
5923             cfg.axis=this.axis
5924         }
5925         if (this.bgcolor) {
5926             cfg.bgcolor=this.bgcolor
5927         }
5928         if (this.charoff) {
5929             cfg.charoff=this.charoff
5930         }
5931         if (this.colspan) {
5932             cfg.colspan=this.colspan
5933         }
5934         if (this.headers) {
5935             cfg.headers=this.headers
5936         }
5937         if (this.height) {
5938             cfg.height=this.height
5939         }
5940         if (this.nowrap) {
5941             cfg.nowrap=this.nowrap
5942         }
5943         if (this.rowspan) {
5944             cfg.rowspan=this.rowspan
5945         }
5946         if (this.scope) {
5947             cfg.scope=this.scope
5948         }
5949         if (this.valign) {
5950             cfg.valign=this.valign
5951         }
5952         if (this.width) {
5953             cfg.width=this.width
5954         }
5955         
5956         
5957         return cfg;
5958     }
5959    
5960 });
5961
5962  
5963
5964  /*
5965  * - LGPL
5966  *
5967  * table row
5968  * 
5969  */
5970
5971 /**
5972  * @class Roo.bootstrap.TableRow
5973  * @extends Roo.bootstrap.Component
5974  * Bootstrap TableRow class
5975  * @cfg {String} cls row class
5976  * @cfg {String} align Aligns the content in a table row
5977  * @cfg {String} bgcolor Specifies a background color for a table row
5978  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5979  * @cfg {String} valign Vertical aligns the content in a table row
5980  * 
5981  * @constructor
5982  * Create a new TableRow
5983  * @param {Object} config The config object
5984  */
5985
5986 Roo.bootstrap.TableRow = function(config){
5987     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5988 };
5989
5990 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5991     
5992     cls: false,
5993     align: false,
5994     bgcolor: false,
5995     charoff: false,
5996     valign: false,
5997     
5998     getAutoCreate : function(){
5999         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6000         
6001         cfg = {
6002             tag: 'tr'
6003         }
6004             
6005         if(this.cls){
6006             cfg.cls = this.cls;
6007         }
6008         if(this.align){
6009             cfg.align = this.align;
6010         }
6011         if(this.bgcolor){
6012             cfg.bgcolor = this.bgcolor;
6013         }
6014         if(this.charoff){
6015             cfg.charoff = this.charoff;
6016         }
6017         if(this.valign){
6018             cfg.valign = this.valign;
6019         }
6020         
6021         return cfg;
6022     }
6023    
6024 });
6025
6026  
6027
6028  /*
6029  * - LGPL
6030  *
6031  * table body
6032  * 
6033  */
6034
6035 /**
6036  * @class Roo.bootstrap.TableBody
6037  * @extends Roo.bootstrap.Component
6038  * Bootstrap TableBody class
6039  * @cfg {String} cls element class
6040  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6041  * @cfg {String} align Aligns the content inside the element
6042  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6043  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6044  * 
6045  * @constructor
6046  * Create a new TableBody
6047  * @param {Object} config The config object
6048  */
6049
6050 Roo.bootstrap.TableBody = function(config){
6051     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6052 };
6053
6054 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6055     
6056     cls: false,
6057     tag: false,
6058     align: false,
6059     charoff: false,
6060     valign: false,
6061     
6062     getAutoCreate : function(){
6063         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6064         
6065         cfg = {
6066             tag: 'tbody'
6067         }
6068             
6069         if (this.cls) {
6070             cfg.cls=this.cls
6071         }
6072         if(this.tag){
6073             cfg.tag = this.tag;
6074         }
6075         
6076         if(this.align){
6077             cfg.align = this.align;
6078         }
6079         if(this.charoff){
6080             cfg.charoff = this.charoff;
6081         }
6082         if(this.valign){
6083             cfg.valign = this.valign;
6084         }
6085         
6086         return cfg;
6087     }
6088     
6089     
6090 //    initEvents : function()
6091 //    {
6092 //        
6093 //        if(!this.store){
6094 //            return;
6095 //        }
6096 //        
6097 //        this.store = Roo.factory(this.store, Roo.data);
6098 //        this.store.on('load', this.onLoad, this);
6099 //        
6100 //        this.store.load();
6101 //        
6102 //    },
6103 //    
6104 //    onLoad: function () 
6105 //    {   
6106 //        this.fireEvent('load', this);
6107 //    }
6108 //    
6109 //   
6110 });
6111
6112  
6113
6114  /*
6115  * Based on:
6116  * Ext JS Library 1.1.1
6117  * Copyright(c) 2006-2007, Ext JS, LLC.
6118  *
6119  * Originally Released Under LGPL - original licence link has changed is not relivant.
6120  *
6121  * Fork - LGPL
6122  * <script type="text/javascript">
6123  */
6124
6125 // as we use this in bootstrap.
6126 Roo.namespace('Roo.form');
6127  /**
6128  * @class Roo.form.Action
6129  * Internal Class used to handle form actions
6130  * @constructor
6131  * @param {Roo.form.BasicForm} el The form element or its id
6132  * @param {Object} config Configuration options
6133  */
6134
6135  
6136  
6137 // define the action interface
6138 Roo.form.Action = function(form, options){
6139     this.form = form;
6140     this.options = options || {};
6141 };
6142 /**
6143  * Client Validation Failed
6144  * @const 
6145  */
6146 Roo.form.Action.CLIENT_INVALID = 'client';
6147 /**
6148  * Server Validation Failed
6149  * @const 
6150  */
6151 Roo.form.Action.SERVER_INVALID = 'server';
6152  /**
6153  * Connect to Server Failed
6154  * @const 
6155  */
6156 Roo.form.Action.CONNECT_FAILURE = 'connect';
6157 /**
6158  * Reading Data from Server Failed
6159  * @const 
6160  */
6161 Roo.form.Action.LOAD_FAILURE = 'load';
6162
6163 Roo.form.Action.prototype = {
6164     type : 'default',
6165     failureType : undefined,
6166     response : undefined,
6167     result : undefined,
6168
6169     // interface method
6170     run : function(options){
6171
6172     },
6173
6174     // interface method
6175     success : function(response){
6176
6177     },
6178
6179     // interface method
6180     handleResponse : function(response){
6181
6182     },
6183
6184     // default connection failure
6185     failure : function(response){
6186         
6187         this.response = response;
6188         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6189         this.form.afterAction(this, false);
6190     },
6191
6192     processResponse : function(response){
6193         this.response = response;
6194         if(!response.responseText){
6195             return true;
6196         }
6197         this.result = this.handleResponse(response);
6198         return this.result;
6199     },
6200
6201     // utility functions used internally
6202     getUrl : function(appendParams){
6203         var url = this.options.url || this.form.url || this.form.el.dom.action;
6204         if(appendParams){
6205             var p = this.getParams();
6206             if(p){
6207                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6208             }
6209         }
6210         return url;
6211     },
6212
6213     getMethod : function(){
6214         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6215     },
6216
6217     getParams : function(){
6218         var bp = this.form.baseParams;
6219         var p = this.options.params;
6220         if(p){
6221             if(typeof p == "object"){
6222                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6223             }else if(typeof p == 'string' && bp){
6224                 p += '&' + Roo.urlEncode(bp);
6225             }
6226         }else if(bp){
6227             p = Roo.urlEncode(bp);
6228         }
6229         return p;
6230     },
6231
6232     createCallback : function(){
6233         return {
6234             success: this.success,
6235             failure: this.failure,
6236             scope: this,
6237             timeout: (this.form.timeout*1000),
6238             upload: this.form.fileUpload ? this.success : undefined
6239         };
6240     }
6241 };
6242
6243 Roo.form.Action.Submit = function(form, options){
6244     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6245 };
6246
6247 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6248     type : 'submit',
6249
6250     haveProgress : false,
6251     uploadComplete : false,
6252     
6253     // uploadProgress indicator.
6254     uploadProgress : function()
6255     {
6256         if (!this.form.progressUrl) {
6257             return;
6258         }
6259         
6260         if (!this.haveProgress) {
6261             Roo.MessageBox.progress("Uploading", "Uploading");
6262         }
6263         if (this.uploadComplete) {
6264            Roo.MessageBox.hide();
6265            return;
6266         }
6267         
6268         this.haveProgress = true;
6269    
6270         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6271         
6272         var c = new Roo.data.Connection();
6273         c.request({
6274             url : this.form.progressUrl,
6275             params: {
6276                 id : uid
6277             },
6278             method: 'GET',
6279             success : function(req){
6280                //console.log(data);
6281                 var rdata = false;
6282                 var edata;
6283                 try  {
6284                    rdata = Roo.decode(req.responseText)
6285                 } catch (e) {
6286                     Roo.log("Invalid data from server..");
6287                     Roo.log(edata);
6288                     return;
6289                 }
6290                 if (!rdata || !rdata.success) {
6291                     Roo.log(rdata);
6292                     Roo.MessageBox.alert(Roo.encode(rdata));
6293                     return;
6294                 }
6295                 var data = rdata.data;
6296                 
6297                 if (this.uploadComplete) {
6298                    Roo.MessageBox.hide();
6299                    return;
6300                 }
6301                    
6302                 if (data){
6303                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6304                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6305                     );
6306                 }
6307                 this.uploadProgress.defer(2000,this);
6308             },
6309        
6310             failure: function(data) {
6311                 Roo.log('progress url failed ');
6312                 Roo.log(data);
6313             },
6314             scope : this
6315         });
6316            
6317     },
6318     
6319     
6320     run : function()
6321     {
6322         // run get Values on the form, so it syncs any secondary forms.
6323         this.form.getValues();
6324         
6325         var o = this.options;
6326         var method = this.getMethod();
6327         var isPost = method == 'POST';
6328         if(o.clientValidation === false || this.form.isValid()){
6329             
6330             if (this.form.progressUrl) {
6331                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6332                     (new Date() * 1) + '' + Math.random());
6333                     
6334             } 
6335             
6336             
6337             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6338                 form:this.form.el.dom,
6339                 url:this.getUrl(!isPost),
6340                 method: method,
6341                 params:isPost ? this.getParams() : null,
6342                 isUpload: this.form.fileUpload
6343             }));
6344             
6345             this.uploadProgress();
6346
6347         }else if (o.clientValidation !== false){ // client validation failed
6348             this.failureType = Roo.form.Action.CLIENT_INVALID;
6349             this.form.afterAction(this, false);
6350         }
6351     },
6352
6353     success : function(response)
6354     {
6355         this.uploadComplete= true;
6356         if (this.haveProgress) {
6357             Roo.MessageBox.hide();
6358         }
6359         
6360         
6361         var result = this.processResponse(response);
6362         if(result === true || result.success){
6363             this.form.afterAction(this, true);
6364             return;
6365         }
6366         if(result.errors){
6367             this.form.markInvalid(result.errors);
6368             this.failureType = Roo.form.Action.SERVER_INVALID;
6369         }
6370         this.form.afterAction(this, false);
6371     },
6372     failure : function(response)
6373     {
6374         this.uploadComplete= true;
6375         if (this.haveProgress) {
6376             Roo.MessageBox.hide();
6377         }
6378         
6379         this.response = response;
6380         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6381         this.form.afterAction(this, false);
6382     },
6383     
6384     handleResponse : function(response){
6385         if(this.form.errorReader){
6386             var rs = this.form.errorReader.read(response);
6387             var errors = [];
6388             if(rs.records){
6389                 for(var i = 0, len = rs.records.length; i < len; i++) {
6390                     var r = rs.records[i];
6391                     errors[i] = r.data;
6392                 }
6393             }
6394             if(errors.length < 1){
6395                 errors = null;
6396             }
6397             return {
6398                 success : rs.success,
6399                 errors : errors
6400             };
6401         }
6402         var ret = false;
6403         try {
6404             ret = Roo.decode(response.responseText);
6405         } catch (e) {
6406             ret = {
6407                 success: false,
6408                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6409                 errors : []
6410             };
6411         }
6412         return ret;
6413         
6414     }
6415 });
6416
6417
6418 Roo.form.Action.Load = function(form, options){
6419     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6420     this.reader = this.form.reader;
6421 };
6422
6423 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6424     type : 'load',
6425
6426     run : function(){
6427         
6428         Roo.Ajax.request(Roo.apply(
6429                 this.createCallback(), {
6430                     method:this.getMethod(),
6431                     url:this.getUrl(false),
6432                     params:this.getParams()
6433         }));
6434     },
6435
6436     success : function(response){
6437         
6438         var result = this.processResponse(response);
6439         if(result === true || !result.success || !result.data){
6440             this.failureType = Roo.form.Action.LOAD_FAILURE;
6441             this.form.afterAction(this, false);
6442             return;
6443         }
6444         this.form.clearInvalid();
6445         this.form.setValues(result.data);
6446         this.form.afterAction(this, true);
6447     },
6448
6449     handleResponse : function(response){
6450         if(this.form.reader){
6451             var rs = this.form.reader.read(response);
6452             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6453             return {
6454                 success : rs.success,
6455                 data : data
6456             };
6457         }
6458         return Roo.decode(response.responseText);
6459     }
6460 });
6461
6462 Roo.form.Action.ACTION_TYPES = {
6463     'load' : Roo.form.Action.Load,
6464     'submit' : Roo.form.Action.Submit
6465 };/*
6466  * - LGPL
6467  *
6468  * form
6469  * 
6470  */
6471
6472 /**
6473  * @class Roo.bootstrap.Form
6474  * @extends Roo.bootstrap.Component
6475  * Bootstrap Form class
6476  * @cfg {String} method  GET | POST (default POST)
6477  * @cfg {String} labelAlign top | left (default top)
6478  * @cfg {String} align left  | right - for navbars
6479  * @cfg {Boolean} loadMask load mask when submit (default true)
6480
6481  * 
6482  * @constructor
6483  * Create a new Form
6484  * @param {Object} config The config object
6485  */
6486
6487
6488 Roo.bootstrap.Form = function(config){
6489     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6490     this.addEvents({
6491         /**
6492          * @event clientvalidation
6493          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6494          * @param {Form} this
6495          * @param {Boolean} valid true if the form has passed client-side validation
6496          */
6497         clientvalidation: true,
6498         /**
6499          * @event beforeaction
6500          * Fires before any action is performed. Return false to cancel the action.
6501          * @param {Form} this
6502          * @param {Action} action The action to be performed
6503          */
6504         beforeaction: true,
6505         /**
6506          * @event actionfailed
6507          * Fires when an action fails.
6508          * @param {Form} this
6509          * @param {Action} action The action that failed
6510          */
6511         actionfailed : true,
6512         /**
6513          * @event actioncomplete
6514          * Fires when an action is completed.
6515          * @param {Form} this
6516          * @param {Action} action The action that completed
6517          */
6518         actioncomplete : true
6519     });
6520     
6521 };
6522
6523 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6524       
6525      /**
6526      * @cfg {String} method
6527      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6528      */
6529     method : 'POST',
6530     /**
6531      * @cfg {String} url
6532      * The URL to use for form actions if one isn't supplied in the action options.
6533      */
6534     /**
6535      * @cfg {Boolean} fileUpload
6536      * Set to true if this form is a file upload.
6537      */
6538      
6539     /**
6540      * @cfg {Object} baseParams
6541      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6542      */
6543       
6544     /**
6545      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6546      */
6547     timeout: 30,
6548     /**
6549      * @cfg {Sting} align (left|right) for navbar forms
6550      */
6551     align : 'left',
6552
6553     // private
6554     activeAction : null,
6555  
6556     /**
6557      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6558      * element by passing it or its id or mask the form itself by passing in true.
6559      * @type Mixed
6560      */
6561     waitMsgTarget : false,
6562     
6563     loadMask : true,
6564     
6565     getAutoCreate : function(){
6566         
6567         var cfg = {
6568             tag: 'form',
6569             method : this.method || 'POST',
6570             id : this.id || Roo.id(),
6571             cls : ''
6572         }
6573         if (this.parent().xtype.match(/^Nav/)) {
6574             cfg.cls = 'navbar-form navbar-' + this.align;
6575             
6576         }
6577         
6578         if (this.labelAlign == 'left' ) {
6579             cfg.cls += ' form-horizontal';
6580         }
6581         
6582         
6583         return cfg;
6584     },
6585     initEvents : function()
6586     {
6587         this.el.on('submit', this.onSubmit, this);
6588         // this was added as random key presses on the form where triggering form submit.
6589         this.el.on('keypress', function(e) {
6590             if (e.getCharCode() != 13) {
6591                 return true;
6592             }
6593             // we might need to allow it for textareas.. and some other items.
6594             // check e.getTarget().
6595             
6596             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6597                 return true;
6598             }
6599         
6600             Roo.log("keypress blocked");
6601             
6602             e.preventDefault();
6603             return false;
6604         });
6605         
6606     },
6607     // private
6608     onSubmit : function(e){
6609         e.stopEvent();
6610     },
6611     
6612      /**
6613      * Returns true if client-side validation on the form is successful.
6614      * @return Boolean
6615      */
6616     isValid : function(){
6617         var items = this.getItems();
6618         var valid = true;
6619         items.each(function(f){
6620            if(!f.validate()){
6621                valid = false;
6622                
6623            }
6624         });
6625         return valid;
6626     },
6627     /**
6628      * Returns true if any fields in this form have changed since their original load.
6629      * @return Boolean
6630      */
6631     isDirty : function(){
6632         var dirty = false;
6633         var items = this.getItems();
6634         items.each(function(f){
6635            if(f.isDirty()){
6636                dirty = true;
6637                return false;
6638            }
6639            return true;
6640         });
6641         return dirty;
6642     },
6643      /**
6644      * Performs a predefined action (submit or load) or custom actions you define on this form.
6645      * @param {String} actionName The name of the action type
6646      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6647      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6648      * accept other config options):
6649      * <pre>
6650 Property          Type             Description
6651 ----------------  ---------------  ----------------------------------------------------------------------------------
6652 url               String           The url for the action (defaults to the form's url)
6653 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6654 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6655 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6656                                    validate the form on the client (defaults to false)
6657      * </pre>
6658      * @return {BasicForm} this
6659      */
6660     doAction : function(action, options){
6661         if(typeof action == 'string'){
6662             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6663         }
6664         if(this.fireEvent('beforeaction', this, action) !== false){
6665             this.beforeAction(action);
6666             action.run.defer(100, action);
6667         }
6668         return this;
6669     },
6670     
6671     // private
6672     beforeAction : function(action){
6673         var o = action.options;
6674         
6675         if(this.loadMask){
6676             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6677         }
6678         // not really supported yet.. ??
6679         
6680         //if(this.waitMsgTarget === true){
6681         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6682         //}else if(this.waitMsgTarget){
6683         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6684         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6685         //}else {
6686         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6687        // }
6688          
6689     },
6690
6691     // private
6692     afterAction : function(action, success){
6693         this.activeAction = null;
6694         var o = action.options;
6695         
6696         //if(this.waitMsgTarget === true){
6697             this.el.unmask();
6698         //}else if(this.waitMsgTarget){
6699         //    this.waitMsgTarget.unmask();
6700         //}else{
6701         //    Roo.MessageBox.updateProgress(1);
6702         //    Roo.MessageBox.hide();
6703        // }
6704         // 
6705         if(success){
6706             if(o.reset){
6707                 this.reset();
6708             }
6709             Roo.callback(o.success, o.scope, [this, action]);
6710             this.fireEvent('actioncomplete', this, action);
6711             
6712         }else{
6713             
6714             // failure condition..
6715             // we have a scenario where updates need confirming.
6716             // eg. if a locking scenario exists..
6717             // we look for { errors : { needs_confirm : true }} in the response.
6718             if (
6719                 (typeof(action.result) != 'undefined')  &&
6720                 (typeof(action.result.errors) != 'undefined')  &&
6721                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6722            ){
6723                 var _t = this;
6724                 Roo.log("not supported yet");
6725                  /*
6726                 
6727                 Roo.MessageBox.confirm(
6728                     "Change requires confirmation",
6729                     action.result.errorMsg,
6730                     function(r) {
6731                         if (r != 'yes') {
6732                             return;
6733                         }
6734                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6735                     }
6736                     
6737                 );
6738                 */
6739                 
6740                 
6741                 return;
6742             }
6743             
6744             Roo.callback(o.failure, o.scope, [this, action]);
6745             // show an error message if no failed handler is set..
6746             if (!this.hasListener('actionfailed')) {
6747                 Roo.log("need to add dialog support");
6748                 /*
6749                 Roo.MessageBox.alert("Error",
6750                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6751                         action.result.errorMsg :
6752                         "Saving Failed, please check your entries or try again"
6753                 );
6754                 */
6755             }
6756             
6757             this.fireEvent('actionfailed', this, action);
6758         }
6759         
6760     },
6761     /**
6762      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6763      * @param {String} id The value to search for
6764      * @return Field
6765      */
6766     findField : function(id){
6767         var items = this.getItems();
6768         var field = items.get(id);
6769         if(!field){
6770              items.each(function(f){
6771                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6772                     field = f;
6773                     return false;
6774                 }
6775                 return true;
6776             });
6777         }
6778         return field || null;
6779     },
6780      /**
6781      * Mark fields in this form invalid in bulk.
6782      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6783      * @return {BasicForm} this
6784      */
6785     markInvalid : function(errors){
6786         if(errors instanceof Array){
6787             for(var i = 0, len = errors.length; i < len; i++){
6788                 var fieldError = errors[i];
6789                 var f = this.findField(fieldError.id);
6790                 if(f){
6791                     f.markInvalid(fieldError.msg);
6792                 }
6793             }
6794         }else{
6795             var field, id;
6796             for(id in errors){
6797                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6798                     field.markInvalid(errors[id]);
6799                 }
6800             }
6801         }
6802         //Roo.each(this.childForms || [], function (f) {
6803         //    f.markInvalid(errors);
6804         //});
6805         
6806         return this;
6807     },
6808
6809     /**
6810      * Set values for fields in this form in bulk.
6811      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6812      * @return {BasicForm} this
6813      */
6814     setValues : function(values){
6815         if(values instanceof Array){ // array of objects
6816             for(var i = 0, len = values.length; i < len; i++){
6817                 var v = values[i];
6818                 var f = this.findField(v.id);
6819                 if(f){
6820                     f.setValue(v.value);
6821                     if(this.trackResetOnLoad){
6822                         f.originalValue = f.getValue();
6823                     }
6824                 }
6825             }
6826         }else{ // object hash
6827             var field, id;
6828             for(id in values){
6829                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6830                     
6831                     if (field.setFromData && 
6832                         field.valueField && 
6833                         field.displayField &&
6834                         // combos' with local stores can 
6835                         // be queried via setValue()
6836                         // to set their value..
6837                         (field.store && !field.store.isLocal)
6838                         ) {
6839                         // it's a combo
6840                         var sd = { };
6841                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6842                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6843                         field.setFromData(sd);
6844                         
6845                     } else {
6846                         field.setValue(values[id]);
6847                     }
6848                     
6849                     
6850                     if(this.trackResetOnLoad){
6851                         field.originalValue = field.getValue();
6852                     }
6853                 }
6854             }
6855         }
6856          
6857         //Roo.each(this.childForms || [], function (f) {
6858         //    f.setValues(values);
6859         //});
6860                 
6861         return this;
6862     },
6863
6864     /**
6865      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6866      * they are returned as an array.
6867      * @param {Boolean} asString
6868      * @return {Object}
6869      */
6870     getValues : function(asString){
6871         //if (this.childForms) {
6872             // copy values from the child forms
6873         //    Roo.each(this.childForms, function (f) {
6874         //        this.setValues(f.getValues());
6875         //    }, this);
6876         //}
6877         
6878         
6879         
6880         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6881         if(asString === true){
6882             return fs;
6883         }
6884         return Roo.urlDecode(fs);
6885     },
6886     
6887     /**
6888      * Returns the fields in this form as an object with key/value pairs. 
6889      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6890      * @return {Object}
6891      */
6892     getFieldValues : function(with_hidden)
6893     {
6894         var items = this.getItems();
6895         var ret = {};
6896         items.each(function(f){
6897             if (!f.getName()) {
6898                 return;
6899             }
6900             var v = f.getValue();
6901             if (f.inputType =='radio') {
6902                 if (typeof(ret[f.getName()]) == 'undefined') {
6903                     ret[f.getName()] = ''; // empty..
6904                 }
6905                 
6906                 if (!f.el.dom.checked) {
6907                     return;
6908                     
6909                 }
6910                 v = f.el.dom.value;
6911                 
6912             }
6913             
6914             // not sure if this supported any more..
6915             if ((typeof(v) == 'object') && f.getRawValue) {
6916                 v = f.getRawValue() ; // dates..
6917             }
6918             // combo boxes where name != hiddenName...
6919             if (f.name != f.getName()) {
6920                 ret[f.name] = f.getRawValue();
6921             }
6922             ret[f.getName()] = v;
6923         });
6924         
6925         return ret;
6926     },
6927
6928     /**
6929      * Clears all invalid messages in this form.
6930      * @return {BasicForm} this
6931      */
6932     clearInvalid : function(){
6933         var items = this.getItems();
6934         
6935         items.each(function(f){
6936            f.clearInvalid();
6937         });
6938         
6939         
6940         
6941         return this;
6942     },
6943
6944     /**
6945      * Resets this form.
6946      * @return {BasicForm} this
6947      */
6948     reset : function(){
6949         var items = this.getItems();
6950         items.each(function(f){
6951             f.reset();
6952         });
6953         
6954         Roo.each(this.childForms || [], function (f) {
6955             f.reset();
6956         });
6957        
6958         
6959         return this;
6960     },
6961     getItems : function()
6962     {
6963         var r=new Roo.util.MixedCollection(false, function(o){
6964             return o.id || (o.id = Roo.id());
6965         });
6966         var iter = function(el) {
6967             if (el.inputEl) {
6968                 r.add(el);
6969             }
6970             if (!el.items) {
6971                 return;
6972             }
6973             Roo.each(el.items,function(e) {
6974                 iter(e);
6975             });
6976             
6977             
6978         };
6979         
6980         iter(this);
6981         return r;
6982         
6983         
6984         
6985         
6986     }
6987     
6988 });
6989
6990  
6991 /*
6992  * Based on:
6993  * Ext JS Library 1.1.1
6994  * Copyright(c) 2006-2007, Ext JS, LLC.
6995  *
6996  * Originally Released Under LGPL - original licence link has changed is not relivant.
6997  *
6998  * Fork - LGPL
6999  * <script type="text/javascript">
7000  */
7001 /**
7002  * @class Roo.form.VTypes
7003  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7004  * @singleton
7005  */
7006 Roo.form.VTypes = function(){
7007     // closure these in so they are only created once.
7008     var alpha = /^[a-zA-Z_]+$/;
7009     var alphanum = /^[a-zA-Z0-9_]+$/;
7010     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7011     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7012
7013     // All these messages and functions are configurable
7014     return {
7015         /**
7016          * The function used to validate email addresses
7017          * @param {String} value The email address
7018          */
7019         'email' : function(v){
7020             return email.test(v);
7021         },
7022         /**
7023          * The error text to display when the email validation function returns false
7024          * @type String
7025          */
7026         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7027         /**
7028          * The keystroke filter mask to be applied on email input
7029          * @type RegExp
7030          */
7031         'emailMask' : /[a-z0-9_\.\-@]/i,
7032
7033         /**
7034          * The function used to validate URLs
7035          * @param {String} value The URL
7036          */
7037         'url' : function(v){
7038             return url.test(v);
7039         },
7040         /**
7041          * The error text to display when the url validation function returns false
7042          * @type String
7043          */
7044         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7045         
7046         /**
7047          * The function used to validate alpha values
7048          * @param {String} value The value
7049          */
7050         'alpha' : function(v){
7051             return alpha.test(v);
7052         },
7053         /**
7054          * The error text to display when the alpha validation function returns false
7055          * @type String
7056          */
7057         'alphaText' : 'This field should only contain letters and _',
7058         /**
7059          * The keystroke filter mask to be applied on alpha input
7060          * @type RegExp
7061          */
7062         'alphaMask' : /[a-z_]/i,
7063
7064         /**
7065          * The function used to validate alphanumeric values
7066          * @param {String} value The value
7067          */
7068         'alphanum' : function(v){
7069             return alphanum.test(v);
7070         },
7071         /**
7072          * The error text to display when the alphanumeric validation function returns false
7073          * @type String
7074          */
7075         'alphanumText' : 'This field should only contain letters, numbers and _',
7076         /**
7077          * The keystroke filter mask to be applied on alphanumeric input
7078          * @type RegExp
7079          */
7080         'alphanumMask' : /[a-z0-9_]/i
7081     };
7082 }();/*
7083  * - LGPL
7084  *
7085  * Input
7086  * 
7087  */
7088
7089 /**
7090  * @class Roo.bootstrap.Input
7091  * @extends Roo.bootstrap.Component
7092  * Bootstrap Input class
7093  * @cfg {Boolean} disabled is it disabled
7094  * @cfg {String} fieldLabel - the label associated
7095  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7096  * @cfg {String} name name of the input
7097  * @cfg {string} fieldLabel - the label associated
7098  * @cfg {string}  inputType - input / file submit ...
7099  * @cfg {string} placeholder - placeholder to put in text.
7100  * @cfg {string}  before - input group add on before
7101  * @cfg {string} after - input group add on after
7102  * @cfg {string} size - (lg|sm) or leave empty..
7103  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7104  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7105  * @cfg {Number} md colspan out of 12 for computer-sized screens
7106  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7107  * @cfg {string} value default value of the input
7108  * @cfg {Number} labelWidth set the width of label (0-12)
7109  * @cfg {String} labelAlign (top|left)
7110  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7111  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7112
7113  * @cfg {String} align (left|center|right) Default left
7114  * 
7115  * 
7116  * 
7117  * @constructor
7118  * Create a new Input
7119  * @param {Object} config The config object
7120  */
7121
7122 Roo.bootstrap.Input = function(config){
7123     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7124    
7125         this.addEvents({
7126             /**
7127              * @event focus
7128              * Fires when this field receives input focus.
7129              * @param {Roo.form.Field} this
7130              */
7131             focus : true,
7132             /**
7133              * @event blur
7134              * Fires when this field loses input focus.
7135              * @param {Roo.form.Field} this
7136              */
7137             blur : true,
7138             /**
7139              * @event specialkey
7140              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7141              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7142              * @param {Roo.form.Field} this
7143              * @param {Roo.EventObject} e The event object
7144              */
7145             specialkey : true,
7146             /**
7147              * @event change
7148              * Fires just before the field blurs if the field value has changed.
7149              * @param {Roo.form.Field} this
7150              * @param {Mixed} newValue The new value
7151              * @param {Mixed} oldValue The original value
7152              */
7153             change : true,
7154             /**
7155              * @event invalid
7156              * Fires after the field has been marked as invalid.
7157              * @param {Roo.form.Field} this
7158              * @param {String} msg The validation message
7159              */
7160             invalid : true,
7161             /**
7162              * @event valid
7163              * Fires after the field has been validated with no errors.
7164              * @param {Roo.form.Field} this
7165              */
7166             valid : true,
7167              /**
7168              * @event keyup
7169              * Fires after the key up
7170              * @param {Roo.form.Field} this
7171              * @param {Roo.EventObject}  e The event Object
7172              */
7173             keyup : true
7174         });
7175 };
7176
7177 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7178      /**
7179      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7180       automatic validation (defaults to "keyup").
7181      */
7182     validationEvent : "keyup",
7183      /**
7184      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7185      */
7186     validateOnBlur : true,
7187     /**
7188      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7189      */
7190     validationDelay : 250,
7191      /**
7192      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7193      */
7194     focusClass : "x-form-focus",  // not needed???
7195     
7196        
7197     /**
7198      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7199      */
7200     invalidClass : "has-warning",
7201     
7202     /**
7203      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7204      */
7205     validClass : "has-success",
7206     
7207     /**
7208      * @cfg {Boolean} hasFeedback (true|false) default true
7209      */
7210     hasFeedback : true,
7211     
7212     /**
7213      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7214      */
7215     invalidFeedbackClass : "glyphicon-warning-sign",
7216     
7217     /**
7218      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7219      */
7220     validFeedbackClass : "glyphicon-ok",
7221     
7222     /**
7223      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7224      */
7225     selectOnFocus : false,
7226     
7227      /**
7228      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7229      */
7230     maskRe : null,
7231        /**
7232      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7233      */
7234     vtype : null,
7235     
7236       /**
7237      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7238      */
7239     disableKeyFilter : false,
7240     
7241        /**
7242      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7243      */
7244     disabled : false,
7245      /**
7246      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7247      */
7248     allowBlank : true,
7249     /**
7250      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7251      */
7252     blankText : "This field is required",
7253     
7254      /**
7255      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7256      */
7257     minLength : 0,
7258     /**
7259      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7260      */
7261     maxLength : Number.MAX_VALUE,
7262     /**
7263      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7264      */
7265     minLengthText : "The minimum length for this field is {0}",
7266     /**
7267      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7268      */
7269     maxLengthText : "The maximum length for this field is {0}",
7270   
7271     
7272     /**
7273      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7274      * If available, this function will be called only after the basic validators all return true, and will be passed the
7275      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7276      */
7277     validator : null,
7278     /**
7279      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7280      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7281      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7282      */
7283     regex : null,
7284     /**
7285      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7286      */
7287     regexText : "",
7288     
7289     autocomplete: false,
7290     
7291     
7292     fieldLabel : '',
7293     inputType : 'text',
7294     
7295     name : false,
7296     placeholder: false,
7297     before : false,
7298     after : false,
7299     size : false,
7300     hasFocus : false,
7301     preventMark: false,
7302     isFormField : true,
7303     value : '',
7304     labelWidth : 2,
7305     labelAlign : false,
7306     readOnly : false,
7307     align : false,
7308     formatedValue : false,
7309     
7310     parentLabelAlign : function()
7311     {
7312         var parent = this;
7313         while (parent.parent()) {
7314             parent = parent.parent();
7315             if (typeof(parent.labelAlign) !='undefined') {
7316                 return parent.labelAlign;
7317             }
7318         }
7319         return 'left';
7320         
7321     },
7322     
7323     getAutoCreate : function(){
7324         
7325         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7326         
7327         var id = Roo.id();
7328         
7329         var cfg = {};
7330         
7331         if(this.inputType != 'hidden'){
7332             cfg.cls = 'form-group' //input-group
7333         }
7334         
7335         var input =  {
7336             tag: 'input',
7337             id : id,
7338             type : this.inputType,
7339             value : this.value,
7340             cls : 'form-control',
7341             placeholder : this.placeholder || '',
7342             autocomplete : this.autocomplete || 'new-password'
7343         };
7344         
7345         
7346         if(this.align){
7347             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7348         }
7349         
7350         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7351             input.maxLength = this.maxLength;
7352         }
7353         
7354         if (this.disabled) {
7355             input.disabled=true;
7356         }
7357         
7358         if (this.readOnly) {
7359             input.readonly=true;
7360         }
7361         
7362         if (this.name) {
7363             input.name = this.name;
7364         }
7365         if (this.size) {
7366             input.cls += ' input-' + this.size;
7367         }
7368         var settings=this;
7369         ['xs','sm','md','lg'].map(function(size){
7370             if (settings[size]) {
7371                 cfg.cls += ' col-' + size + '-' + settings[size];
7372             }
7373         });
7374         
7375         var inputblock = input;
7376         
7377         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7378             
7379             var feedback = {
7380                 tag: 'span',
7381                 cls: 'glyphicon form-control-feedback'
7382             };
7383
7384             inputblock = {
7385                 cls : 'has-feedback',
7386                 cn :  [
7387                     input,
7388                     feedback
7389                 ] 
7390             };  
7391         }
7392          
7393 //        var inputblock = input;
7394         
7395         if (this.before || this.after) {
7396             
7397             inputblock = {
7398                 cls : 'input-group',
7399                 cn :  [] 
7400             };
7401             
7402             if (this.before && typeof(this.before) == 'string') {
7403                 
7404                 inputblock.cn.push({
7405                     tag :'span',
7406                     cls : 'roo-input-before input-group-addon',
7407                     html : this.before
7408                 });
7409             }
7410             if (this.before && typeof(this.before) == 'object') {
7411                 this.before = Roo.factory(this.before);
7412                 Roo.log(this.before);
7413                 inputblock.cn.push({
7414                     tag :'span',
7415                     cls : 'roo-input-before input-group-' +
7416                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7417                 });
7418             }
7419             
7420             inputblock.cn.push(input);
7421             
7422             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7423                 inputblock.cls += ' has-feedback';
7424                 inputblock.cn.push(feedback);
7425             }
7426             
7427             if (this.after && typeof(this.after) == 'string') {
7428                 inputblock.cn.push({
7429                     tag :'span',
7430                     cls : 'roo-input-after input-group-addon',
7431                     html : this.after
7432                 });
7433             }
7434             if (this.after && typeof(this.after) == 'object') {
7435                 this.after = Roo.factory(this.after);
7436                 Roo.log(this.after);
7437                 inputblock.cn.push({
7438                     tag :'span',
7439                     cls : 'roo-input-after input-group-' +
7440                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7441                 });
7442             }
7443         };
7444         
7445         if (align ==='left' && this.fieldLabel.length) {
7446                 Roo.log("left and has label");
7447                 cfg.cn = [
7448                     
7449                     {
7450                         tag: 'label',
7451                         'for' :  id,
7452                         cls : 'control-label col-sm-' + this.labelWidth,
7453                         html : this.fieldLabel
7454                         
7455                     },
7456                     {
7457                         cls : "col-sm-" + (12 - this.labelWidth), 
7458                         cn: [
7459                             inputblock
7460                         ]
7461                     }
7462                     
7463                 ];
7464         } else if ( this.fieldLabel.length) {
7465                 Roo.log(" label");
7466                  cfg.cn = [
7467                    
7468                     {
7469                         tag: 'label',
7470                         //cls : 'input-group-addon',
7471                         html : this.fieldLabel
7472                         
7473                     },
7474                     
7475                     inputblock
7476                     
7477                 ];
7478
7479         } else {
7480             
7481                 Roo.log(" no label && no align");
7482                 cfg.cn = [
7483                     
7484                         inputblock
7485                     
7486                 ];
7487                 
7488                 
7489         };
7490         Roo.log('input-parentType: ' + this.parentType);
7491         
7492         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7493            cfg.cls += ' navbar-form';
7494            Roo.log(cfg);
7495         }
7496         
7497         return cfg;
7498         
7499     },
7500     /**
7501      * return the real input element.
7502      */
7503     inputEl: function ()
7504     {
7505         return this.el.select('input.form-control',true).first();
7506     },
7507     
7508     tooltipEl : function()
7509     {
7510         return this.inputEl();
7511     },
7512     
7513     setDisabled : function(v)
7514     {
7515         var i  = this.inputEl().dom;
7516         if (!v) {
7517             i.removeAttribute('disabled');
7518             return;
7519             
7520         }
7521         i.setAttribute('disabled','true');
7522     },
7523     initEvents : function()
7524     {
7525           
7526         this.inputEl().on("keydown" , this.fireKey,  this);
7527         this.inputEl().on("focus", this.onFocus,  this);
7528         this.inputEl().on("blur", this.onBlur,  this);
7529         
7530         this.inputEl().relayEvent('keyup', this);
7531
7532         // reference to original value for reset
7533         this.originalValue = this.getValue();
7534         //Roo.form.TextField.superclass.initEvents.call(this);
7535         if(this.validationEvent == 'keyup'){
7536             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7537             this.inputEl().on('keyup', this.filterValidation, this);
7538         }
7539         else if(this.validationEvent !== false){
7540             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7541         }
7542         
7543         if(this.selectOnFocus){
7544             this.on("focus", this.preFocus, this);
7545             
7546         }
7547         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7548             this.inputEl().on("keypress", this.filterKeys, this);
7549         }
7550        /* if(this.grow){
7551             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7552             this.el.on("click", this.autoSize,  this);
7553         }
7554         */
7555         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7556             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7557         }
7558         
7559         if (typeof(this.before) == 'object') {
7560             this.before.render(this.el.select('.roo-input-before',true).first());
7561         }
7562         if (typeof(this.after) == 'object') {
7563             this.after.render(this.el.select('.roo-input-after',true).first());
7564         }
7565         
7566         
7567     },
7568     filterValidation : function(e){
7569         if(!e.isNavKeyPress()){
7570             this.validationTask.delay(this.validationDelay);
7571         }
7572     },
7573      /**
7574      * Validates the field value
7575      * @return {Boolean} True if the value is valid, else false
7576      */
7577     validate : function(){
7578         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7579         if(this.disabled || this.validateValue(this.getRawValue())){
7580             this.markValid();
7581             return true;
7582         }
7583         
7584         this.markInvalid();
7585         return false;
7586     },
7587     
7588     
7589     /**
7590      * Validates a value according to the field's validation rules and marks the field as invalid
7591      * if the validation fails
7592      * @param {Mixed} value The value to validate
7593      * @return {Boolean} True if the value is valid, else false
7594      */
7595     validateValue : function(value){
7596         if(value.length < 1)  { // if it's blank
7597             if(this.allowBlank){
7598                 return true;
7599             }
7600             return false;
7601         }
7602         
7603         if(value.length < this.minLength){
7604             return false;
7605         }
7606         if(value.length > this.maxLength){
7607             return false;
7608         }
7609         if(this.vtype){
7610             var vt = Roo.form.VTypes;
7611             if(!vt[this.vtype](value, this)){
7612                 return false;
7613             }
7614         }
7615         if(typeof this.validator == "function"){
7616             var msg = this.validator(value);
7617             if(msg !== true){
7618                 return false;
7619             }
7620         }
7621         
7622         if(this.regex && !this.regex.test(value)){
7623             return false;
7624         }
7625         
7626         return true;
7627     },
7628
7629     
7630     
7631      // private
7632     fireKey : function(e){
7633         //Roo.log('field ' + e.getKey());
7634         if(e.isNavKeyPress()){
7635             this.fireEvent("specialkey", this, e);
7636         }
7637     },
7638     focus : function (selectText){
7639         if(this.rendered){
7640             this.inputEl().focus();
7641             if(selectText === true){
7642                 this.inputEl().dom.select();
7643             }
7644         }
7645         return this;
7646     } ,
7647     
7648     onFocus : function(){
7649         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7650            // this.el.addClass(this.focusClass);
7651         }
7652         if(!this.hasFocus){
7653             this.hasFocus = true;
7654             this.startValue = this.getValue();
7655             this.fireEvent("focus", this);
7656         }
7657     },
7658     
7659     beforeBlur : Roo.emptyFn,
7660
7661     
7662     // private
7663     onBlur : function(){
7664         this.beforeBlur();
7665         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7666             //this.el.removeClass(this.focusClass);
7667         }
7668         this.hasFocus = false;
7669         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7670             this.validate();
7671         }
7672         var v = this.getValue();
7673         if(String(v) !== String(this.startValue)){
7674             this.fireEvent('change', this, v, this.startValue);
7675         }
7676         this.fireEvent("blur", this);
7677     },
7678     
7679     /**
7680      * Resets the current field value to the originally loaded value and clears any validation messages
7681      */
7682     reset : function(){
7683         this.setValue(this.originalValue);
7684         this.validate();
7685     },
7686      /**
7687      * Returns the name of the field
7688      * @return {Mixed} name The name field
7689      */
7690     getName: function(){
7691         return this.name;
7692     },
7693      /**
7694      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7695      * @return {Mixed} value The field value
7696      */
7697     getValue : function(){
7698         
7699         var v = this.inputEl().getValue();
7700         
7701         return v;
7702     },
7703     /**
7704      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7705      * @return {Mixed} value The field value
7706      */
7707     getRawValue : function(){
7708         var v = this.inputEl().getValue();
7709         
7710         return v;
7711     },
7712     
7713     /**
7714      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7715      * @param {Mixed} value The value to set
7716      */
7717     setRawValue : function(v){
7718         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7719     },
7720     
7721     selectText : function(start, end){
7722         var v = this.getRawValue();
7723         if(v.length > 0){
7724             start = start === undefined ? 0 : start;
7725             end = end === undefined ? v.length : end;
7726             var d = this.inputEl().dom;
7727             if(d.setSelectionRange){
7728                 d.setSelectionRange(start, end);
7729             }else if(d.createTextRange){
7730                 var range = d.createTextRange();
7731                 range.moveStart("character", start);
7732                 range.moveEnd("character", v.length-end);
7733                 range.select();
7734             }
7735         }
7736     },
7737     
7738     /**
7739      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7740      * @param {Mixed} value The value to set
7741      */
7742     setValue : function(v){
7743         this.value = v;
7744         if(this.rendered){
7745             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7746             this.validate();
7747         }
7748     },
7749     
7750     /*
7751     processValue : function(value){
7752         if(this.stripCharsRe){
7753             var newValue = value.replace(this.stripCharsRe, '');
7754             if(newValue !== value){
7755                 this.setRawValue(newValue);
7756                 return newValue;
7757             }
7758         }
7759         return value;
7760     },
7761   */
7762     preFocus : function(){
7763         
7764         if(this.selectOnFocus){
7765             this.inputEl().dom.select();
7766         }
7767     },
7768     filterKeys : function(e){
7769         var k = e.getKey();
7770         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7771             return;
7772         }
7773         var c = e.getCharCode(), cc = String.fromCharCode(c);
7774         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7775             return;
7776         }
7777         if(!this.maskRe.test(cc)){
7778             e.stopEvent();
7779         }
7780     },
7781      /**
7782      * Clear any invalid styles/messages for this field
7783      */
7784     clearInvalid : function(){
7785         
7786         if(!this.el || this.preventMark){ // not rendered
7787             return;
7788         }
7789         this.el.removeClass(this.invalidClass);
7790         
7791         this.fireEvent('valid', this);
7792     },
7793     
7794      /**
7795      * Mark this field as valid
7796      */
7797     markValid : function(){
7798         if(!this.el  || this.preventMark){ // not rendered
7799             return;
7800         }
7801         
7802         this.el.removeClass([this.invalidClass, this.validClass]);
7803         
7804         if(this.disabled || this.allowBlank){
7805             return;
7806         }
7807         
7808         this.el.addClass(this.validClass);
7809         
7810         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
7811             
7812             var feedback = this.el.select('.form-control-feedback', true).first();
7813             
7814             if(feedback){
7815                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7816                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
7817             }
7818             
7819         }
7820         
7821         this.fireEvent('valid', this);
7822     },
7823     
7824      /**
7825      * Mark this field as invalid
7826      * @param {String} msg The validation message
7827      */
7828     markInvalid : function(msg){
7829         if(!this.el  || this.preventMark){ // not rendered
7830             return;
7831         }
7832         
7833         this.el.removeClass([this.invalidClass, this.validClass]);
7834         
7835         if(this.disabled || this.allowBlank){
7836             return;
7837         }
7838         
7839         this.el.addClass(this.invalidClass);
7840         
7841         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7842             
7843             var feedback = this.el.select('.form-control-feedback', true).first();
7844             
7845             if(feedback){
7846                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7847                 
7848                 if(this.getValue().length){
7849                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
7850                 }
7851                 
7852             }
7853             
7854         }
7855         
7856         this.fireEvent('invalid', this, msg);
7857     },
7858     // private
7859     SafariOnKeyDown : function(event)
7860     {
7861         // this is a workaround for a password hang bug on chrome/ webkit.
7862         
7863         var isSelectAll = false;
7864         
7865         if(this.inputEl().dom.selectionEnd > 0){
7866             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7867         }
7868         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7869             event.preventDefault();
7870             this.setValue('');
7871             return;
7872         }
7873         
7874         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
7875             
7876             event.preventDefault();
7877             // this is very hacky as keydown always get's upper case.
7878             //
7879             var cc = String.fromCharCode(event.getCharCode());
7880             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7881             
7882         }
7883     },
7884     adjustWidth : function(tag, w){
7885         tag = tag.toLowerCase();
7886         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7887             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7888                 if(tag == 'input'){
7889                     return w + 2;
7890                 }
7891                 if(tag == 'textarea'){
7892                     return w-2;
7893                 }
7894             }else if(Roo.isOpera){
7895                 if(tag == 'input'){
7896                     return w + 2;
7897                 }
7898                 if(tag == 'textarea'){
7899                     return w-2;
7900                 }
7901             }
7902         }
7903         return w;
7904     }
7905     
7906 });
7907
7908  
7909 /*
7910  * - LGPL
7911  *
7912  * Input
7913  * 
7914  */
7915
7916 /**
7917  * @class Roo.bootstrap.TextArea
7918  * @extends Roo.bootstrap.Input
7919  * Bootstrap TextArea class
7920  * @cfg {Number} cols Specifies the visible width of a text area
7921  * @cfg {Number} rows Specifies the visible number of lines in a text area
7922  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7923  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7924  * @cfg {string} html text
7925  * 
7926  * @constructor
7927  * Create a new TextArea
7928  * @param {Object} config The config object
7929  */
7930
7931 Roo.bootstrap.TextArea = function(config){
7932     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7933    
7934 };
7935
7936 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7937      
7938     cols : false,
7939     rows : 5,
7940     readOnly : false,
7941     warp : 'soft',
7942     resize : false,
7943     value: false,
7944     html: false,
7945     
7946     getAutoCreate : function(){
7947         
7948         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7949         
7950         var id = Roo.id();
7951         
7952         var cfg = {};
7953         
7954         var input =  {
7955             tag: 'textarea',
7956             id : id,
7957             warp : this.warp,
7958             rows : this.rows,
7959             value : this.value || '',
7960             html: this.html || '',
7961             cls : 'form-control',
7962             placeholder : this.placeholder || '' 
7963             
7964         };
7965         
7966         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7967             input.maxLength = this.maxLength;
7968         }
7969         
7970         if(this.resize){
7971             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7972         }
7973         
7974         if(this.cols){
7975             input.cols = this.cols;
7976         }
7977         
7978         if (this.readOnly) {
7979             input.readonly = true;
7980         }
7981         
7982         if (this.name) {
7983             input.name = this.name;
7984         }
7985         
7986         if (this.size) {
7987             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7988         }
7989         
7990         var settings=this;
7991         ['xs','sm','md','lg'].map(function(size){
7992             if (settings[size]) {
7993                 cfg.cls += ' col-' + size + '-' + settings[size];
7994             }
7995         });
7996         
7997         var inputblock = input;
7998         
7999         if(this.hasFeedback && !this.allowBlank){
8000             
8001             var feedback = {
8002                 tag: 'span',
8003                 cls: 'glyphicon form-control-feedback'
8004             };
8005
8006             inputblock = {
8007                 cls : 'has-feedback',
8008                 cn :  [
8009                     input,
8010                     feedback
8011                 ] 
8012             };  
8013         }
8014         
8015         
8016         if (this.before || this.after) {
8017             
8018             inputblock = {
8019                 cls : 'input-group',
8020                 cn :  [] 
8021             };
8022             if (this.before) {
8023                 inputblock.cn.push({
8024                     tag :'span',
8025                     cls : 'input-group-addon',
8026                     html : this.before
8027                 });
8028             }
8029             
8030             inputblock.cn.push(input);
8031             
8032             if(this.hasFeedback && !this.allowBlank){
8033                 inputblock.cls += ' has-feedback';
8034                 inputblock.cn.push(feedback);
8035             }
8036             
8037             if (this.after) {
8038                 inputblock.cn.push({
8039                     tag :'span',
8040                     cls : 'input-group-addon',
8041                     html : this.after
8042                 });
8043             }
8044             
8045         }
8046         
8047         if (align ==='left' && this.fieldLabel.length) {
8048                 Roo.log("left and has label");
8049                 cfg.cn = [
8050                     
8051                     {
8052                         tag: 'label',
8053                         'for' :  id,
8054                         cls : 'control-label col-sm-' + this.labelWidth,
8055                         html : this.fieldLabel
8056                         
8057                     },
8058                     {
8059                         cls : "col-sm-" + (12 - this.labelWidth), 
8060                         cn: [
8061                             inputblock
8062                         ]
8063                     }
8064                     
8065                 ];
8066         } else if ( this.fieldLabel.length) {
8067                 Roo.log(" label");
8068                  cfg.cn = [
8069                    
8070                     {
8071                         tag: 'label',
8072                         //cls : 'input-group-addon',
8073                         html : this.fieldLabel
8074                         
8075                     },
8076                     
8077                     inputblock
8078                     
8079                 ];
8080
8081         } else {
8082             
8083                    Roo.log(" no label && no align");
8084                 cfg.cn = [
8085                     
8086                         inputblock
8087                     
8088                 ];
8089                 
8090                 
8091         }
8092         
8093         if (this.disabled) {
8094             input.disabled=true;
8095         }
8096         
8097         return cfg;
8098         
8099     },
8100     /**
8101      * return the real textarea element.
8102      */
8103     inputEl: function ()
8104     {
8105         return this.el.select('textarea.form-control',true).first();
8106     }
8107 });
8108
8109  
8110 /*
8111  * - LGPL
8112  *
8113  * trigger field - base class for combo..
8114  * 
8115  */
8116  
8117 /**
8118  * @class Roo.bootstrap.TriggerField
8119  * @extends Roo.bootstrap.Input
8120  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8121  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8122  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8123  * for which you can provide a custom implementation.  For example:
8124  * <pre><code>
8125 var trigger = new Roo.bootstrap.TriggerField();
8126 trigger.onTriggerClick = myTriggerFn;
8127 trigger.applyTo('my-field');
8128 </code></pre>
8129  *
8130  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8131  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8132  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8133  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8134  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8135
8136  * @constructor
8137  * Create a new TriggerField.
8138  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8139  * to the base TextField)
8140  */
8141 Roo.bootstrap.TriggerField = function(config){
8142     this.mimicing = false;
8143     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8144 };
8145
8146 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8147     /**
8148      * @cfg {String} triggerClass A CSS class to apply to the trigger
8149      */
8150      /**
8151      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8152      */
8153     hideTrigger:false,
8154
8155     /** @cfg {Boolean} grow @hide */
8156     /** @cfg {Number} growMin @hide */
8157     /** @cfg {Number} growMax @hide */
8158
8159     /**
8160      * @hide 
8161      * @method
8162      */
8163     autoSize: Roo.emptyFn,
8164     // private
8165     monitorTab : true,
8166     // private
8167     deferHeight : true,
8168
8169     
8170     actionMode : 'wrap',
8171     
8172     caret : false,
8173     
8174     
8175     getAutoCreate : function(){
8176        
8177         var align = this.labelAlign || this.parentLabelAlign();
8178         
8179         var id = Roo.id();
8180         
8181         var cfg = {
8182             cls: 'form-group' //input-group
8183         };
8184         
8185         
8186         var input =  {
8187             tag: 'input',
8188             id : id,
8189             type : this.inputType,
8190             cls : 'form-control',
8191             autocomplete: 'new-password',
8192             placeholder : this.placeholder || '' 
8193             
8194         };
8195         if (this.name) {
8196             input.name = this.name;
8197         }
8198         if (this.size) {
8199             input.cls += ' input-' + this.size;
8200         }
8201         
8202         if (this.disabled) {
8203             input.disabled=true;
8204         }
8205         
8206         var inputblock = input;
8207         
8208         if(this.hasFeedback && !this.allowBlank){
8209             
8210             var feedback = {
8211                 tag: 'span',
8212                 cls: 'glyphicon form-control-feedback'
8213             };
8214
8215             inputblock = {
8216                 cls : 'has-feedback',
8217                 cn :  [
8218                     input,
8219                     feedback
8220                 ] 
8221             };  
8222         }
8223         
8224         if (this.before || this.after) {
8225             
8226             inputblock = {
8227                 cls : 'input-group',
8228                 cn :  [] 
8229             };
8230             if (this.before) {
8231                 inputblock.cn.push({
8232                     tag :'span',
8233                     cls : 'input-group-addon',
8234                     html : this.before
8235                 });
8236             }
8237             
8238             inputblock.cn.push(input);
8239             
8240             if(this.hasFeedback && !this.allowBlank){
8241                 inputblock.cls += ' has-feedback';
8242                 inputblock.cn.push(feedback);
8243             }
8244             
8245             if (this.after) {
8246                 inputblock.cn.push({
8247                     tag :'span',
8248                     cls : 'input-group-addon',
8249                     html : this.after
8250                 });
8251             }
8252             
8253         };
8254         
8255         var box = {
8256             tag: 'div',
8257             cn: [
8258                 {
8259                     tag: 'input',
8260                     type : 'hidden',
8261                     cls: 'form-hidden-field'
8262                 },
8263                 inputblock
8264             ]
8265             
8266         };
8267         
8268         if(this.multiple){
8269             Roo.log('multiple');
8270             
8271             box = {
8272                 tag: 'div',
8273                 cn: [
8274                     {
8275                         tag: 'input',
8276                         type : 'hidden',
8277                         cls: 'form-hidden-field'
8278                     },
8279                     {
8280                         tag: 'ul',
8281                         cls: 'select2-choices',
8282                         cn:[
8283                             {
8284                                 tag: 'li',
8285                                 cls: 'select2-search-field',
8286                                 cn: [
8287
8288                                     inputblock
8289                                 ]
8290                             }
8291                         ]
8292                     }
8293                 ]
8294             }
8295         };
8296         
8297         var combobox = {
8298             cls: 'select2-container input-group',
8299             cn: [
8300                 box
8301 //                {
8302 //                    tag: 'ul',
8303 //                    cls: 'typeahead typeahead-long dropdown-menu',
8304 //                    style: 'display:none'
8305 //                }
8306             ]
8307         };
8308         
8309         if(!this.multiple && this.showToggleBtn){
8310             
8311             var caret = {
8312                         tag: 'span',
8313                         cls: 'caret'
8314              };
8315             if (this.caret != false) {
8316                 caret = {
8317                      tag: 'i',
8318                      cls: 'fa fa-' + this.caret
8319                 };
8320                 
8321             }
8322             
8323             combobox.cn.push({
8324                 tag :'span',
8325                 cls : 'input-group-addon btn dropdown-toggle',
8326                 cn : [
8327                     caret,
8328                     {
8329                         tag: 'span',
8330                         cls: 'combobox-clear',
8331                         cn  : [
8332                             {
8333                                 tag : 'i',
8334                                 cls: 'icon-remove'
8335                             }
8336                         ]
8337                     }
8338                 ]
8339
8340             })
8341         }
8342         
8343         if(this.multiple){
8344             combobox.cls += ' select2-container-multi';
8345         }
8346         
8347         if (align ==='left' && this.fieldLabel.length) {
8348             
8349                 Roo.log("left and has label");
8350                 cfg.cn = [
8351                     
8352                     {
8353                         tag: 'label',
8354                         'for' :  id,
8355                         cls : 'control-label col-sm-' + this.labelWidth,
8356                         html : this.fieldLabel
8357                         
8358                     },
8359                     {
8360                         cls : "col-sm-" + (12 - this.labelWidth), 
8361                         cn: [
8362                             combobox
8363                         ]
8364                     }
8365                     
8366                 ];
8367         } else if ( this.fieldLabel.length) {
8368                 Roo.log(" label");
8369                  cfg.cn = [
8370                    
8371                     {
8372                         tag: 'label',
8373                         //cls : 'input-group-addon',
8374                         html : this.fieldLabel
8375                         
8376                     },
8377                     
8378                     combobox
8379                     
8380                 ];
8381
8382         } else {
8383             
8384                 Roo.log(" no label && no align");
8385                 cfg = combobox
8386                      
8387                 
8388         }
8389          
8390         var settings=this;
8391         ['xs','sm','md','lg'].map(function(size){
8392             if (settings[size]) {
8393                 cfg.cls += ' col-' + size + '-' + settings[size];
8394             }
8395         });
8396         
8397         return cfg;
8398         
8399     },
8400     
8401     
8402     
8403     // private
8404     onResize : function(w, h){
8405 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8406 //        if(typeof w == 'number'){
8407 //            var x = w - this.trigger.getWidth();
8408 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8409 //            this.trigger.setStyle('left', x+'px');
8410 //        }
8411     },
8412
8413     // private
8414     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8415
8416     // private
8417     getResizeEl : function(){
8418         return this.inputEl();
8419     },
8420
8421     // private
8422     getPositionEl : function(){
8423         return this.inputEl();
8424     },
8425
8426     // private
8427     alignErrorIcon : function(){
8428         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8429     },
8430
8431     // private
8432     initEvents : function(){
8433         
8434         this.createList();
8435         
8436         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8437         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8438         if(!this.multiple && this.showToggleBtn){
8439             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8440             if(this.hideTrigger){
8441                 this.trigger.setDisplayed(false);
8442             }
8443             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8444         }
8445         
8446         if(this.multiple){
8447             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8448         }
8449         
8450         //this.trigger.addClassOnOver('x-form-trigger-over');
8451         //this.trigger.addClassOnClick('x-form-trigger-click');
8452         
8453         //if(!this.width){
8454         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8455         //}
8456     },
8457     
8458     createList : function()
8459     {
8460         this.list = Roo.get(document.body).createChild({
8461             tag: 'ul',
8462             cls: 'typeahead typeahead-long dropdown-menu',
8463             style: 'display:none'
8464         });
8465         
8466         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8467         
8468     },
8469
8470     // private
8471     initTrigger : function(){
8472        
8473     },
8474
8475     // private
8476     onDestroy : function(){
8477         if(this.trigger){
8478             this.trigger.removeAllListeners();
8479           //  this.trigger.remove();
8480         }
8481         //if(this.wrap){
8482         //    this.wrap.remove();
8483         //}
8484         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8485     },
8486
8487     // private
8488     onFocus : function(){
8489         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8490         /*
8491         if(!this.mimicing){
8492             this.wrap.addClass('x-trigger-wrap-focus');
8493             this.mimicing = true;
8494             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8495             if(this.monitorTab){
8496                 this.el.on("keydown", this.checkTab, this);
8497             }
8498         }
8499         */
8500     },
8501
8502     // private
8503     checkTab : function(e){
8504         if(e.getKey() == e.TAB){
8505             this.triggerBlur();
8506         }
8507     },
8508
8509     // private
8510     onBlur : function(){
8511         // do nothing
8512     },
8513
8514     // private
8515     mimicBlur : function(e, t){
8516         /*
8517         if(!this.wrap.contains(t) && this.validateBlur()){
8518             this.triggerBlur();
8519         }
8520         */
8521     },
8522
8523     // private
8524     triggerBlur : function(){
8525         this.mimicing = false;
8526         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8527         if(this.monitorTab){
8528             this.el.un("keydown", this.checkTab, this);
8529         }
8530         //this.wrap.removeClass('x-trigger-wrap-focus');
8531         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8532     },
8533
8534     // private
8535     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8536     validateBlur : function(e, t){
8537         return true;
8538     },
8539
8540     // private
8541     onDisable : function(){
8542         this.inputEl().dom.disabled = true;
8543         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8544         //if(this.wrap){
8545         //    this.wrap.addClass('x-item-disabled');
8546         //}
8547     },
8548
8549     // private
8550     onEnable : function(){
8551         this.inputEl().dom.disabled = false;
8552         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8553         //if(this.wrap){
8554         //    this.el.removeClass('x-item-disabled');
8555         //}
8556     },
8557
8558     // private
8559     onShow : function(){
8560         var ae = this.getActionEl();
8561         
8562         if(ae){
8563             ae.dom.style.display = '';
8564             ae.dom.style.visibility = 'visible';
8565         }
8566     },
8567
8568     // private
8569     
8570     onHide : function(){
8571         var ae = this.getActionEl();
8572         ae.dom.style.display = 'none';
8573     },
8574
8575     /**
8576      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8577      * by an implementing function.
8578      * @method
8579      * @param {EventObject} e
8580      */
8581     onTriggerClick : Roo.emptyFn
8582 });
8583  /*
8584  * Based on:
8585  * Ext JS Library 1.1.1
8586  * Copyright(c) 2006-2007, Ext JS, LLC.
8587  *
8588  * Originally Released Under LGPL - original licence link has changed is not relivant.
8589  *
8590  * Fork - LGPL
8591  * <script type="text/javascript">
8592  */
8593
8594
8595 /**
8596  * @class Roo.data.SortTypes
8597  * @singleton
8598  * Defines the default sorting (casting?) comparison functions used when sorting data.
8599  */
8600 Roo.data.SortTypes = {
8601     /**
8602      * Default sort that does nothing
8603      * @param {Mixed} s The value being converted
8604      * @return {Mixed} The comparison value
8605      */
8606     none : function(s){
8607         return s;
8608     },
8609     
8610     /**
8611      * The regular expression used to strip tags
8612      * @type {RegExp}
8613      * @property
8614      */
8615     stripTagsRE : /<\/?[^>]+>/gi,
8616     
8617     /**
8618      * Strips all HTML tags to sort on text only
8619      * @param {Mixed} s The value being converted
8620      * @return {String} The comparison value
8621      */
8622     asText : function(s){
8623         return String(s).replace(this.stripTagsRE, "");
8624     },
8625     
8626     /**
8627      * Strips all HTML tags to sort on text only - Case insensitive
8628      * @param {Mixed} s The value being converted
8629      * @return {String} The comparison value
8630      */
8631     asUCText : function(s){
8632         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8633     },
8634     
8635     /**
8636      * Case insensitive string
8637      * @param {Mixed} s The value being converted
8638      * @return {String} The comparison value
8639      */
8640     asUCString : function(s) {
8641         return String(s).toUpperCase();
8642     },
8643     
8644     /**
8645      * Date sorting
8646      * @param {Mixed} s The value being converted
8647      * @return {Number} The comparison value
8648      */
8649     asDate : function(s) {
8650         if(!s){
8651             return 0;
8652         }
8653         if(s instanceof Date){
8654             return s.getTime();
8655         }
8656         return Date.parse(String(s));
8657     },
8658     
8659     /**
8660      * Float sorting
8661      * @param {Mixed} s The value being converted
8662      * @return {Float} The comparison value
8663      */
8664     asFloat : function(s) {
8665         var val = parseFloat(String(s).replace(/,/g, ""));
8666         if(isNaN(val)) val = 0;
8667         return val;
8668     },
8669     
8670     /**
8671      * Integer sorting
8672      * @param {Mixed} s The value being converted
8673      * @return {Number} The comparison value
8674      */
8675     asInt : function(s) {
8676         var val = parseInt(String(s).replace(/,/g, ""));
8677         if(isNaN(val)) val = 0;
8678         return val;
8679     }
8680 };/*
8681  * Based on:
8682  * Ext JS Library 1.1.1
8683  * Copyright(c) 2006-2007, Ext JS, LLC.
8684  *
8685  * Originally Released Under LGPL - original licence link has changed is not relivant.
8686  *
8687  * Fork - LGPL
8688  * <script type="text/javascript">
8689  */
8690
8691 /**
8692 * @class Roo.data.Record
8693  * Instances of this class encapsulate both record <em>definition</em> information, and record
8694  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8695  * to access Records cached in an {@link Roo.data.Store} object.<br>
8696  * <p>
8697  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8698  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8699  * objects.<br>
8700  * <p>
8701  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8702  * @constructor
8703  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8704  * {@link #create}. The parameters are the same.
8705  * @param {Array} data An associative Array of data values keyed by the field name.
8706  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8707  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8708  * not specified an integer id is generated.
8709  */
8710 Roo.data.Record = function(data, id){
8711     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8712     this.data = data;
8713 };
8714
8715 /**
8716  * Generate a constructor for a specific record layout.
8717  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8718  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8719  * Each field definition object may contain the following properties: <ul>
8720  * <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,
8721  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8722  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8723  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8724  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8725  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8726  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8727  * this may be omitted.</p></li>
8728  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8729  * <ul><li>auto (Default, implies no conversion)</li>
8730  * <li>string</li>
8731  * <li>int</li>
8732  * <li>float</li>
8733  * <li>boolean</li>
8734  * <li>date</li></ul></p></li>
8735  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8736  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8737  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8738  * by the Reader into an object that will be stored in the Record. It is passed the
8739  * following parameters:<ul>
8740  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8741  * </ul></p></li>
8742  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8743  * </ul>
8744  * <br>usage:<br><pre><code>
8745 var TopicRecord = Roo.data.Record.create(
8746     {name: 'title', mapping: 'topic_title'},
8747     {name: 'author', mapping: 'username'},
8748     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8749     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8750     {name: 'lastPoster', mapping: 'user2'},
8751     {name: 'excerpt', mapping: 'post_text'}
8752 );
8753
8754 var myNewRecord = new TopicRecord({
8755     title: 'Do my job please',
8756     author: 'noobie',
8757     totalPosts: 1,
8758     lastPost: new Date(),
8759     lastPoster: 'Animal',
8760     excerpt: 'No way dude!'
8761 });
8762 myStore.add(myNewRecord);
8763 </code></pre>
8764  * @method create
8765  * @static
8766  */
8767 Roo.data.Record.create = function(o){
8768     var f = function(){
8769         f.superclass.constructor.apply(this, arguments);
8770     };
8771     Roo.extend(f, Roo.data.Record);
8772     var p = f.prototype;
8773     p.fields = new Roo.util.MixedCollection(false, function(field){
8774         return field.name;
8775     });
8776     for(var i = 0, len = o.length; i < len; i++){
8777         p.fields.add(new Roo.data.Field(o[i]));
8778     }
8779     f.getField = function(name){
8780         return p.fields.get(name);  
8781     };
8782     return f;
8783 };
8784
8785 Roo.data.Record.AUTO_ID = 1000;
8786 Roo.data.Record.EDIT = 'edit';
8787 Roo.data.Record.REJECT = 'reject';
8788 Roo.data.Record.COMMIT = 'commit';
8789
8790 Roo.data.Record.prototype = {
8791     /**
8792      * Readonly flag - true if this record has been modified.
8793      * @type Boolean
8794      */
8795     dirty : false,
8796     editing : false,
8797     error: null,
8798     modified: null,
8799
8800     // private
8801     join : function(store){
8802         this.store = store;
8803     },
8804
8805     /**
8806      * Set the named field to the specified value.
8807      * @param {String} name The name of the field to set.
8808      * @param {Object} value The value to set the field to.
8809      */
8810     set : function(name, value){
8811         if(this.data[name] == value){
8812             return;
8813         }
8814         this.dirty = true;
8815         if(!this.modified){
8816             this.modified = {};
8817         }
8818         if(typeof this.modified[name] == 'undefined'){
8819             this.modified[name] = this.data[name];
8820         }
8821         this.data[name] = value;
8822         if(!this.editing && this.store){
8823             this.store.afterEdit(this);
8824         }       
8825     },
8826
8827     /**
8828      * Get the value of the named field.
8829      * @param {String} name The name of the field to get the value of.
8830      * @return {Object} The value of the field.
8831      */
8832     get : function(name){
8833         return this.data[name]; 
8834     },
8835
8836     // private
8837     beginEdit : function(){
8838         this.editing = true;
8839         this.modified = {}; 
8840     },
8841
8842     // private
8843     cancelEdit : function(){
8844         this.editing = false;
8845         delete this.modified;
8846     },
8847
8848     // private
8849     endEdit : function(){
8850         this.editing = false;
8851         if(this.dirty && this.store){
8852             this.store.afterEdit(this);
8853         }
8854     },
8855
8856     /**
8857      * Usually called by the {@link Roo.data.Store} which owns the Record.
8858      * Rejects all changes made to the Record since either creation, or the last commit operation.
8859      * Modified fields are reverted to their original values.
8860      * <p>
8861      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8862      * of reject operations.
8863      */
8864     reject : function(){
8865         var m = this.modified;
8866         for(var n in m){
8867             if(typeof m[n] != "function"){
8868                 this.data[n] = m[n];
8869             }
8870         }
8871         this.dirty = false;
8872         delete this.modified;
8873         this.editing = false;
8874         if(this.store){
8875             this.store.afterReject(this);
8876         }
8877     },
8878
8879     /**
8880      * Usually called by the {@link Roo.data.Store} which owns the Record.
8881      * Commits all changes made to the Record since either creation, or the last commit operation.
8882      * <p>
8883      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8884      * of commit operations.
8885      */
8886     commit : function(){
8887         this.dirty = false;
8888         delete this.modified;
8889         this.editing = false;
8890         if(this.store){
8891             this.store.afterCommit(this);
8892         }
8893     },
8894
8895     // private
8896     hasError : function(){
8897         return this.error != null;
8898     },
8899
8900     // private
8901     clearError : function(){
8902         this.error = null;
8903     },
8904
8905     /**
8906      * Creates a copy of this record.
8907      * @param {String} id (optional) A new record id if you don't want to use this record's id
8908      * @return {Record}
8909      */
8910     copy : function(newId) {
8911         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8912     }
8913 };/*
8914  * Based on:
8915  * Ext JS Library 1.1.1
8916  * Copyright(c) 2006-2007, Ext JS, LLC.
8917  *
8918  * Originally Released Under LGPL - original licence link has changed is not relivant.
8919  *
8920  * Fork - LGPL
8921  * <script type="text/javascript">
8922  */
8923
8924
8925
8926 /**
8927  * @class Roo.data.Store
8928  * @extends Roo.util.Observable
8929  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8930  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8931  * <p>
8932  * 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
8933  * has no knowledge of the format of the data returned by the Proxy.<br>
8934  * <p>
8935  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8936  * instances from the data object. These records are cached and made available through accessor functions.
8937  * @constructor
8938  * Creates a new Store.
8939  * @param {Object} config A config object containing the objects needed for the Store to access data,
8940  * and read the data into Records.
8941  */
8942 Roo.data.Store = function(config){
8943     this.data = new Roo.util.MixedCollection(false);
8944     this.data.getKey = function(o){
8945         return o.id;
8946     };
8947     this.baseParams = {};
8948     // private
8949     this.paramNames = {
8950         "start" : "start",
8951         "limit" : "limit",
8952         "sort" : "sort",
8953         "dir" : "dir",
8954         "multisort" : "_multisort"
8955     };
8956
8957     if(config && config.data){
8958         this.inlineData = config.data;
8959         delete config.data;
8960     }
8961
8962     Roo.apply(this, config);
8963     
8964     if(this.reader){ // reader passed
8965         this.reader = Roo.factory(this.reader, Roo.data);
8966         this.reader.xmodule = this.xmodule || false;
8967         if(!this.recordType){
8968             this.recordType = this.reader.recordType;
8969         }
8970         if(this.reader.onMetaChange){
8971             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8972         }
8973     }
8974
8975     if(this.recordType){
8976         this.fields = this.recordType.prototype.fields;
8977     }
8978     this.modified = [];
8979
8980     this.addEvents({
8981         /**
8982          * @event datachanged
8983          * Fires when the data cache has changed, and a widget which is using this Store
8984          * as a Record cache should refresh its view.
8985          * @param {Store} this
8986          */
8987         datachanged : true,
8988         /**
8989          * @event metachange
8990          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8991          * @param {Store} this
8992          * @param {Object} meta The JSON metadata
8993          */
8994         metachange : true,
8995         /**
8996          * @event add
8997          * Fires when Records have been added to the Store
8998          * @param {Store} this
8999          * @param {Roo.data.Record[]} records The array of Records added
9000          * @param {Number} index The index at which the record(s) were added
9001          */
9002         add : true,
9003         /**
9004          * @event remove
9005          * Fires when a Record has been removed from the Store
9006          * @param {Store} this
9007          * @param {Roo.data.Record} record The Record that was removed
9008          * @param {Number} index The index at which the record was removed
9009          */
9010         remove : true,
9011         /**
9012          * @event update
9013          * Fires when a Record has been updated
9014          * @param {Store} this
9015          * @param {Roo.data.Record} record The Record that was updated
9016          * @param {String} operation The update operation being performed.  Value may be one of:
9017          * <pre><code>
9018  Roo.data.Record.EDIT
9019  Roo.data.Record.REJECT
9020  Roo.data.Record.COMMIT
9021          * </code></pre>
9022          */
9023         update : true,
9024         /**
9025          * @event clear
9026          * Fires when the data cache has been cleared.
9027          * @param {Store} this
9028          */
9029         clear : true,
9030         /**
9031          * @event beforeload
9032          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9033          * the load action will be canceled.
9034          * @param {Store} this
9035          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9036          */
9037         beforeload : true,
9038         /**
9039          * @event beforeloadadd
9040          * Fires after a new set of Records has been loaded.
9041          * @param {Store} this
9042          * @param {Roo.data.Record[]} records The Records that were loaded
9043          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9044          */
9045         beforeloadadd : true,
9046         /**
9047          * @event load
9048          * Fires after a new set of Records has been loaded, before they are added to the store.
9049          * @param {Store} this
9050          * @param {Roo.data.Record[]} records The Records that were loaded
9051          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9052          * @params {Object} return from reader
9053          */
9054         load : true,
9055         /**
9056          * @event loadexception
9057          * Fires if an exception occurs in the Proxy during loading.
9058          * Called with the signature of the Proxy's "loadexception" event.
9059          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9060          * 
9061          * @param {Proxy} 
9062          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9063          * @param {Object} load options 
9064          * @param {Object} jsonData from your request (normally this contains the Exception)
9065          */
9066         loadexception : true
9067     });
9068     
9069     if(this.proxy){
9070         this.proxy = Roo.factory(this.proxy, Roo.data);
9071         this.proxy.xmodule = this.xmodule || false;
9072         this.relayEvents(this.proxy,  ["loadexception"]);
9073     }
9074     this.sortToggle = {};
9075     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9076
9077     Roo.data.Store.superclass.constructor.call(this);
9078
9079     if(this.inlineData){
9080         this.loadData(this.inlineData);
9081         delete this.inlineData;
9082     }
9083 };
9084
9085 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9086      /**
9087     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9088     * without a remote query - used by combo/forms at present.
9089     */
9090     
9091     /**
9092     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9093     */
9094     /**
9095     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9096     */
9097     /**
9098     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9099     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9100     */
9101     /**
9102     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9103     * on any HTTP request
9104     */
9105     /**
9106     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9107     */
9108     /**
9109     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9110     */
9111     multiSort: false,
9112     /**
9113     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9114     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9115     */
9116     remoteSort : false,
9117
9118     /**
9119     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9120      * loaded or when a record is removed. (defaults to false).
9121     */
9122     pruneModifiedRecords : false,
9123
9124     // private
9125     lastOptions : null,
9126
9127     /**
9128      * Add Records to the Store and fires the add event.
9129      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9130      */
9131     add : function(records){
9132         records = [].concat(records);
9133         for(var i = 0, len = records.length; i < len; i++){
9134             records[i].join(this);
9135         }
9136         var index = this.data.length;
9137         this.data.addAll(records);
9138         this.fireEvent("add", this, records, index);
9139     },
9140
9141     /**
9142      * Remove a Record from the Store and fires the remove event.
9143      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9144      */
9145     remove : function(record){
9146         var index = this.data.indexOf(record);
9147         this.data.removeAt(index);
9148         if(this.pruneModifiedRecords){
9149             this.modified.remove(record);
9150         }
9151         this.fireEvent("remove", this, record, index);
9152     },
9153
9154     /**
9155      * Remove all Records from the Store and fires the clear event.
9156      */
9157     removeAll : function(){
9158         this.data.clear();
9159         if(this.pruneModifiedRecords){
9160             this.modified = [];
9161         }
9162         this.fireEvent("clear", this);
9163     },
9164
9165     /**
9166      * Inserts Records to the Store at the given index and fires the add event.
9167      * @param {Number} index The start index at which to insert the passed Records.
9168      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9169      */
9170     insert : function(index, records){
9171         records = [].concat(records);
9172         for(var i = 0, len = records.length; i < len; i++){
9173             this.data.insert(index, records[i]);
9174             records[i].join(this);
9175         }
9176         this.fireEvent("add", this, records, index);
9177     },
9178
9179     /**
9180      * Get the index within the cache of the passed Record.
9181      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9182      * @return {Number} The index of the passed Record. Returns -1 if not found.
9183      */
9184     indexOf : function(record){
9185         return this.data.indexOf(record);
9186     },
9187
9188     /**
9189      * Get the index within the cache of the Record with the passed id.
9190      * @param {String} id The id of the Record to find.
9191      * @return {Number} The index of the Record. Returns -1 if not found.
9192      */
9193     indexOfId : function(id){
9194         return this.data.indexOfKey(id);
9195     },
9196
9197     /**
9198      * Get the Record with the specified id.
9199      * @param {String} id The id of the Record to find.
9200      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9201      */
9202     getById : function(id){
9203         return this.data.key(id);
9204     },
9205
9206     /**
9207      * Get the Record at the specified index.
9208      * @param {Number} index The index of the Record to find.
9209      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9210      */
9211     getAt : function(index){
9212         return this.data.itemAt(index);
9213     },
9214
9215     /**
9216      * Returns a range of Records between specified indices.
9217      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9218      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9219      * @return {Roo.data.Record[]} An array of Records
9220      */
9221     getRange : function(start, end){
9222         return this.data.getRange(start, end);
9223     },
9224
9225     // private
9226     storeOptions : function(o){
9227         o = Roo.apply({}, o);
9228         delete o.callback;
9229         delete o.scope;
9230         this.lastOptions = o;
9231     },
9232
9233     /**
9234      * Loads the Record cache from the configured Proxy using the configured Reader.
9235      * <p>
9236      * If using remote paging, then the first load call must specify the <em>start</em>
9237      * and <em>limit</em> properties in the options.params property to establish the initial
9238      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9239      * <p>
9240      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9241      * and this call will return before the new data has been loaded. Perform any post-processing
9242      * in a callback function, or in a "load" event handler.</strong>
9243      * <p>
9244      * @param {Object} options An object containing properties which control loading options:<ul>
9245      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9246      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9247      * passed the following arguments:<ul>
9248      * <li>r : Roo.data.Record[]</li>
9249      * <li>options: Options object from the load call</li>
9250      * <li>success: Boolean success indicator</li></ul></li>
9251      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9252      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9253      * </ul>
9254      */
9255     load : function(options){
9256         options = options || {};
9257         if(this.fireEvent("beforeload", this, options) !== false){
9258             this.storeOptions(options);
9259             var p = Roo.apply(options.params || {}, this.baseParams);
9260             // if meta was not loaded from remote source.. try requesting it.
9261             if (!this.reader.metaFromRemote) {
9262                 p._requestMeta = 1;
9263             }
9264             if(this.sortInfo && this.remoteSort){
9265                 var pn = this.paramNames;
9266                 p[pn["sort"]] = this.sortInfo.field;
9267                 p[pn["dir"]] = this.sortInfo.direction;
9268             }
9269             if (this.multiSort) {
9270                 var pn = this.paramNames;
9271                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9272             }
9273             
9274             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9275         }
9276     },
9277
9278     /**
9279      * Reloads the Record cache from the configured Proxy using the configured Reader and
9280      * the options from the last load operation performed.
9281      * @param {Object} options (optional) An object containing properties which may override the options
9282      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9283      * the most recently used options are reused).
9284      */
9285     reload : function(options){
9286         this.load(Roo.applyIf(options||{}, this.lastOptions));
9287     },
9288
9289     // private
9290     // Called as a callback by the Reader during a load operation.
9291     loadRecords : function(o, options, success){
9292         if(!o || success === false){
9293             if(success !== false){
9294                 this.fireEvent("load", this, [], options, o);
9295             }
9296             if(options.callback){
9297                 options.callback.call(options.scope || this, [], options, false);
9298             }
9299             return;
9300         }
9301         // if data returned failure - throw an exception.
9302         if (o.success === false) {
9303             // show a message if no listener is registered.
9304             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9305                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9306             }
9307             // loadmask wil be hooked into this..
9308             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9309             return;
9310         }
9311         var r = o.records, t = o.totalRecords || r.length;
9312         
9313         this.fireEvent("beforeloadadd", this, r, options, o);
9314         
9315         if(!options || options.add !== true){
9316             if(this.pruneModifiedRecords){
9317                 this.modified = [];
9318             }
9319             for(var i = 0, len = r.length; i < len; i++){
9320                 r[i].join(this);
9321             }
9322             if(this.snapshot){
9323                 this.data = this.snapshot;
9324                 delete this.snapshot;
9325             }
9326             this.data.clear();
9327             this.data.addAll(r);
9328             this.totalLength = t;
9329             this.applySort();
9330             this.fireEvent("datachanged", this);
9331         }else{
9332             this.totalLength = Math.max(t, this.data.length+r.length);
9333             this.add(r);
9334         }
9335         this.fireEvent("load", this, r, options, o);
9336         if(options.callback){
9337             options.callback.call(options.scope || this, r, options, true);
9338         }
9339     },
9340
9341
9342     /**
9343      * Loads data from a passed data block. A Reader which understands the format of the data
9344      * must have been configured in the constructor.
9345      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9346      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9347      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9348      */
9349     loadData : function(o, append){
9350         var r = this.reader.readRecords(o);
9351         this.loadRecords(r, {add: append}, true);
9352     },
9353
9354     /**
9355      * Gets the number of cached records.
9356      * <p>
9357      * <em>If using paging, this may not be the total size of the dataset. If the data object
9358      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9359      * the data set size</em>
9360      */
9361     getCount : function(){
9362         return this.data.length || 0;
9363     },
9364
9365     /**
9366      * Gets the total number of records in the dataset as returned by the server.
9367      * <p>
9368      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9369      * the dataset size</em>
9370      */
9371     getTotalCount : function(){
9372         return this.totalLength || 0;
9373     },
9374
9375     /**
9376      * Returns the sort state of the Store as an object with two properties:
9377      * <pre><code>
9378  field {String} The name of the field by which the Records are sorted
9379  direction {String} The sort order, "ASC" or "DESC"
9380      * </code></pre>
9381      */
9382     getSortState : function(){
9383         return this.sortInfo;
9384     },
9385
9386     // private
9387     applySort : function(){
9388         if(this.sortInfo && !this.remoteSort){
9389             var s = this.sortInfo, f = s.field;
9390             var st = this.fields.get(f).sortType;
9391             var fn = function(r1, r2){
9392                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9393                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9394             };
9395             this.data.sort(s.direction, fn);
9396             if(this.snapshot && this.snapshot != this.data){
9397                 this.snapshot.sort(s.direction, fn);
9398             }
9399         }
9400     },
9401
9402     /**
9403      * Sets the default sort column and order to be used by the next load operation.
9404      * @param {String} fieldName The name of the field to sort by.
9405      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9406      */
9407     setDefaultSort : function(field, dir){
9408         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9409     },
9410
9411     /**
9412      * Sort the Records.
9413      * If remote sorting is used, the sort is performed on the server, and the cache is
9414      * reloaded. If local sorting is used, the cache is sorted internally.
9415      * @param {String} fieldName The name of the field to sort by.
9416      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9417      */
9418     sort : function(fieldName, dir){
9419         var f = this.fields.get(fieldName);
9420         if(!dir){
9421             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9422             
9423             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9424                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9425             }else{
9426                 dir = f.sortDir;
9427             }
9428         }
9429         this.sortToggle[f.name] = dir;
9430         this.sortInfo = {field: f.name, direction: dir};
9431         if(!this.remoteSort){
9432             this.applySort();
9433             this.fireEvent("datachanged", this);
9434         }else{
9435             this.load(this.lastOptions);
9436         }
9437     },
9438
9439     /**
9440      * Calls the specified function for each of the Records in the cache.
9441      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9442      * Returning <em>false</em> aborts and exits the iteration.
9443      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9444      */
9445     each : function(fn, scope){
9446         this.data.each(fn, scope);
9447     },
9448
9449     /**
9450      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9451      * (e.g., during paging).
9452      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9453      */
9454     getModifiedRecords : function(){
9455         return this.modified;
9456     },
9457
9458     // private
9459     createFilterFn : function(property, value, anyMatch){
9460         if(!value.exec){ // not a regex
9461             value = String(value);
9462             if(value.length == 0){
9463                 return false;
9464             }
9465             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9466         }
9467         return function(r){
9468             return value.test(r.data[property]);
9469         };
9470     },
9471
9472     /**
9473      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9474      * @param {String} property A field on your records
9475      * @param {Number} start The record index to start at (defaults to 0)
9476      * @param {Number} end The last record index to include (defaults to length - 1)
9477      * @return {Number} The sum
9478      */
9479     sum : function(property, start, end){
9480         var rs = this.data.items, v = 0;
9481         start = start || 0;
9482         end = (end || end === 0) ? end : rs.length-1;
9483
9484         for(var i = start; i <= end; i++){
9485             v += (rs[i].data[property] || 0);
9486         }
9487         return v;
9488     },
9489
9490     /**
9491      * Filter the records by a specified property.
9492      * @param {String} field A field on your records
9493      * @param {String/RegExp} value Either a string that the field
9494      * should start with or a RegExp to test against the field
9495      * @param {Boolean} anyMatch True to match any part not just the beginning
9496      */
9497     filter : function(property, value, anyMatch){
9498         var fn = this.createFilterFn(property, value, anyMatch);
9499         return fn ? this.filterBy(fn) : this.clearFilter();
9500     },
9501
9502     /**
9503      * Filter by a function. The specified function will be called with each
9504      * record in this data source. If the function returns true the record is included,
9505      * otherwise it is filtered.
9506      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9507      * @param {Object} scope (optional) The scope of the function (defaults to this)
9508      */
9509     filterBy : function(fn, scope){
9510         this.snapshot = this.snapshot || this.data;
9511         this.data = this.queryBy(fn, scope||this);
9512         this.fireEvent("datachanged", this);
9513     },
9514
9515     /**
9516      * Query the records by a specified property.
9517      * @param {String} field A field on your records
9518      * @param {String/RegExp} value Either a string that the field
9519      * should start with or a RegExp to test against the field
9520      * @param {Boolean} anyMatch True to match any part not just the beginning
9521      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9522      */
9523     query : function(property, value, anyMatch){
9524         var fn = this.createFilterFn(property, value, anyMatch);
9525         return fn ? this.queryBy(fn) : this.data.clone();
9526     },
9527
9528     /**
9529      * Query by a function. The specified function will be called with each
9530      * record in this data source. If the function returns true the record is included
9531      * in the results.
9532      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9533      * @param {Object} scope (optional) The scope of the function (defaults to this)
9534       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9535      **/
9536     queryBy : function(fn, scope){
9537         var data = this.snapshot || this.data;
9538         return data.filterBy(fn, scope||this);
9539     },
9540
9541     /**
9542      * Collects unique values for a particular dataIndex from this store.
9543      * @param {String} dataIndex The property to collect
9544      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9545      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9546      * @return {Array} An array of the unique values
9547      **/
9548     collect : function(dataIndex, allowNull, bypassFilter){
9549         var d = (bypassFilter === true && this.snapshot) ?
9550                 this.snapshot.items : this.data.items;
9551         var v, sv, r = [], l = {};
9552         for(var i = 0, len = d.length; i < len; i++){
9553             v = d[i].data[dataIndex];
9554             sv = String(v);
9555             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9556                 l[sv] = true;
9557                 r[r.length] = v;
9558             }
9559         }
9560         return r;
9561     },
9562
9563     /**
9564      * Revert to a view of the Record cache with no filtering applied.
9565      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9566      */
9567     clearFilter : function(suppressEvent){
9568         if(this.snapshot && this.snapshot != this.data){
9569             this.data = this.snapshot;
9570             delete this.snapshot;
9571             if(suppressEvent !== true){
9572                 this.fireEvent("datachanged", this);
9573             }
9574         }
9575     },
9576
9577     // private
9578     afterEdit : function(record){
9579         if(this.modified.indexOf(record) == -1){
9580             this.modified.push(record);
9581         }
9582         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9583     },
9584     
9585     // private
9586     afterReject : function(record){
9587         this.modified.remove(record);
9588         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9589     },
9590
9591     // private
9592     afterCommit : function(record){
9593         this.modified.remove(record);
9594         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9595     },
9596
9597     /**
9598      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9599      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9600      */
9601     commitChanges : function(){
9602         var m = this.modified.slice(0);
9603         this.modified = [];
9604         for(var i = 0, len = m.length; i < len; i++){
9605             m[i].commit();
9606         }
9607     },
9608
9609     /**
9610      * Cancel outstanding changes on all changed records.
9611      */
9612     rejectChanges : function(){
9613         var m = this.modified.slice(0);
9614         this.modified = [];
9615         for(var i = 0, len = m.length; i < len; i++){
9616             m[i].reject();
9617         }
9618     },
9619
9620     onMetaChange : function(meta, rtype, o){
9621         this.recordType = rtype;
9622         this.fields = rtype.prototype.fields;
9623         delete this.snapshot;
9624         this.sortInfo = meta.sortInfo || this.sortInfo;
9625         this.modified = [];
9626         this.fireEvent('metachange', this, this.reader.meta);
9627     },
9628     
9629     moveIndex : function(data, type)
9630     {
9631         var index = this.indexOf(data);
9632         
9633         var newIndex = index + type;
9634         
9635         this.remove(data);
9636         
9637         this.insert(newIndex, data);
9638         
9639     }
9640 });/*
9641  * Based on:
9642  * Ext JS Library 1.1.1
9643  * Copyright(c) 2006-2007, Ext JS, LLC.
9644  *
9645  * Originally Released Under LGPL - original licence link has changed is not relivant.
9646  *
9647  * Fork - LGPL
9648  * <script type="text/javascript">
9649  */
9650
9651 /**
9652  * @class Roo.data.SimpleStore
9653  * @extends Roo.data.Store
9654  * Small helper class to make creating Stores from Array data easier.
9655  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9656  * @cfg {Array} fields An array of field definition objects, or field name strings.
9657  * @cfg {Array} data The multi-dimensional array of data
9658  * @constructor
9659  * @param {Object} config
9660  */
9661 Roo.data.SimpleStore = function(config){
9662     Roo.data.SimpleStore.superclass.constructor.call(this, {
9663         isLocal : true,
9664         reader: new Roo.data.ArrayReader({
9665                 id: config.id
9666             },
9667             Roo.data.Record.create(config.fields)
9668         ),
9669         proxy : new Roo.data.MemoryProxy(config.data)
9670     });
9671     this.load();
9672 };
9673 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9674  * Based on:
9675  * Ext JS Library 1.1.1
9676  * Copyright(c) 2006-2007, Ext JS, LLC.
9677  *
9678  * Originally Released Under LGPL - original licence link has changed is not relivant.
9679  *
9680  * Fork - LGPL
9681  * <script type="text/javascript">
9682  */
9683
9684 /**
9685 /**
9686  * @extends Roo.data.Store
9687  * @class Roo.data.JsonStore
9688  * Small helper class to make creating Stores for JSON data easier. <br/>
9689 <pre><code>
9690 var store = new Roo.data.JsonStore({
9691     url: 'get-images.php',
9692     root: 'images',
9693     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9694 });
9695 </code></pre>
9696  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9697  * JsonReader and HttpProxy (unless inline data is provided).</b>
9698  * @cfg {Array} fields An array of field definition objects, or field name strings.
9699  * @constructor
9700  * @param {Object} config
9701  */
9702 Roo.data.JsonStore = function(c){
9703     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9704         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9705         reader: new Roo.data.JsonReader(c, c.fields)
9706     }));
9707 };
9708 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9709  * Based on:
9710  * Ext JS Library 1.1.1
9711  * Copyright(c) 2006-2007, Ext JS, LLC.
9712  *
9713  * Originally Released Under LGPL - original licence link has changed is not relivant.
9714  *
9715  * Fork - LGPL
9716  * <script type="text/javascript">
9717  */
9718
9719  
9720 Roo.data.Field = function(config){
9721     if(typeof config == "string"){
9722         config = {name: config};
9723     }
9724     Roo.apply(this, config);
9725     
9726     if(!this.type){
9727         this.type = "auto";
9728     }
9729     
9730     var st = Roo.data.SortTypes;
9731     // named sortTypes are supported, here we look them up
9732     if(typeof this.sortType == "string"){
9733         this.sortType = st[this.sortType];
9734     }
9735     
9736     // set default sortType for strings and dates
9737     if(!this.sortType){
9738         switch(this.type){
9739             case "string":
9740                 this.sortType = st.asUCString;
9741                 break;
9742             case "date":
9743                 this.sortType = st.asDate;
9744                 break;
9745             default:
9746                 this.sortType = st.none;
9747         }
9748     }
9749
9750     // define once
9751     var stripRe = /[\$,%]/g;
9752
9753     // prebuilt conversion function for this field, instead of
9754     // switching every time we're reading a value
9755     if(!this.convert){
9756         var cv, dateFormat = this.dateFormat;
9757         switch(this.type){
9758             case "":
9759             case "auto":
9760             case undefined:
9761                 cv = function(v){ return v; };
9762                 break;
9763             case "string":
9764                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9765                 break;
9766             case "int":
9767                 cv = function(v){
9768                     return v !== undefined && v !== null && v !== '' ?
9769                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9770                     };
9771                 break;
9772             case "float":
9773                 cv = function(v){
9774                     return v !== undefined && v !== null && v !== '' ?
9775                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9776                     };
9777                 break;
9778             case "bool":
9779             case "boolean":
9780                 cv = function(v){ return v === true || v === "true" || v == 1; };
9781                 break;
9782             case "date":
9783                 cv = function(v){
9784                     if(!v){
9785                         return '';
9786                     }
9787                     if(v instanceof Date){
9788                         return v;
9789                     }
9790                     if(dateFormat){
9791                         if(dateFormat == "timestamp"){
9792                             return new Date(v*1000);
9793                         }
9794                         return Date.parseDate(v, dateFormat);
9795                     }
9796                     var parsed = Date.parse(v);
9797                     return parsed ? new Date(parsed) : null;
9798                 };
9799              break;
9800             
9801         }
9802         this.convert = cv;
9803     }
9804 };
9805
9806 Roo.data.Field.prototype = {
9807     dateFormat: null,
9808     defaultValue: "",
9809     mapping: null,
9810     sortType : null,
9811     sortDir : "ASC"
9812 };/*
9813  * Based on:
9814  * Ext JS Library 1.1.1
9815  * Copyright(c) 2006-2007, Ext JS, LLC.
9816  *
9817  * Originally Released Under LGPL - original licence link has changed is not relivant.
9818  *
9819  * Fork - LGPL
9820  * <script type="text/javascript">
9821  */
9822  
9823 // Base class for reading structured data from a data source.  This class is intended to be
9824 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9825
9826 /**
9827  * @class Roo.data.DataReader
9828  * Base class for reading structured data from a data source.  This class is intended to be
9829  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9830  */
9831
9832 Roo.data.DataReader = function(meta, recordType){
9833     
9834     this.meta = meta;
9835     
9836     this.recordType = recordType instanceof Array ? 
9837         Roo.data.Record.create(recordType) : recordType;
9838 };
9839
9840 Roo.data.DataReader.prototype = {
9841      /**
9842      * Create an empty record
9843      * @param {Object} data (optional) - overlay some values
9844      * @return {Roo.data.Record} record created.
9845      */
9846     newRow :  function(d) {
9847         var da =  {};
9848         this.recordType.prototype.fields.each(function(c) {
9849             switch( c.type) {
9850                 case 'int' : da[c.name] = 0; break;
9851                 case 'date' : da[c.name] = new Date(); break;
9852                 case 'float' : da[c.name] = 0.0; break;
9853                 case 'boolean' : da[c.name] = false; break;
9854                 default : da[c.name] = ""; break;
9855             }
9856             
9857         });
9858         return new this.recordType(Roo.apply(da, d));
9859     }
9860     
9861 };/*
9862  * Based on:
9863  * Ext JS Library 1.1.1
9864  * Copyright(c) 2006-2007, Ext JS, LLC.
9865  *
9866  * Originally Released Under LGPL - original licence link has changed is not relivant.
9867  *
9868  * Fork - LGPL
9869  * <script type="text/javascript">
9870  */
9871
9872 /**
9873  * @class Roo.data.DataProxy
9874  * @extends Roo.data.Observable
9875  * This class is an abstract base class for implementations which provide retrieval of
9876  * unformatted data objects.<br>
9877  * <p>
9878  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9879  * (of the appropriate type which knows how to parse the data object) to provide a block of
9880  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9881  * <p>
9882  * Custom implementations must implement the load method as described in
9883  * {@link Roo.data.HttpProxy#load}.
9884  */
9885 Roo.data.DataProxy = function(){
9886     this.addEvents({
9887         /**
9888          * @event beforeload
9889          * Fires before a network request is made to retrieve a data object.
9890          * @param {Object} This DataProxy object.
9891          * @param {Object} params The params parameter to the load function.
9892          */
9893         beforeload : true,
9894         /**
9895          * @event load
9896          * Fires before the load method's callback is called.
9897          * @param {Object} This DataProxy object.
9898          * @param {Object} o The data object.
9899          * @param {Object} arg The callback argument object passed to the load function.
9900          */
9901         load : true,
9902         /**
9903          * @event loadexception
9904          * Fires if an Exception occurs during data retrieval.
9905          * @param {Object} This DataProxy object.
9906          * @param {Object} o The data object.
9907          * @param {Object} arg The callback argument object passed to the load function.
9908          * @param {Object} e The Exception.
9909          */
9910         loadexception : true
9911     });
9912     Roo.data.DataProxy.superclass.constructor.call(this);
9913 };
9914
9915 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9916
9917     /**
9918      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9919      */
9920 /*
9921  * Based on:
9922  * Ext JS Library 1.1.1
9923  * Copyright(c) 2006-2007, Ext JS, LLC.
9924  *
9925  * Originally Released Under LGPL - original licence link has changed is not relivant.
9926  *
9927  * Fork - LGPL
9928  * <script type="text/javascript">
9929  */
9930 /**
9931  * @class Roo.data.MemoryProxy
9932  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9933  * to the Reader when its load method is called.
9934  * @constructor
9935  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9936  */
9937 Roo.data.MemoryProxy = function(data){
9938     if (data.data) {
9939         data = data.data;
9940     }
9941     Roo.data.MemoryProxy.superclass.constructor.call(this);
9942     this.data = data;
9943 };
9944
9945 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9946     /**
9947      * Load data from the requested source (in this case an in-memory
9948      * data object passed to the constructor), read the data object into
9949      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9950      * process that block using the passed callback.
9951      * @param {Object} params This parameter is not used by the MemoryProxy class.
9952      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9953      * object into a block of Roo.data.Records.
9954      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9955      * The function must be passed <ul>
9956      * <li>The Record block object</li>
9957      * <li>The "arg" argument from the load function</li>
9958      * <li>A boolean success indicator</li>
9959      * </ul>
9960      * @param {Object} scope The scope in which to call the callback
9961      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9962      */
9963     load : function(params, reader, callback, scope, arg){
9964         params = params || {};
9965         var result;
9966         try {
9967             result = reader.readRecords(this.data);
9968         }catch(e){
9969             this.fireEvent("loadexception", this, arg, null, e);
9970             callback.call(scope, null, arg, false);
9971             return;
9972         }
9973         callback.call(scope, result, arg, true);
9974     },
9975     
9976     // private
9977     update : function(params, records){
9978         
9979     }
9980 });/*
9981  * Based on:
9982  * Ext JS Library 1.1.1
9983  * Copyright(c) 2006-2007, Ext JS, LLC.
9984  *
9985  * Originally Released Under LGPL - original licence link has changed is not relivant.
9986  *
9987  * Fork - LGPL
9988  * <script type="text/javascript">
9989  */
9990 /**
9991  * @class Roo.data.HttpProxy
9992  * @extends Roo.data.DataProxy
9993  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9994  * configured to reference a certain URL.<br><br>
9995  * <p>
9996  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9997  * from which the running page was served.<br><br>
9998  * <p>
9999  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10000  * <p>
10001  * Be aware that to enable the browser to parse an XML document, the server must set
10002  * the Content-Type header in the HTTP response to "text/xml".
10003  * @constructor
10004  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10005  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10006  * will be used to make the request.
10007  */
10008 Roo.data.HttpProxy = function(conn){
10009     Roo.data.HttpProxy.superclass.constructor.call(this);
10010     // is conn a conn config or a real conn?
10011     this.conn = conn;
10012     this.useAjax = !conn || !conn.events;
10013   
10014 };
10015
10016 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10017     // thse are take from connection...
10018     
10019     /**
10020      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10021      */
10022     /**
10023      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10024      * extra parameters to each request made by this object. (defaults to undefined)
10025      */
10026     /**
10027      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10028      *  to each request made by this object. (defaults to undefined)
10029      */
10030     /**
10031      * @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)
10032      */
10033     /**
10034      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10035      */
10036      /**
10037      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10038      * @type Boolean
10039      */
10040   
10041
10042     /**
10043      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10044      * @type Boolean
10045      */
10046     /**
10047      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10048      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10049      * a finer-grained basis than the DataProxy events.
10050      */
10051     getConnection : function(){
10052         return this.useAjax ? Roo.Ajax : this.conn;
10053     },
10054
10055     /**
10056      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10057      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10058      * process that block using the passed callback.
10059      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10060      * for the request to the remote server.
10061      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10062      * object into a block of Roo.data.Records.
10063      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10064      * The function must be passed <ul>
10065      * <li>The Record block object</li>
10066      * <li>The "arg" argument from the load function</li>
10067      * <li>A boolean success indicator</li>
10068      * </ul>
10069      * @param {Object} scope The scope in which to call the callback
10070      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10071      */
10072     load : function(params, reader, callback, scope, arg){
10073         if(this.fireEvent("beforeload", this, params) !== false){
10074             var  o = {
10075                 params : params || {},
10076                 request: {
10077                     callback : callback,
10078                     scope : scope,
10079                     arg : arg
10080                 },
10081                 reader: reader,
10082                 callback : this.loadResponse,
10083                 scope: this
10084             };
10085             if(this.useAjax){
10086                 Roo.applyIf(o, this.conn);
10087                 if(this.activeRequest){
10088                     Roo.Ajax.abort(this.activeRequest);
10089                 }
10090                 this.activeRequest = Roo.Ajax.request(o);
10091             }else{
10092                 this.conn.request(o);
10093             }
10094         }else{
10095             callback.call(scope||this, null, arg, false);
10096         }
10097     },
10098
10099     // private
10100     loadResponse : function(o, success, response){
10101         delete this.activeRequest;
10102         if(!success){
10103             this.fireEvent("loadexception", this, o, response);
10104             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10105             return;
10106         }
10107         var result;
10108         try {
10109             result = o.reader.read(response);
10110         }catch(e){
10111             this.fireEvent("loadexception", this, o, response, e);
10112             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10113             return;
10114         }
10115         
10116         this.fireEvent("load", this, o, o.request.arg);
10117         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10118     },
10119
10120     // private
10121     update : function(dataSet){
10122
10123     },
10124
10125     // private
10126     updateResponse : function(dataSet){
10127
10128     }
10129 });/*
10130  * Based on:
10131  * Ext JS Library 1.1.1
10132  * Copyright(c) 2006-2007, Ext JS, LLC.
10133  *
10134  * Originally Released Under LGPL - original licence link has changed is not relivant.
10135  *
10136  * Fork - LGPL
10137  * <script type="text/javascript">
10138  */
10139
10140 /**
10141  * @class Roo.data.ScriptTagProxy
10142  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10143  * other than the originating domain of the running page.<br><br>
10144  * <p>
10145  * <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
10146  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10147  * <p>
10148  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10149  * source code that is used as the source inside a &lt;script> tag.<br><br>
10150  * <p>
10151  * In order for the browser to process the returned data, the server must wrap the data object
10152  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10153  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10154  * depending on whether the callback name was passed:
10155  * <p>
10156  * <pre><code>
10157 boolean scriptTag = false;
10158 String cb = request.getParameter("callback");
10159 if (cb != null) {
10160     scriptTag = true;
10161     response.setContentType("text/javascript");
10162 } else {
10163     response.setContentType("application/x-json");
10164 }
10165 Writer out = response.getWriter();
10166 if (scriptTag) {
10167     out.write(cb + "(");
10168 }
10169 out.print(dataBlock.toJsonString());
10170 if (scriptTag) {
10171     out.write(");");
10172 }
10173 </pre></code>
10174  *
10175  * @constructor
10176  * @param {Object} config A configuration object.
10177  */
10178 Roo.data.ScriptTagProxy = function(config){
10179     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10180     Roo.apply(this, config);
10181     this.head = document.getElementsByTagName("head")[0];
10182 };
10183
10184 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10185
10186 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10187     /**
10188      * @cfg {String} url The URL from which to request the data object.
10189      */
10190     /**
10191      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10192      */
10193     timeout : 30000,
10194     /**
10195      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10196      * the server the name of the callback function set up by the load call to process the returned data object.
10197      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10198      * javascript output which calls this named function passing the data object as its only parameter.
10199      */
10200     callbackParam : "callback",
10201     /**
10202      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10203      * name to the request.
10204      */
10205     nocache : true,
10206
10207     /**
10208      * Load data from the configured URL, read the data object into
10209      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10210      * process that block using the passed callback.
10211      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10212      * for the request to the remote server.
10213      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10214      * object into a block of Roo.data.Records.
10215      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10216      * The function must be passed <ul>
10217      * <li>The Record block object</li>
10218      * <li>The "arg" argument from the load function</li>
10219      * <li>A boolean success indicator</li>
10220      * </ul>
10221      * @param {Object} scope The scope in which to call the callback
10222      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10223      */
10224     load : function(params, reader, callback, scope, arg){
10225         if(this.fireEvent("beforeload", this, params) !== false){
10226
10227             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10228
10229             var url = this.url;
10230             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10231             if(this.nocache){
10232                 url += "&_dc=" + (new Date().getTime());
10233             }
10234             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10235             var trans = {
10236                 id : transId,
10237                 cb : "stcCallback"+transId,
10238                 scriptId : "stcScript"+transId,
10239                 params : params,
10240                 arg : arg,
10241                 url : url,
10242                 callback : callback,
10243                 scope : scope,
10244                 reader : reader
10245             };
10246             var conn = this;
10247
10248             window[trans.cb] = function(o){
10249                 conn.handleResponse(o, trans);
10250             };
10251
10252             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10253
10254             if(this.autoAbort !== false){
10255                 this.abort();
10256             }
10257
10258             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10259
10260             var script = document.createElement("script");
10261             script.setAttribute("src", url);
10262             script.setAttribute("type", "text/javascript");
10263             script.setAttribute("id", trans.scriptId);
10264             this.head.appendChild(script);
10265
10266             this.trans = trans;
10267         }else{
10268             callback.call(scope||this, null, arg, false);
10269         }
10270     },
10271
10272     // private
10273     isLoading : function(){
10274         return this.trans ? true : false;
10275     },
10276
10277     /**
10278      * Abort the current server request.
10279      */
10280     abort : function(){
10281         if(this.isLoading()){
10282             this.destroyTrans(this.trans);
10283         }
10284     },
10285
10286     // private
10287     destroyTrans : function(trans, isLoaded){
10288         this.head.removeChild(document.getElementById(trans.scriptId));
10289         clearTimeout(trans.timeoutId);
10290         if(isLoaded){
10291             window[trans.cb] = undefined;
10292             try{
10293                 delete window[trans.cb];
10294             }catch(e){}
10295         }else{
10296             // if hasn't been loaded, wait for load to remove it to prevent script error
10297             window[trans.cb] = function(){
10298                 window[trans.cb] = undefined;
10299                 try{
10300                     delete window[trans.cb];
10301                 }catch(e){}
10302             };
10303         }
10304     },
10305
10306     // private
10307     handleResponse : function(o, trans){
10308         this.trans = false;
10309         this.destroyTrans(trans, true);
10310         var result;
10311         try {
10312             result = trans.reader.readRecords(o);
10313         }catch(e){
10314             this.fireEvent("loadexception", this, o, trans.arg, e);
10315             trans.callback.call(trans.scope||window, null, trans.arg, false);
10316             return;
10317         }
10318         this.fireEvent("load", this, o, trans.arg);
10319         trans.callback.call(trans.scope||window, result, trans.arg, true);
10320     },
10321
10322     // private
10323     handleFailure : function(trans){
10324         this.trans = false;
10325         this.destroyTrans(trans, false);
10326         this.fireEvent("loadexception", this, null, trans.arg);
10327         trans.callback.call(trans.scope||window, null, trans.arg, false);
10328     }
10329 });/*
10330  * Based on:
10331  * Ext JS Library 1.1.1
10332  * Copyright(c) 2006-2007, Ext JS, LLC.
10333  *
10334  * Originally Released Under LGPL - original licence link has changed is not relivant.
10335  *
10336  * Fork - LGPL
10337  * <script type="text/javascript">
10338  */
10339
10340 /**
10341  * @class Roo.data.JsonReader
10342  * @extends Roo.data.DataReader
10343  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10344  * based on mappings in a provided Roo.data.Record constructor.
10345  * 
10346  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10347  * in the reply previously. 
10348  * 
10349  * <p>
10350  * Example code:
10351  * <pre><code>
10352 var RecordDef = Roo.data.Record.create([
10353     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10354     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10355 ]);
10356 var myReader = new Roo.data.JsonReader({
10357     totalProperty: "results",    // The property which contains the total dataset size (optional)
10358     root: "rows",                // The property which contains an Array of row objects
10359     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10360 }, RecordDef);
10361 </code></pre>
10362  * <p>
10363  * This would consume a JSON file like this:
10364  * <pre><code>
10365 { 'results': 2, 'rows': [
10366     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10367     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10368 }
10369 </code></pre>
10370  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10371  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10372  * paged from the remote server.
10373  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10374  * @cfg {String} root name of the property which contains the Array of row objects.
10375  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10376  * @constructor
10377  * Create a new JsonReader
10378  * @param {Object} meta Metadata configuration options
10379  * @param {Object} recordType Either an Array of field definition objects,
10380  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10381  */
10382 Roo.data.JsonReader = function(meta, recordType){
10383     
10384     meta = meta || {};
10385     // set some defaults:
10386     Roo.applyIf(meta, {
10387         totalProperty: 'total',
10388         successProperty : 'success',
10389         root : 'data',
10390         id : 'id'
10391     });
10392     
10393     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10394 };
10395 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10396     
10397     /**
10398      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10399      * Used by Store query builder to append _requestMeta to params.
10400      * 
10401      */
10402     metaFromRemote : false,
10403     /**
10404      * This method is only used by a DataProxy which has retrieved data from a remote server.
10405      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10406      * @return {Object} data A data block which is used by an Roo.data.Store object as
10407      * a cache of Roo.data.Records.
10408      */
10409     read : function(response){
10410         var json = response.responseText;
10411        
10412         var o = /* eval:var:o */ eval("("+json+")");
10413         if(!o) {
10414             throw {message: "JsonReader.read: Json object not found"};
10415         }
10416         
10417         if(o.metaData){
10418             
10419             delete this.ef;
10420             this.metaFromRemote = true;
10421             this.meta = o.metaData;
10422             this.recordType = Roo.data.Record.create(o.metaData.fields);
10423             this.onMetaChange(this.meta, this.recordType, o);
10424         }
10425         return this.readRecords(o);
10426     },
10427
10428     // private function a store will implement
10429     onMetaChange : function(meta, recordType, o){
10430
10431     },
10432
10433     /**
10434          * @ignore
10435          */
10436     simpleAccess: function(obj, subsc) {
10437         return obj[subsc];
10438     },
10439
10440         /**
10441          * @ignore
10442          */
10443     getJsonAccessor: function(){
10444         var re = /[\[\.]/;
10445         return function(expr) {
10446             try {
10447                 return(re.test(expr))
10448                     ? new Function("obj", "return obj." + expr)
10449                     : function(obj){
10450                         return obj[expr];
10451                     };
10452             } catch(e){}
10453             return Roo.emptyFn;
10454         };
10455     }(),
10456
10457     /**
10458      * Create a data block containing Roo.data.Records from an XML document.
10459      * @param {Object} o An object which contains an Array of row objects in the property specified
10460      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10461      * which contains the total size of the dataset.
10462      * @return {Object} data A data block which is used by an Roo.data.Store object as
10463      * a cache of Roo.data.Records.
10464      */
10465     readRecords : function(o){
10466         /**
10467          * After any data loads, the raw JSON data is available for further custom processing.
10468          * @type Object
10469          */
10470         this.o = o;
10471         var s = this.meta, Record = this.recordType,
10472             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10473
10474 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10475         if (!this.ef) {
10476             if(s.totalProperty) {
10477                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10478                 }
10479                 if(s.successProperty) {
10480                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10481                 }
10482                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10483                 if (s.id) {
10484                         var g = this.getJsonAccessor(s.id);
10485                         this.getId = function(rec) {
10486                                 var r = g(rec);  
10487                                 return (r === undefined || r === "") ? null : r;
10488                         };
10489                 } else {
10490                         this.getId = function(){return null;};
10491                 }
10492             this.ef = [];
10493             for(var jj = 0; jj < fl; jj++){
10494                 f = fi[jj];
10495                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10496                 this.ef[jj] = this.getJsonAccessor(map);
10497             }
10498         }
10499
10500         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10501         if(s.totalProperty){
10502             var vt = parseInt(this.getTotal(o), 10);
10503             if(!isNaN(vt)){
10504                 totalRecords = vt;
10505             }
10506         }
10507         if(s.successProperty){
10508             var vs = this.getSuccess(o);
10509             if(vs === false || vs === 'false'){
10510                 success = false;
10511             }
10512         }
10513         var records = [];
10514         for(var i = 0; i < c; i++){
10515                 var n = root[i];
10516             var values = {};
10517             var id = this.getId(n);
10518             for(var j = 0; j < fl; j++){
10519                 f = fi[j];
10520             var v = this.ef[j](n);
10521             if (!f.convert) {
10522                 Roo.log('missing convert for ' + f.name);
10523                 Roo.log(f);
10524                 continue;
10525             }
10526             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10527             }
10528             var record = new Record(values, id);
10529             record.json = n;
10530             records[i] = record;
10531         }
10532         return {
10533             raw : o,
10534             success : success,
10535             records : records,
10536             totalRecords : totalRecords
10537         };
10538     }
10539 });/*
10540  * Based on:
10541  * Ext JS Library 1.1.1
10542  * Copyright(c) 2006-2007, Ext JS, LLC.
10543  *
10544  * Originally Released Under LGPL - original licence link has changed is not relivant.
10545  *
10546  * Fork - LGPL
10547  * <script type="text/javascript">
10548  */
10549
10550 /**
10551  * @class Roo.data.ArrayReader
10552  * @extends Roo.data.DataReader
10553  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10554  * Each element of that Array represents a row of data fields. The
10555  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10556  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10557  * <p>
10558  * Example code:.
10559  * <pre><code>
10560 var RecordDef = Roo.data.Record.create([
10561     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10562     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10563 ]);
10564 var myReader = new Roo.data.ArrayReader({
10565     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10566 }, RecordDef);
10567 </code></pre>
10568  * <p>
10569  * This would consume an Array like this:
10570  * <pre><code>
10571 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10572   </code></pre>
10573  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10574  * @constructor
10575  * Create a new JsonReader
10576  * @param {Object} meta Metadata configuration options.
10577  * @param {Object} recordType Either an Array of field definition objects
10578  * as specified to {@link Roo.data.Record#create},
10579  * or an {@link Roo.data.Record} object
10580  * created using {@link Roo.data.Record#create}.
10581  */
10582 Roo.data.ArrayReader = function(meta, recordType){
10583     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10584 };
10585
10586 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10587     /**
10588      * Create a data block containing Roo.data.Records from an XML document.
10589      * @param {Object} o An Array of row objects which represents the dataset.
10590      * @return {Object} data A data block which is used by an Roo.data.Store object as
10591      * a cache of Roo.data.Records.
10592      */
10593     readRecords : function(o){
10594         var sid = this.meta ? this.meta.id : null;
10595         var recordType = this.recordType, fields = recordType.prototype.fields;
10596         var records = [];
10597         var root = o;
10598             for(var i = 0; i < root.length; i++){
10599                     var n = root[i];
10600                 var values = {};
10601                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10602                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10603                 var f = fields.items[j];
10604                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10605                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10606                 v = f.convert(v);
10607                 values[f.name] = v;
10608             }
10609                 var record = new recordType(values, id);
10610                 record.json = n;
10611                 records[records.length] = record;
10612             }
10613             return {
10614                 records : records,
10615                 totalRecords : records.length
10616             };
10617     }
10618 });/*
10619  * - LGPL
10620  * * 
10621  */
10622
10623 /**
10624  * @class Roo.bootstrap.ComboBox
10625  * @extends Roo.bootstrap.TriggerField
10626  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10627  * @cfg {Boolean} append (true|false) default false
10628  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10629  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10630  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10631  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10632  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10633  * @constructor
10634  * Create a new ComboBox.
10635  * @param {Object} config Configuration options
10636  */
10637 Roo.bootstrap.ComboBox = function(config){
10638     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10639     this.addEvents({
10640         /**
10641          * @event expand
10642          * Fires when the dropdown list is expanded
10643              * @param {Roo.bootstrap.ComboBox} combo This combo box
10644              */
10645         'expand' : true,
10646         /**
10647          * @event collapse
10648          * Fires when the dropdown list is collapsed
10649              * @param {Roo.bootstrap.ComboBox} combo This combo box
10650              */
10651         'collapse' : true,
10652         /**
10653          * @event beforeselect
10654          * Fires before a list item is selected. Return false to cancel the selection.
10655              * @param {Roo.bootstrap.ComboBox} combo This combo box
10656              * @param {Roo.data.Record} record The data record returned from the underlying store
10657              * @param {Number} index The index of the selected item in the dropdown list
10658              */
10659         'beforeselect' : true,
10660         /**
10661          * @event select
10662          * Fires when a list item is selected
10663              * @param {Roo.bootstrap.ComboBox} combo This combo box
10664              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10665              * @param {Number} index The index of the selected item in the dropdown list
10666              */
10667         'select' : true,
10668         /**
10669          * @event beforequery
10670          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10671          * The event object passed has these properties:
10672              * @param {Roo.bootstrap.ComboBox} combo This combo box
10673              * @param {String} query The query
10674              * @param {Boolean} forceAll true to force "all" query
10675              * @param {Boolean} cancel true to cancel the query
10676              * @param {Object} e The query event object
10677              */
10678         'beforequery': true,
10679          /**
10680          * @event add
10681          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10682              * @param {Roo.bootstrap.ComboBox} combo This combo box
10683              */
10684         'add' : true,
10685         /**
10686          * @event edit
10687          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10688              * @param {Roo.bootstrap.ComboBox} combo This combo box
10689              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10690              */
10691         'edit' : true,
10692         /**
10693          * @event remove
10694          * Fires when the remove value from the combobox array
10695              * @param {Roo.bootstrap.ComboBox} combo This combo box
10696              */
10697         'remove' : true
10698         
10699     });
10700     
10701     this.item = [];
10702     this.tickItems = [];
10703     
10704     this.selectedIndex = -1;
10705     if(this.mode == 'local'){
10706         if(config.queryDelay === undefined){
10707             this.queryDelay = 10;
10708         }
10709         if(config.minChars === undefined){
10710             this.minChars = 0;
10711         }
10712     }
10713 };
10714
10715 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10716      
10717     /**
10718      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10719      * rendering into an Roo.Editor, defaults to false)
10720      */
10721     /**
10722      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10723      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10724      */
10725     /**
10726      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10727      */
10728     /**
10729      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10730      * the dropdown list (defaults to undefined, with no header element)
10731      */
10732
10733      /**
10734      * @cfg {String/Roo.Template} tpl The template to use to render the output
10735      */
10736      
10737      /**
10738      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10739      */
10740     listWidth: undefined,
10741     /**
10742      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10743      * mode = 'remote' or 'text' if mode = 'local')
10744      */
10745     displayField: undefined,
10746     /**
10747      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10748      * mode = 'remote' or 'value' if mode = 'local'). 
10749      * Note: use of a valueField requires the user make a selection
10750      * in order for a value to be mapped.
10751      */
10752     valueField: undefined,
10753     
10754     
10755     /**
10756      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10757      * field's data value (defaults to the underlying DOM element's name)
10758      */
10759     hiddenName: undefined,
10760     /**
10761      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10762      */
10763     listClass: '',
10764     /**
10765      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10766      */
10767     selectedClass: 'active',
10768     
10769     /**
10770      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10771      */
10772     shadow:'sides',
10773     /**
10774      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10775      * anchor positions (defaults to 'tl-bl')
10776      */
10777     listAlign: 'tl-bl?',
10778     /**
10779      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10780      */
10781     maxHeight: 300,
10782     /**
10783      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10784      * query specified by the allQuery config option (defaults to 'query')
10785      */
10786     triggerAction: 'query',
10787     /**
10788      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10789      * (defaults to 4, does not apply if editable = false)
10790      */
10791     minChars : 4,
10792     /**
10793      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10794      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10795      */
10796     typeAhead: false,
10797     /**
10798      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10799      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10800      */
10801     queryDelay: 500,
10802     /**
10803      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10804      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10805      */
10806     pageSize: 0,
10807     /**
10808      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10809      * when editable = true (defaults to false)
10810      */
10811     selectOnFocus:false,
10812     /**
10813      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10814      */
10815     queryParam: 'query',
10816     /**
10817      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10818      * when mode = 'remote' (defaults to 'Loading...')
10819      */
10820     loadingText: 'Loading...',
10821     /**
10822      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10823      */
10824     resizable: false,
10825     /**
10826      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10827      */
10828     handleHeight : 8,
10829     /**
10830      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10831      * traditional select (defaults to true)
10832      */
10833     editable: true,
10834     /**
10835      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10836      */
10837     allQuery: '',
10838     /**
10839      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10840      */
10841     mode: 'remote',
10842     /**
10843      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10844      * listWidth has a higher value)
10845      */
10846     minListWidth : 70,
10847     /**
10848      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10849      * allow the user to set arbitrary text into the field (defaults to false)
10850      */
10851     forceSelection:false,
10852     /**
10853      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10854      * if typeAhead = true (defaults to 250)
10855      */
10856     typeAheadDelay : 250,
10857     /**
10858      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10859      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10860      */
10861     valueNotFoundText : undefined,
10862     /**
10863      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10864      */
10865     blockFocus : false,
10866     
10867     /**
10868      * @cfg {Boolean} disableClear Disable showing of clear button.
10869      */
10870     disableClear : false,
10871     /**
10872      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10873      */
10874     alwaysQuery : false,
10875     
10876     /**
10877      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10878      */
10879     multiple : false,
10880     
10881     /**
10882      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
10883      */
10884     invalidClass : "has-warning",
10885     
10886     /**
10887      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
10888      */
10889     validClass : "has-success",
10890     
10891     /**
10892      * @cfg {Boolean} loadonce  (true|false) load one time, then filter the records, default false
10893      */
10894     loadonce : false,
10895     
10896     //private
10897     addicon : false,
10898     editicon: false,
10899     
10900     page: 0,
10901     hasQuery: false,
10902     append: false,
10903     loadNext: false,
10904     autoFocus : true,
10905     tickable : false,
10906     btnPosition : 'right',
10907     triggerList : true,
10908     showToggleBtn : true,
10909     // element that contains real text value.. (when hidden is used..)
10910     
10911     getAutoCreate : function()
10912     {
10913         var cfg = false;
10914         
10915         /*
10916          *  Normal ComboBox
10917          */
10918         if(!this.tickable){
10919             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10920             return cfg;
10921         }
10922         
10923         /*
10924          *  ComboBox with tickable selections
10925          */
10926              
10927         var align = this.labelAlign || this.parentLabelAlign();
10928         
10929         cfg = {
10930             cls : 'form-group roo-combobox-tickable' //input-group
10931         };
10932         
10933         var buttons = {
10934             tag : 'div',
10935             cls : 'tickable-buttons',
10936             cn : [
10937                 {
10938                     tag : 'button',
10939                     type : 'button',
10940                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10941                     html : 'Edit'
10942                 },
10943                 {
10944                     tag : 'button',
10945                     type : 'button',
10946                     name : 'ok',
10947                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10948                     html : 'Done'
10949                 },
10950                 {
10951                     tag : 'button',
10952                     type : 'button',
10953                     name : 'cancel',
10954                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10955                     html : 'Cancel'
10956                 }
10957             ]
10958         };
10959         
10960         if(this.editable){
10961             buttons.cn.unshift({
10962                 tag: 'input',
10963                 cls: 'select2-search-field-input'
10964             });
10965         }
10966         
10967         var _this = this;
10968         
10969         Roo.each(buttons.cn, function(c){
10970             if (_this.size) {
10971                 c.cls += ' btn-' + _this.size;
10972             }
10973
10974             if (_this.disabled) {
10975                 c.disabled = true;
10976             }
10977         });
10978         
10979         var box = {
10980             tag: 'div',
10981             cn: [
10982                 {
10983                     tag: 'input',
10984                     type : 'hidden',
10985                     cls: 'form-hidden-field'
10986                 },
10987                 {
10988                     tag: 'ul',
10989                     cls: 'select2-choices',
10990                     cn:[
10991                         {
10992                             tag: 'li',
10993                             cls: 'select2-search-field',
10994                             cn: [
10995
10996                                 buttons
10997                             ]
10998                         }
10999                     ]
11000                 }
11001             ]
11002         }
11003         
11004         var combobox = {
11005             cls: 'select2-container input-group select2-container-multi',
11006             cn: [
11007                 box
11008 //                {
11009 //                    tag: 'ul',
11010 //                    cls: 'typeahead typeahead-long dropdown-menu',
11011 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11012 //                }
11013             ]
11014         };
11015         
11016         if(this.hasFeedback && !this.allowBlank){
11017             
11018             var feedback = {
11019                 tag: 'span',
11020                 cls: 'glyphicon form-control-feedback'
11021             };
11022
11023             combobox.cn.push(feedback);
11024         }
11025         
11026         if (align ==='left' && this.fieldLabel.length) {
11027             
11028                 Roo.log("left and has label");
11029                 cfg.cn = [
11030                     
11031                     {
11032                         tag: 'label',
11033                         'for' :  id,
11034                         cls : 'control-label col-sm-' + this.labelWidth,
11035                         html : this.fieldLabel
11036                         
11037                     },
11038                     {
11039                         cls : "col-sm-" + (12 - this.labelWidth), 
11040                         cn: [
11041                             combobox
11042                         ]
11043                     }
11044                     
11045                 ];
11046         } else if ( this.fieldLabel.length) {
11047                 Roo.log(" label");
11048                  cfg.cn = [
11049                    
11050                     {
11051                         tag: 'label',
11052                         //cls : 'input-group-addon',
11053                         html : this.fieldLabel
11054                         
11055                     },
11056                     
11057                     combobox
11058                     
11059                 ];
11060
11061         } else {
11062             
11063                 Roo.log(" no label && no align");
11064                 cfg = combobox
11065                      
11066                 
11067         }
11068          
11069         var settings=this;
11070         ['xs','sm','md','lg'].map(function(size){
11071             if (settings[size]) {
11072                 cfg.cls += ' col-' + size + '-' + settings[size];
11073             }
11074         });
11075         
11076         return cfg;
11077         
11078     },
11079     
11080     // private
11081     initEvents: function()
11082     {
11083         
11084         if (!this.store) {
11085             throw "can not find store for combo";
11086         }
11087         this.store = Roo.factory(this.store, Roo.data);
11088         
11089         if(this.tickable){
11090             this.initTickableEvents();
11091             return;
11092         }
11093         
11094         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11095         
11096         if(this.hiddenName){
11097             
11098             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11099             
11100             this.hiddenField.dom.value =
11101                 this.hiddenValue !== undefined ? this.hiddenValue :
11102                 this.value !== undefined ? this.value : '';
11103
11104             // prevent input submission
11105             this.el.dom.removeAttribute('name');
11106             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11107              
11108              
11109         }
11110         //if(Roo.isGecko){
11111         //    this.el.dom.setAttribute('autocomplete', 'off');
11112         //}
11113         
11114         var cls = 'x-combo-list';
11115         
11116         //this.list = new Roo.Layer({
11117         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11118         //});
11119         
11120         var _this = this;
11121         
11122         (function(){
11123             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11124             _this.list.setWidth(lw);
11125         }).defer(100);
11126         
11127         this.list.on('mouseover', this.onViewOver, this);
11128         this.list.on('mousemove', this.onViewMove, this);
11129         
11130         this.list.on('scroll', this.onViewScroll, this);
11131         
11132         /*
11133         this.list.swallowEvent('mousewheel');
11134         this.assetHeight = 0;
11135
11136         if(this.title){
11137             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11138             this.assetHeight += this.header.getHeight();
11139         }
11140
11141         this.innerList = this.list.createChild({cls:cls+'-inner'});
11142         this.innerList.on('mouseover', this.onViewOver, this);
11143         this.innerList.on('mousemove', this.onViewMove, this);
11144         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11145         
11146         if(this.allowBlank && !this.pageSize && !this.disableClear){
11147             this.footer = this.list.createChild({cls:cls+'-ft'});
11148             this.pageTb = new Roo.Toolbar(this.footer);
11149            
11150         }
11151         if(this.pageSize){
11152             this.footer = this.list.createChild({cls:cls+'-ft'});
11153             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11154                     {pageSize: this.pageSize});
11155             
11156         }
11157         
11158         if (this.pageTb && this.allowBlank && !this.disableClear) {
11159             var _this = this;
11160             this.pageTb.add(new Roo.Toolbar.Fill(), {
11161                 cls: 'x-btn-icon x-btn-clear',
11162                 text: '&#160;',
11163                 handler: function()
11164                 {
11165                     _this.collapse();
11166                     _this.clearValue();
11167                     _this.onSelect(false, -1);
11168                 }
11169             });
11170         }
11171         if (this.footer) {
11172             this.assetHeight += this.footer.getHeight();
11173         }
11174         */
11175             
11176         if(!this.tpl){
11177             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11178         }
11179
11180         this.view = new Roo.View(this.list, this.tpl, {
11181             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11182         });
11183         //this.view.wrapEl.setDisplayed(false);
11184         this.view.on('click', this.onViewClick, this);
11185         
11186         
11187         
11188         this.store.on('beforeload', this.onBeforeLoad, this);
11189         this.store.on('load', this.onLoad, this);
11190         this.store.on('loadexception', this.onLoadException, this);
11191         /*
11192         if(this.resizable){
11193             this.resizer = new Roo.Resizable(this.list,  {
11194                pinned:true, handles:'se'
11195             });
11196             this.resizer.on('resize', function(r, w, h){
11197                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11198                 this.listWidth = w;
11199                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11200                 this.restrictHeight();
11201             }, this);
11202             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11203         }
11204         */
11205         if(!this.editable){
11206             this.editable = true;
11207             this.setEditable(false);
11208         }
11209         
11210         /*
11211         
11212         if (typeof(this.events.add.listeners) != 'undefined') {
11213             
11214             this.addicon = this.wrap.createChild(
11215                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11216        
11217             this.addicon.on('click', function(e) {
11218                 this.fireEvent('add', this);
11219             }, this);
11220         }
11221         if (typeof(this.events.edit.listeners) != 'undefined') {
11222             
11223             this.editicon = this.wrap.createChild(
11224                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11225             if (this.addicon) {
11226                 this.editicon.setStyle('margin-left', '40px');
11227             }
11228             this.editicon.on('click', function(e) {
11229                 
11230                 // we fire even  if inothing is selected..
11231                 this.fireEvent('edit', this, this.lastData );
11232                 
11233             }, this);
11234         }
11235         */
11236         
11237         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11238             "up" : function(e){
11239                 this.inKeyMode = true;
11240                 this.selectPrev();
11241             },
11242
11243             "down" : function(e){
11244                 if(!this.isExpanded()){
11245                     this.onTriggerClick();
11246                 }else{
11247                     this.inKeyMode = true;
11248                     this.selectNext();
11249                 }
11250             },
11251
11252             "enter" : function(e){
11253 //                this.onViewClick();
11254                 //return true;
11255                 this.collapse();
11256                 
11257                 if(this.fireEvent("specialkey", this, e)){
11258                     this.onViewClick(false);
11259                 }
11260                 
11261                 return true;
11262             },
11263
11264             "esc" : function(e){
11265                 this.collapse();
11266             },
11267
11268             "tab" : function(e){
11269                 this.collapse();
11270                 
11271                 if(this.fireEvent("specialkey", this, e)){
11272                     this.onViewClick(false);
11273                 }
11274                 
11275                 return true;
11276             },
11277
11278             scope : this,
11279
11280             doRelay : function(foo, bar, hname){
11281                 if(hname == 'down' || this.scope.isExpanded()){
11282                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11283                 }
11284                 return true;
11285             },
11286
11287             forceKeyDown: true
11288         });
11289         
11290         
11291         this.queryDelay = Math.max(this.queryDelay || 10,
11292                 this.mode == 'local' ? 10 : 250);
11293         
11294         
11295         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11296         
11297         if(this.typeAhead){
11298             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11299         }
11300         if(this.editable !== false){
11301             this.inputEl().on("keyup", this.onKeyUp, this);
11302         }
11303         if(this.forceSelection){
11304             this.inputEl().on('blur', this.doForce, this);
11305         }
11306         
11307         if(this.multiple){
11308             this.choices = this.el.select('ul.select2-choices', true).first();
11309             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11310         }
11311     },
11312     
11313     initTickableEvents: function()
11314     {   
11315         this.createList();
11316         
11317         if(this.hiddenName){
11318             
11319             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11320             
11321             this.hiddenField.dom.value =
11322                 this.hiddenValue !== undefined ? this.hiddenValue :
11323                 this.value !== undefined ? this.value : '';
11324
11325             // prevent input submission
11326             this.el.dom.removeAttribute('name');
11327             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11328              
11329              
11330         }
11331         
11332 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11333         
11334         this.choices = this.el.select('ul.select2-choices', true).first();
11335         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11336         if(this.triggerList){
11337             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11338         }
11339          
11340         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11341         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11342         
11343         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11344         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11345         
11346         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11347         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11348         
11349         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11350         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11351         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11352         
11353         this.okBtn.hide();
11354         this.cancelBtn.hide();
11355         
11356         var _this = this;
11357         
11358         (function(){
11359             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11360             _this.list.setWidth(lw);
11361         }).defer(100);
11362         
11363         this.list.on('mouseover', this.onViewOver, this);
11364         this.list.on('mousemove', this.onViewMove, this);
11365         
11366         this.list.on('scroll', this.onViewScroll, this);
11367         
11368         if(!this.tpl){
11369             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>';
11370         }
11371
11372         this.view = new Roo.View(this.list, this.tpl, {
11373             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11374         });
11375         
11376         //this.view.wrapEl.setDisplayed(false);
11377         this.view.on('click', this.onViewClick, this);
11378         
11379         
11380         
11381         this.store.on('beforeload', this.onBeforeLoad, this);
11382         this.store.on('load', this.onLoad, this);
11383         this.store.on('loadexception', this.onLoadException, this);
11384         
11385         if(this.editable){
11386             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11387                 "up" : function(e){
11388                     this.inKeyMode = true;
11389                     this.selectPrev();
11390                 },
11391
11392                 "down" : function(e){
11393                     this.inKeyMode = true;
11394                     this.selectNext();
11395                 },
11396
11397                 "enter" : function(e){
11398                     if(this.fireEvent("specialkey", this, e)){
11399                         this.onViewClick(false);
11400                     }
11401                     
11402                     return true;
11403                 },
11404
11405                 "esc" : function(e){
11406                     this.onTickableFooterButtonClick(e, false, false);
11407                 },
11408
11409                 "tab" : function(e){
11410                     this.fireEvent("specialkey", this, e);
11411                     
11412                     this.onTickableFooterButtonClick(e, false, false);
11413                     
11414                     return true;
11415                 },
11416
11417                 scope : this,
11418
11419                 doRelay : function(e, fn, key){
11420                     if(this.scope.isExpanded()){
11421                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11422                     }
11423                     return true;
11424                 },
11425
11426                 forceKeyDown: true
11427             });
11428         }
11429         
11430         this.queryDelay = Math.max(this.queryDelay || 10,
11431                 this.mode == 'local' ? 10 : 250);
11432         
11433         
11434         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11435         
11436         if(this.typeAhead){
11437             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11438         }
11439         
11440         if(this.editable !== false){
11441             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11442         }
11443         
11444     },
11445
11446     onDestroy : function(){
11447         if(this.view){
11448             this.view.setStore(null);
11449             this.view.el.removeAllListeners();
11450             this.view.el.remove();
11451             this.view.purgeListeners();
11452         }
11453         if(this.list){
11454             this.list.dom.innerHTML  = '';
11455         }
11456         
11457         if(this.store){
11458             this.store.un('beforeload', this.onBeforeLoad, this);
11459             this.store.un('load', this.onLoad, this);
11460             this.store.un('loadexception', this.onLoadException, this);
11461         }
11462         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11463     },
11464
11465     // private
11466     fireKey : function(e){
11467         if(e.isNavKeyPress() && !this.list.isVisible()){
11468             this.fireEvent("specialkey", this, e);
11469         }
11470     },
11471
11472     // private
11473     onResize: function(w, h){
11474 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11475 //        
11476 //        if(typeof w != 'number'){
11477 //            // we do not handle it!?!?
11478 //            return;
11479 //        }
11480 //        var tw = this.trigger.getWidth();
11481 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11482 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11483 //        var x = w - tw;
11484 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11485 //            
11486 //        //this.trigger.setStyle('left', x+'px');
11487 //        
11488 //        if(this.list && this.listWidth === undefined){
11489 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11490 //            this.list.setWidth(lw);
11491 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11492 //        }
11493         
11494     
11495         
11496     },
11497
11498     /**
11499      * Allow or prevent the user from directly editing the field text.  If false is passed,
11500      * the user will only be able to select from the items defined in the dropdown list.  This method
11501      * is the runtime equivalent of setting the 'editable' config option at config time.
11502      * @param {Boolean} value True to allow the user to directly edit the field text
11503      */
11504     setEditable : function(value){
11505         if(value == this.editable){
11506             return;
11507         }
11508         this.editable = value;
11509         if(!value){
11510             this.inputEl().dom.setAttribute('readOnly', true);
11511             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11512             this.inputEl().addClass('x-combo-noedit');
11513         }else{
11514             this.inputEl().dom.setAttribute('readOnly', false);
11515             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11516             this.inputEl().removeClass('x-combo-noedit');
11517         }
11518     },
11519
11520     // private
11521     
11522     onBeforeLoad : function(combo,opts){
11523         if(!this.hasFocus){
11524             return;
11525         }
11526          if (!opts.add) {
11527             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11528          }
11529         this.restrictHeight();
11530         this.selectedIndex = -1;
11531     },
11532
11533     // private
11534     onLoad : function(){
11535         
11536         this.hasQuery = false;
11537         
11538         if(!this.hasFocus){
11539             return;
11540         }
11541         
11542         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11543             this.loading.hide();
11544         }
11545         
11546         if(this.store.getCount() > 0){
11547             this.expand();
11548             this.restrictHeight();
11549             if(this.lastQuery == this.allQuery){
11550                 if(this.editable && !this.tickable){
11551                     this.inputEl().dom.select();
11552                 }
11553                 
11554                 if(
11555                     !this.selectByValue(this.value, true) &&
11556                     this.autoFocus && 
11557                     (
11558                         !this.store.lastOptions ||
11559                         typeof(this.store.lastOptions.add) == 'undefined' || 
11560                         this.store.lastOptions.add != true
11561                     )
11562                 ){
11563                     this.select(0, true);
11564                 }
11565             }else{
11566                 if(this.autoFocus){
11567                     this.selectNext();
11568                 }
11569                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11570                     this.taTask.delay(this.typeAheadDelay);
11571                 }
11572             }
11573         }else{
11574             this.restrictHeight();
11575             this.onEmptyResults();
11576         }
11577         
11578         //this.el.focus();
11579     },
11580     // private
11581     onLoadException : function()
11582     {
11583         this.hasQuery = false;
11584         
11585         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11586             this.loading.hide();
11587         }
11588         
11589         if(this.tickable && this.editable){
11590             return;
11591         }
11592         
11593         this.collapse();
11594         
11595         Roo.log(this.store.reader.jsonData);
11596         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11597             // fixme
11598             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11599         }
11600         
11601         
11602     },
11603     // private
11604     onTypeAhead : function(){
11605         if(this.store.getCount() > 0){
11606             var r = this.store.getAt(0);
11607             var newValue = r.data[this.displayField];
11608             var len = newValue.length;
11609             var selStart = this.getRawValue().length;
11610             
11611             if(selStart != len){
11612                 this.setRawValue(newValue);
11613                 this.selectText(selStart, newValue.length);
11614             }
11615         }
11616     },
11617
11618     // private
11619     onSelect : function(record, index){
11620         
11621         if(this.fireEvent('beforeselect', this, record, index) !== false){
11622         
11623             this.setFromData(index > -1 ? record.data : false);
11624             
11625             this.collapse();
11626             this.fireEvent('select', this, record, index);
11627         }
11628     },
11629
11630     /**
11631      * Returns the currently selected field value or empty string if no value is set.
11632      * @return {String} value The selected value
11633      */
11634     getValue : function(){
11635         
11636         if(this.multiple){
11637             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11638         }
11639         
11640         if(this.valueField){
11641             return typeof this.value != 'undefined' ? this.value : '';
11642         }else{
11643             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11644         }
11645     },
11646
11647     /**
11648      * Clears any text/value currently set in the field
11649      */
11650     clearValue : function(){
11651         if(this.hiddenField){
11652             this.hiddenField.dom.value = '';
11653         }
11654         this.value = '';
11655         this.setRawValue('');
11656         this.lastSelectionText = '';
11657         this.lastData = false;
11658         
11659     },
11660
11661     /**
11662      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11663      * will be displayed in the field.  If the value does not match the data value of an existing item,
11664      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11665      * Otherwise the field will be blank (although the value will still be set).
11666      * @param {String} value The value to match
11667      */
11668     setValue : function(v){
11669         if(this.multiple){
11670             this.syncValue();
11671             return;
11672         }
11673         
11674         var text = v;
11675         if(this.valueField){
11676             var r = this.findRecord(this.valueField, v);
11677             if(r){
11678                 text = r.data[this.displayField];
11679             }else if(this.valueNotFoundText !== undefined){
11680                 text = this.valueNotFoundText;
11681             }
11682         }
11683         this.lastSelectionText = text;
11684         if(this.hiddenField){
11685             this.hiddenField.dom.value = v;
11686         }
11687         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11688         this.value = v;
11689     },
11690     /**
11691      * @property {Object} the last set data for the element
11692      */
11693     
11694     lastData : false,
11695     /**
11696      * Sets the value of the field based on a object which is related to the record format for the store.
11697      * @param {Object} value the value to set as. or false on reset?
11698      */
11699     setFromData : function(o){
11700         
11701         if(this.multiple){
11702             this.addItem(o);
11703             return;
11704         }
11705             
11706         var dv = ''; // display value
11707         var vv = ''; // value value..
11708         this.lastData = o;
11709         if (this.displayField) {
11710             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11711         } else {
11712             // this is an error condition!!!
11713             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11714         }
11715         
11716         if(this.valueField){
11717             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11718         }
11719         
11720         if(this.hiddenField){
11721             this.hiddenField.dom.value = vv;
11722             
11723             this.lastSelectionText = dv;
11724             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11725             this.value = vv;
11726             return;
11727         }
11728         // no hidden field.. - we store the value in 'value', but still display
11729         // display field!!!!
11730         this.lastSelectionText = dv;
11731         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11732         this.value = vv;
11733         
11734         
11735     },
11736     // private
11737     reset : function(){
11738         // overridden so that last data is reset..
11739         this.setValue(this.originalValue);
11740         this.clearInvalid();
11741         this.lastData = false;
11742         if (this.view) {
11743             this.view.clearSelections();
11744         }
11745     },
11746     // private
11747     findRecord : function(prop, value){
11748         var record;
11749         if(this.store.getCount() > 0){
11750             this.store.each(function(r){
11751                 if(r.data[prop] == value){
11752                     record = r;
11753                     return false;
11754                 }
11755                 return true;
11756             });
11757         }
11758         return record;
11759     },
11760     
11761     getName: function()
11762     {
11763         // returns hidden if it's set..
11764         if (!this.rendered) {return ''};
11765         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11766         
11767     },
11768     // private
11769     onViewMove : function(e, t){
11770         this.inKeyMode = false;
11771     },
11772
11773     // private
11774     onViewOver : function(e, t){
11775         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11776             return;
11777         }
11778         var item = this.view.findItemFromChild(t);
11779         
11780         if(item){
11781             var index = this.view.indexOf(item);
11782             this.select(index, false);
11783         }
11784     },
11785
11786     // private
11787     onViewClick : function(view, doFocus, el, e)
11788     {
11789         var index = this.view.getSelectedIndexes()[0];
11790         
11791         var r = this.store.getAt(index);
11792         
11793         if(this.tickable){
11794             
11795             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
11796                 return;
11797             }
11798             
11799             var rm = false;
11800             var _this = this;
11801             
11802             Roo.each(this.tickItems, function(v,k){
11803                 
11804                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11805                     _this.tickItems.splice(k, 1);
11806                     
11807                     if(typeof(e) == 'undefined' && view == false){
11808                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
11809                     }
11810                     
11811                     rm = true;
11812                     return;
11813                 }
11814             });
11815             
11816             if(rm){
11817                 return;
11818             }
11819             
11820             this.tickItems.push(r.data);
11821             
11822             if(typeof(e) == 'undefined' && view == false){
11823                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
11824             }
11825                     
11826             return;
11827         }
11828         
11829         if(r){
11830             this.onSelect(r, index);
11831         }
11832         if(doFocus !== false && !this.blockFocus){
11833             this.inputEl().focus();
11834         }
11835     },
11836
11837     // private
11838     restrictHeight : function(){
11839         //this.innerList.dom.style.height = '';
11840         //var inner = this.innerList.dom;
11841         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11842         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11843         //this.list.beginUpdate();
11844         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11845         this.list.alignTo(this.inputEl(), this.listAlign);
11846         this.list.alignTo(this.inputEl(), this.listAlign);
11847         //this.list.endUpdate();
11848     },
11849
11850     // private
11851     onEmptyResults : function(){
11852         
11853         if(this.tickable && this.editable){
11854             return;
11855         }
11856         
11857         this.collapse();
11858     },
11859
11860     /**
11861      * Returns true if the dropdown list is expanded, else false.
11862      */
11863     isExpanded : function(){
11864         return this.list.isVisible();
11865     },
11866
11867     /**
11868      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11869      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11870      * @param {String} value The data value of the item to select
11871      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11872      * selected item if it is not currently in view (defaults to true)
11873      * @return {Boolean} True if the value matched an item in the list, else false
11874      */
11875     selectByValue : function(v, scrollIntoView){
11876         if(v !== undefined && v !== null){
11877             var r = this.findRecord(this.valueField || this.displayField, v);
11878             if(r){
11879                 this.select(this.store.indexOf(r), scrollIntoView);
11880                 return true;
11881             }
11882         }
11883         return false;
11884     },
11885
11886     /**
11887      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11888      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11889      * @param {Number} index The zero-based index of the list item to select
11890      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11891      * selected item if it is not currently in view (defaults to true)
11892      */
11893     select : function(index, scrollIntoView){
11894         this.selectedIndex = index;
11895         this.view.select(index);
11896         if(scrollIntoView !== false){
11897             var el = this.view.getNode(index);
11898             /*
11899              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
11900              */
11901             if(el){
11902                 this.list.scrollChildIntoView(el, false);
11903             }
11904         }
11905     },
11906
11907     // private
11908     selectNext : function(){
11909         var ct = this.store.getCount();
11910         if(ct > 0){
11911             if(this.selectedIndex == -1){
11912                 this.select(0);
11913             }else if(this.selectedIndex < ct-1){
11914                 this.select(this.selectedIndex+1);
11915             }
11916         }
11917     },
11918
11919     // private
11920     selectPrev : function(){
11921         var ct = this.store.getCount();
11922         if(ct > 0){
11923             if(this.selectedIndex == -1){
11924                 this.select(0);
11925             }else if(this.selectedIndex != 0){
11926                 this.select(this.selectedIndex-1);
11927             }
11928         }
11929     },
11930
11931     // private
11932     onKeyUp : function(e){
11933         if(this.editable !== false && !e.isSpecialKey()){
11934             this.lastKey = e.getKey();
11935             this.dqTask.delay(this.queryDelay);
11936         }
11937     },
11938
11939     // private
11940     validateBlur : function(){
11941         return !this.list || !this.list.isVisible();   
11942     },
11943
11944     // private
11945     initQuery : function(){
11946         
11947         var v = this.getRawValue();
11948         
11949         if(this.tickable && this.editable){
11950             v = this.tickableInputEl().getValue();
11951         }
11952         
11953         this.doQuery(v);
11954     },
11955
11956     // private
11957     doForce : function(){
11958         if(this.inputEl().dom.value.length > 0){
11959             this.inputEl().dom.value =
11960                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11961              
11962         }
11963     },
11964
11965     /**
11966      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11967      * query allowing the query action to be canceled if needed.
11968      * @param {String} query The SQL query to execute
11969      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11970      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11971      * saved in the current store (defaults to false)
11972      */
11973     doQuery : function(q, forceAll){
11974         
11975         if(q === undefined || q === null){
11976             q = '';
11977         }
11978         var qe = {
11979             query: q,
11980             forceAll: forceAll,
11981             combo: this,
11982             cancel:false
11983         };
11984         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11985             return false;
11986         }
11987         q = qe.query;
11988         
11989         forceAll = qe.forceAll;
11990         if(forceAll === true || (q.length >= this.minChars)){
11991             
11992             this.hasQuery = true;
11993             
11994             if(this.lastQuery != q || this.alwaysQuery){
11995                 this.lastQuery = q;
11996                 if(this.mode == 'local'){
11997                     this.selectedIndex = -1;
11998                     if(forceAll){
11999                         this.store.clearFilter();
12000                     }else{
12001                         this.store.filter(this.displayField, q);
12002                     }
12003                     this.onLoad();
12004                 }else{
12005                     this.store.baseParams[this.queryParam] = q;
12006                     
12007                     var options = {params : this.getParams(q)};
12008                     
12009                     if(this.loadNext){
12010                         options.add = true;
12011                         options.params.start = this.page * this.pageSize;
12012                     }
12013                     
12014                     if(this.loadonce && this.store.getCount() > 0){
12015                         this.selectedIndex = -1;
12016                         if(forceAll){
12017                             this.store.clearFilter();
12018                         }else{
12019                             this.store.filter(this.displayField, q);
12020                         }
12021                         this.onLoad();
12022                         return;
12023                     }
12024                     
12025                     this.store.load(options);
12026                     
12027                     /*
12028                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12029                      *  we should expand the list on onLoad
12030                      *  so command out it
12031                      */
12032 //                    this.expand();
12033                 }
12034             }else{
12035                 this.selectedIndex = -1;
12036                 this.onLoad();   
12037             }
12038         }
12039         
12040         this.loadNext = false;
12041     },
12042
12043     // private
12044     getParams : function(q){
12045         var p = {};
12046         //p[this.queryParam] = q;
12047         
12048         if(this.pageSize){
12049             p.start = 0;
12050             p.limit = this.pageSize;
12051         }
12052         return p;
12053     },
12054
12055     /**
12056      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12057      */
12058     collapse : function(){
12059         if(!this.isExpanded()){
12060             return;
12061         }
12062         
12063         this.list.hide();
12064         
12065         if(this.tickable){
12066             this.hasFocus = false;
12067             this.okBtn.hide();
12068             this.cancelBtn.hide();
12069             this.trigger.show();
12070             
12071             if(this.editable){
12072                 this.tickableInputEl().dom.value = '';
12073                 this.tickableInputEl().blur();
12074             }
12075             
12076         }
12077         
12078         Roo.get(document).un('mousedown', this.collapseIf, this);
12079         Roo.get(document).un('mousewheel', this.collapseIf, this);
12080         if (!this.editable) {
12081             Roo.get(document).un('keydown', this.listKeyPress, this);
12082         }
12083         this.fireEvent('collapse', this);
12084     },
12085
12086     // private
12087     collapseIf : function(e){
12088         var in_combo  = e.within(this.el);
12089         var in_list =  e.within(this.list);
12090         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12091         
12092         if (in_combo || in_list || is_list) {
12093             //e.stopPropagation();
12094             return;
12095         }
12096         
12097         if(this.tickable){
12098             this.onTickableFooterButtonClick(e, false, false);
12099         }
12100
12101         this.collapse();
12102         
12103     },
12104
12105     /**
12106      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12107      */
12108     expand : function(){
12109        
12110         if(this.isExpanded() || !this.hasFocus){
12111             return;
12112         }
12113         
12114         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12115         this.list.setWidth(lw);
12116         
12117         
12118          Roo.log('expand');
12119         
12120         this.list.show();
12121         
12122         this.restrictHeight();
12123         
12124         if(this.tickable){
12125             
12126             this.tickItems = Roo.apply([], this.item);
12127             
12128             this.okBtn.show();
12129             this.cancelBtn.show();
12130             this.trigger.hide();
12131             
12132             if(this.editable){
12133                 this.tickableInputEl().focus();
12134             }
12135             
12136         }
12137         
12138         Roo.get(document).on('mousedown', this.collapseIf, this);
12139         Roo.get(document).on('mousewheel', this.collapseIf, this);
12140         if (!this.editable) {
12141             Roo.get(document).on('keydown', this.listKeyPress, this);
12142         }
12143         
12144         this.fireEvent('expand', this);
12145     },
12146
12147     // private
12148     // Implements the default empty TriggerField.onTriggerClick function
12149     onTriggerClick : function(e)
12150     {
12151         Roo.log('trigger click');
12152         
12153         if(this.disabled || !this.triggerList){
12154             return;
12155         }
12156         
12157         this.page = 0;
12158         this.loadNext = false;
12159         
12160         if(this.isExpanded()){
12161             this.collapse();
12162             if (!this.blockFocus) {
12163                 this.inputEl().focus();
12164             }
12165             
12166         }else {
12167             this.hasFocus = true;
12168             if(this.triggerAction == 'all') {
12169                 this.doQuery(this.allQuery, true);
12170             } else {
12171                 this.doQuery(this.getRawValue());
12172             }
12173             if (!this.blockFocus) {
12174                 this.inputEl().focus();
12175             }
12176         }
12177     },
12178     
12179     onTickableTriggerClick : function(e)
12180     {
12181         if(this.disabled){
12182             return;
12183         }
12184         
12185         this.page = 0;
12186         this.loadNext = false;
12187         this.hasFocus = true;
12188         
12189         if(this.triggerAction == 'all') {
12190             this.doQuery(this.allQuery, true);
12191         } else {
12192             this.doQuery(this.getRawValue());
12193         }
12194     },
12195     
12196     onSearchFieldClick : function(e)
12197     {
12198         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12199             this.onTickableFooterButtonClick(e, false, false);
12200             return;
12201         }
12202         
12203         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12204             return;
12205         }
12206         
12207         this.page = 0;
12208         this.loadNext = false;
12209         this.hasFocus = true;
12210         
12211         if(this.triggerAction == 'all') {
12212             this.doQuery(this.allQuery, true);
12213         } else {
12214             this.doQuery(this.getRawValue());
12215         }
12216     },
12217     
12218     listKeyPress : function(e)
12219     {
12220         //Roo.log('listkeypress');
12221         // scroll to first matching element based on key pres..
12222         if (e.isSpecialKey()) {
12223             return false;
12224         }
12225         var k = String.fromCharCode(e.getKey()).toUpperCase();
12226         //Roo.log(k);
12227         var match  = false;
12228         var csel = this.view.getSelectedNodes();
12229         var cselitem = false;
12230         if (csel.length) {
12231             var ix = this.view.indexOf(csel[0]);
12232             cselitem  = this.store.getAt(ix);
12233             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12234                 cselitem = false;
12235             }
12236             
12237         }
12238         
12239         this.store.each(function(v) { 
12240             if (cselitem) {
12241                 // start at existing selection.
12242                 if (cselitem.id == v.id) {
12243                     cselitem = false;
12244                 }
12245                 return true;
12246             }
12247                 
12248             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12249                 match = this.store.indexOf(v);
12250                 return false;
12251             }
12252             return true;
12253         }, this);
12254         
12255         if (match === false) {
12256             return true; // no more action?
12257         }
12258         // scroll to?
12259         this.view.select(match);
12260         var sn = Roo.get(this.view.getSelectedNodes()[0])
12261         sn.scrollIntoView(sn.dom.parentNode, false);
12262     },
12263     
12264     onViewScroll : function(e, t){
12265         
12266         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){
12267             return;
12268         }
12269         
12270         this.hasQuery = true;
12271         
12272         this.loading = this.list.select('.loading', true).first();
12273         
12274         if(this.loading === null){
12275             this.list.createChild({
12276                 tag: 'div',
12277                 cls: 'loading select2-more-results select2-active',
12278                 html: 'Loading more results...'
12279             })
12280             
12281             this.loading = this.list.select('.loading', true).first();
12282             
12283             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12284             
12285             this.loading.hide();
12286         }
12287         
12288         this.loading.show();
12289         
12290         var _combo = this;
12291         
12292         this.page++;
12293         this.loadNext = true;
12294         
12295         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12296         
12297         return;
12298     },
12299     
12300     addItem : function(o)
12301     {   
12302         var dv = ''; // display value
12303         
12304         if (this.displayField) {
12305             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12306         } else {
12307             // this is an error condition!!!
12308             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12309         }
12310         
12311         if(!dv.length){
12312             return;
12313         }
12314         
12315         var choice = this.choices.createChild({
12316             tag: 'li',
12317             cls: 'select2-search-choice',
12318             cn: [
12319                 {
12320                     tag: 'div',
12321                     html: dv
12322                 },
12323                 {
12324                     tag: 'a',
12325                     href: '#',
12326                     cls: 'select2-search-choice-close',
12327                     tabindex: '-1'
12328                 }
12329             ]
12330             
12331         }, this.searchField);
12332         
12333         var close = choice.select('a.select2-search-choice-close', true).first()
12334         
12335         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12336         
12337         this.item.push(o);
12338         
12339         this.lastData = o;
12340         
12341         this.syncValue();
12342         
12343         this.inputEl().dom.value = '';
12344         
12345         this.validate();
12346     },
12347     
12348     onRemoveItem : function(e, _self, o)
12349     {
12350         e.preventDefault();
12351         
12352         this.lastItem = Roo.apply([], this.item);
12353         
12354         var index = this.item.indexOf(o.data) * 1;
12355         
12356         if( index < 0){
12357             Roo.log('not this item?!');
12358             return;
12359         }
12360         
12361         this.item.splice(index, 1);
12362         o.item.remove();
12363         
12364         this.syncValue();
12365         
12366         this.fireEvent('remove', this, e);
12367         
12368         this.validate();
12369         
12370     },
12371     
12372     syncValue : function()
12373     {
12374         if(!this.item.length){
12375             this.clearValue();
12376             return;
12377         }
12378             
12379         var value = [];
12380         var _this = this;
12381         Roo.each(this.item, function(i){
12382             if(_this.valueField){
12383                 value.push(i[_this.valueField]);
12384                 return;
12385             }
12386
12387             value.push(i);
12388         });
12389
12390         this.value = value.join(',');
12391
12392         if(this.hiddenField){
12393             this.hiddenField.dom.value = this.value;
12394         }
12395     },
12396     
12397     clearItem : function()
12398     {
12399         if(!this.multiple){
12400             return;
12401         }
12402         
12403         this.item = [];
12404         
12405         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12406            c.remove();
12407         });
12408         
12409         this.syncValue();
12410         
12411         this.validate();
12412     },
12413     
12414     inputEl: function ()
12415     {
12416         if(this.tickable){
12417             return this.searchField;
12418         }
12419         return this.el.select('input.form-control',true).first();
12420     },
12421     
12422     
12423     onTickableFooterButtonClick : function(e, btn, el)
12424     {
12425         e.preventDefault();
12426         
12427         this.lastItem = Roo.apply([], this.item);
12428         
12429         if(btn && btn.name == 'cancel'){
12430             this.tickItems = Roo.apply([], this.item);
12431             this.collapse();
12432             return;
12433         }
12434         
12435         this.clearItem();
12436         
12437         var _this = this;
12438         
12439         Roo.each(this.tickItems, function(o){
12440             _this.addItem(o);
12441         });
12442         
12443         this.collapse();
12444         
12445     },
12446     
12447     validate : function()
12448     {
12449         var v = this.getRawValue();
12450         
12451         if(this.multiple){
12452             v = this.getValue();
12453         }
12454         
12455         if(this.disabled || this.allowBlank || v.length){
12456             this.markValid();
12457             return true;
12458         }
12459         
12460         this.markInvalid();
12461         return false;
12462     },
12463     
12464     tickableInputEl : function()
12465     {
12466         if(!this.tickable || !this.editable){
12467             return this.inputEl();
12468         }
12469         
12470         return this.inputEl().select('.select2-search-field-input', true).first();
12471     }
12472     
12473     
12474
12475     /** 
12476     * @cfg {Boolean} grow 
12477     * @hide 
12478     */
12479     /** 
12480     * @cfg {Number} growMin 
12481     * @hide 
12482     */
12483     /** 
12484     * @cfg {Number} growMax 
12485     * @hide 
12486     */
12487     /**
12488      * @hide
12489      * @method autoSize
12490      */
12491 });
12492 /*
12493  * Based on:
12494  * Ext JS Library 1.1.1
12495  * Copyright(c) 2006-2007, Ext JS, LLC.
12496  *
12497  * Originally Released Under LGPL - original licence link has changed is not relivant.
12498  *
12499  * Fork - LGPL
12500  * <script type="text/javascript">
12501  */
12502
12503 /**
12504  * @class Roo.View
12505  * @extends Roo.util.Observable
12506  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12507  * This class also supports single and multi selection modes. <br>
12508  * Create a data model bound view:
12509  <pre><code>
12510  var store = new Roo.data.Store(...);
12511
12512  var view = new Roo.View({
12513     el : "my-element",
12514     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12515  
12516     singleSelect: true,
12517     selectedClass: "ydataview-selected",
12518     store: store
12519  });
12520
12521  // listen for node click?
12522  view.on("click", function(vw, index, node, e){
12523  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12524  });
12525
12526  // load XML data
12527  dataModel.load("foobar.xml");
12528  </code></pre>
12529  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12530  * <br><br>
12531  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12532  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12533  * 
12534  * Note: old style constructor is still suported (container, template, config)
12535  * 
12536  * @constructor
12537  * Create a new View
12538  * @param {Object} config The config object
12539  * 
12540  */
12541 Roo.View = function(config, depreciated_tpl, depreciated_config){
12542     
12543     this.parent = false;
12544     
12545     if (typeof(depreciated_tpl) == 'undefined') {
12546         // new way.. - universal constructor.
12547         Roo.apply(this, config);
12548         this.el  = Roo.get(this.el);
12549     } else {
12550         // old format..
12551         this.el  = Roo.get(config);
12552         this.tpl = depreciated_tpl;
12553         Roo.apply(this, depreciated_config);
12554     }
12555     this.wrapEl  = this.el.wrap().wrap();
12556     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12557     
12558     
12559     if(typeof(this.tpl) == "string"){
12560         this.tpl = new Roo.Template(this.tpl);
12561     } else {
12562         // support xtype ctors..
12563         this.tpl = new Roo.factory(this.tpl, Roo);
12564     }
12565     
12566     
12567     this.tpl.compile();
12568     
12569     /** @private */
12570     this.addEvents({
12571         /**
12572          * @event beforeclick
12573          * Fires before a click is processed. Returns false to cancel the default action.
12574          * @param {Roo.View} this
12575          * @param {Number} index The index of the target node
12576          * @param {HTMLElement} node The target node
12577          * @param {Roo.EventObject} e The raw event object
12578          */
12579             "beforeclick" : true,
12580         /**
12581          * @event click
12582          * Fires when a template node is clicked.
12583          * @param {Roo.View} this
12584          * @param {Number} index The index of the target node
12585          * @param {HTMLElement} node The target node
12586          * @param {Roo.EventObject} e The raw event object
12587          */
12588             "click" : true,
12589         /**
12590          * @event dblclick
12591          * Fires when a template node is double clicked.
12592          * @param {Roo.View} this
12593          * @param {Number} index The index of the target node
12594          * @param {HTMLElement} node The target node
12595          * @param {Roo.EventObject} e The raw event object
12596          */
12597             "dblclick" : true,
12598         /**
12599          * @event contextmenu
12600          * Fires when a template node is right clicked.
12601          * @param {Roo.View} this
12602          * @param {Number} index The index of the target node
12603          * @param {HTMLElement} node The target node
12604          * @param {Roo.EventObject} e The raw event object
12605          */
12606             "contextmenu" : true,
12607         /**
12608          * @event selectionchange
12609          * Fires when the selected nodes change.
12610          * @param {Roo.View} this
12611          * @param {Array} selections Array of the selected nodes
12612          */
12613             "selectionchange" : true,
12614     
12615         /**
12616          * @event beforeselect
12617          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12618          * @param {Roo.View} this
12619          * @param {HTMLElement} node The node to be selected
12620          * @param {Array} selections Array of currently selected nodes
12621          */
12622             "beforeselect" : true,
12623         /**
12624          * @event preparedata
12625          * Fires on every row to render, to allow you to change the data.
12626          * @param {Roo.View} this
12627          * @param {Object} data to be rendered (change this)
12628          */
12629           "preparedata" : true
12630           
12631           
12632         });
12633
12634
12635
12636     this.el.on({
12637         "click": this.onClick,
12638         "dblclick": this.onDblClick,
12639         "contextmenu": this.onContextMenu,
12640         scope:this
12641     });
12642
12643     this.selections = [];
12644     this.nodes = [];
12645     this.cmp = new Roo.CompositeElementLite([]);
12646     if(this.store){
12647         this.store = Roo.factory(this.store, Roo.data);
12648         this.setStore(this.store, true);
12649     }
12650     
12651     if ( this.footer && this.footer.xtype) {
12652            
12653          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12654         
12655         this.footer.dataSource = this.store
12656         this.footer.container = fctr;
12657         this.footer = Roo.factory(this.footer, Roo);
12658         fctr.insertFirst(this.el);
12659         
12660         // this is a bit insane - as the paging toolbar seems to detach the el..
12661 //        dom.parentNode.parentNode.parentNode
12662          // they get detached?
12663     }
12664     
12665     
12666     Roo.View.superclass.constructor.call(this);
12667     
12668     
12669 };
12670
12671 Roo.extend(Roo.View, Roo.util.Observable, {
12672     
12673      /**
12674      * @cfg {Roo.data.Store} store Data store to load data from.
12675      */
12676     store : false,
12677     
12678     /**
12679      * @cfg {String|Roo.Element} el The container element.
12680      */
12681     el : '',
12682     
12683     /**
12684      * @cfg {String|Roo.Template} tpl The template used by this View 
12685      */
12686     tpl : false,
12687     /**
12688      * @cfg {String} dataName the named area of the template to use as the data area
12689      *                          Works with domtemplates roo-name="name"
12690      */
12691     dataName: false,
12692     /**
12693      * @cfg {String} selectedClass The css class to add to selected nodes
12694      */
12695     selectedClass : "x-view-selected",
12696      /**
12697      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12698      */
12699     emptyText : "",
12700     
12701     /**
12702      * @cfg {String} text to display on mask (default Loading)
12703      */
12704     mask : false,
12705     /**
12706      * @cfg {Boolean} multiSelect Allow multiple selection
12707      */
12708     multiSelect : false,
12709     /**
12710      * @cfg {Boolean} singleSelect Allow single selection
12711      */
12712     singleSelect:  false,
12713     
12714     /**
12715      * @cfg {Boolean} toggleSelect - selecting 
12716      */
12717     toggleSelect : false,
12718     
12719     /**
12720      * @cfg {Boolean} tickable - selecting 
12721      */
12722     tickable : false,
12723     
12724     /**
12725      * Returns the element this view is bound to.
12726      * @return {Roo.Element}
12727      */
12728     getEl : function(){
12729         return this.wrapEl;
12730     },
12731     
12732     
12733
12734     /**
12735      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12736      */
12737     refresh : function(){
12738         //Roo.log('refresh');
12739         var t = this.tpl;
12740         
12741         // if we are using something like 'domtemplate', then
12742         // the what gets used is:
12743         // t.applySubtemplate(NAME, data, wrapping data..)
12744         // the outer template then get' applied with
12745         //     the store 'extra data'
12746         // and the body get's added to the
12747         //      roo-name="data" node?
12748         //      <span class='roo-tpl-{name}'></span> ?????
12749         
12750         
12751         
12752         this.clearSelections();
12753         this.el.update("");
12754         var html = [];
12755         var records = this.store.getRange();
12756         if(records.length < 1) {
12757             
12758             // is this valid??  = should it render a template??
12759             
12760             this.el.update(this.emptyText);
12761             return;
12762         }
12763         var el = this.el;
12764         if (this.dataName) {
12765             this.el.update(t.apply(this.store.meta)); //????
12766             el = this.el.child('.roo-tpl-' + this.dataName);
12767         }
12768         
12769         for(var i = 0, len = records.length; i < len; i++){
12770             var data = this.prepareData(records[i].data, i, records[i]);
12771             this.fireEvent("preparedata", this, data, i, records[i]);
12772             
12773             var d = Roo.apply({}, data);
12774             
12775             if(this.tickable){
12776                 Roo.apply(d, {'roo-id' : Roo.id()});
12777                 
12778                 var _this = this;
12779             
12780                 Roo.each(this.parent.item, function(item){
12781                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12782                         return;
12783                     }
12784                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12785                 });
12786             }
12787             
12788             html[html.length] = Roo.util.Format.trim(
12789                 this.dataName ?
12790                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12791                     t.apply(d)
12792             );
12793         }
12794         
12795         
12796         
12797         el.update(html.join(""));
12798         this.nodes = el.dom.childNodes;
12799         this.updateIndexes(0);
12800     },
12801     
12802
12803     /**
12804      * Function to override to reformat the data that is sent to
12805      * the template for each node.
12806      * DEPRICATED - use the preparedata event handler.
12807      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12808      * a JSON object for an UpdateManager bound view).
12809      */
12810     prepareData : function(data, index, record)
12811     {
12812         this.fireEvent("preparedata", this, data, index, record);
12813         return data;
12814     },
12815
12816     onUpdate : function(ds, record){
12817         // Roo.log('on update');   
12818         this.clearSelections();
12819         var index = this.store.indexOf(record);
12820         var n = this.nodes[index];
12821         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12822         n.parentNode.removeChild(n);
12823         this.updateIndexes(index, index);
12824     },
12825
12826     
12827     
12828 // --------- FIXME     
12829     onAdd : function(ds, records, index)
12830     {
12831         //Roo.log(['on Add', ds, records, index] );        
12832         this.clearSelections();
12833         if(this.nodes.length == 0){
12834             this.refresh();
12835             return;
12836         }
12837         var n = this.nodes[index];
12838         for(var i = 0, len = records.length; i < len; i++){
12839             var d = this.prepareData(records[i].data, i, records[i]);
12840             if(n){
12841                 this.tpl.insertBefore(n, d);
12842             }else{
12843                 
12844                 this.tpl.append(this.el, d);
12845             }
12846         }
12847         this.updateIndexes(index);
12848     },
12849
12850     onRemove : function(ds, record, index){
12851        // Roo.log('onRemove');
12852         this.clearSelections();
12853         var el = this.dataName  ?
12854             this.el.child('.roo-tpl-' + this.dataName) :
12855             this.el; 
12856         
12857         el.dom.removeChild(this.nodes[index]);
12858         this.updateIndexes(index);
12859     },
12860
12861     /**
12862      * Refresh an individual node.
12863      * @param {Number} index
12864      */
12865     refreshNode : function(index){
12866         this.onUpdate(this.store, this.store.getAt(index));
12867     },
12868
12869     updateIndexes : function(startIndex, endIndex){
12870         var ns = this.nodes;
12871         startIndex = startIndex || 0;
12872         endIndex = endIndex || ns.length - 1;
12873         for(var i = startIndex; i <= endIndex; i++){
12874             ns[i].nodeIndex = i;
12875         }
12876     },
12877
12878     /**
12879      * Changes the data store this view uses and refresh the view.
12880      * @param {Store} store
12881      */
12882     setStore : function(store, initial){
12883         if(!initial && this.store){
12884             this.store.un("datachanged", this.refresh);
12885             this.store.un("add", this.onAdd);
12886             this.store.un("remove", this.onRemove);
12887             this.store.un("update", this.onUpdate);
12888             this.store.un("clear", this.refresh);
12889             this.store.un("beforeload", this.onBeforeLoad);
12890             this.store.un("load", this.onLoad);
12891             this.store.un("loadexception", this.onLoad);
12892         }
12893         if(store){
12894           
12895             store.on("datachanged", this.refresh, this);
12896             store.on("add", this.onAdd, this);
12897             store.on("remove", this.onRemove, this);
12898             store.on("update", this.onUpdate, this);
12899             store.on("clear", this.refresh, this);
12900             store.on("beforeload", this.onBeforeLoad, this);
12901             store.on("load", this.onLoad, this);
12902             store.on("loadexception", this.onLoad, this);
12903         }
12904         
12905         if(store){
12906             this.refresh();
12907         }
12908     },
12909     /**
12910      * onbeforeLoad - masks the loading area.
12911      *
12912      */
12913     onBeforeLoad : function(store,opts)
12914     {
12915          //Roo.log('onBeforeLoad');   
12916         if (!opts.add) {
12917             this.el.update("");
12918         }
12919         this.el.mask(this.mask ? this.mask : "Loading" ); 
12920     },
12921     onLoad : function ()
12922     {
12923         this.el.unmask();
12924     },
12925     
12926
12927     /**
12928      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12929      * @param {HTMLElement} node
12930      * @return {HTMLElement} The template node
12931      */
12932     findItemFromChild : function(node){
12933         var el = this.dataName  ?
12934             this.el.child('.roo-tpl-' + this.dataName,true) :
12935             this.el.dom; 
12936         
12937         if(!node || node.parentNode == el){
12938                     return node;
12939             }
12940             var p = node.parentNode;
12941             while(p && p != el){
12942             if(p.parentNode == el){
12943                 return p;
12944             }
12945             p = p.parentNode;
12946         }
12947             return null;
12948     },
12949
12950     /** @ignore */
12951     onClick : function(e){
12952         var item = this.findItemFromChild(e.getTarget());
12953         if(item){
12954             var index = this.indexOf(item);
12955             if(this.onItemClick(item, index, e) !== false){
12956                 this.fireEvent("click", this, index, item, e);
12957             }
12958         }else{
12959             this.clearSelections();
12960         }
12961     },
12962
12963     /** @ignore */
12964     onContextMenu : function(e){
12965         var item = this.findItemFromChild(e.getTarget());
12966         if(item){
12967             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12968         }
12969     },
12970
12971     /** @ignore */
12972     onDblClick : function(e){
12973         var item = this.findItemFromChild(e.getTarget());
12974         if(item){
12975             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12976         }
12977     },
12978
12979     onItemClick : function(item, index, e)
12980     {
12981         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12982             return false;
12983         }
12984         if (this.toggleSelect) {
12985             var m = this.isSelected(item) ? 'unselect' : 'select';
12986             //Roo.log(m);
12987             var _t = this;
12988             _t[m](item, true, false);
12989             return true;
12990         }
12991         if(this.multiSelect || this.singleSelect){
12992             if(this.multiSelect && e.shiftKey && this.lastSelection){
12993                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12994             }else{
12995                 this.select(item, this.multiSelect && e.ctrlKey);
12996                 this.lastSelection = item;
12997             }
12998             
12999             if(!this.tickable){
13000                 e.preventDefault();
13001             }
13002             
13003         }
13004         return true;
13005     },
13006
13007     /**
13008      * Get the number of selected nodes.
13009      * @return {Number}
13010      */
13011     getSelectionCount : function(){
13012         return this.selections.length;
13013     },
13014
13015     /**
13016      * Get the currently selected nodes.
13017      * @return {Array} An array of HTMLElements
13018      */
13019     getSelectedNodes : function(){
13020         return this.selections;
13021     },
13022
13023     /**
13024      * Get the indexes of the selected nodes.
13025      * @return {Array}
13026      */
13027     getSelectedIndexes : function(){
13028         var indexes = [], s = this.selections;
13029         for(var i = 0, len = s.length; i < len; i++){
13030             indexes.push(s[i].nodeIndex);
13031         }
13032         return indexes;
13033     },
13034
13035     /**
13036      * Clear all selections
13037      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13038      */
13039     clearSelections : function(suppressEvent){
13040         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13041             this.cmp.elements = this.selections;
13042             this.cmp.removeClass(this.selectedClass);
13043             this.selections = [];
13044             if(!suppressEvent){
13045                 this.fireEvent("selectionchange", this, this.selections);
13046             }
13047         }
13048     },
13049
13050     /**
13051      * Returns true if the passed node is selected
13052      * @param {HTMLElement/Number} node The node or node index
13053      * @return {Boolean}
13054      */
13055     isSelected : function(node){
13056         var s = this.selections;
13057         if(s.length < 1){
13058             return false;
13059         }
13060         node = this.getNode(node);
13061         return s.indexOf(node) !== -1;
13062     },
13063
13064     /**
13065      * Selects nodes.
13066      * @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
13067      * @param {Boolean} keepExisting (optional) true to keep existing selections
13068      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13069      */
13070     select : function(nodeInfo, keepExisting, suppressEvent){
13071         if(nodeInfo instanceof Array){
13072             if(!keepExisting){
13073                 this.clearSelections(true);
13074             }
13075             for(var i = 0, len = nodeInfo.length; i < len; i++){
13076                 this.select(nodeInfo[i], true, true);
13077             }
13078             return;
13079         } 
13080         var node = this.getNode(nodeInfo);
13081         if(!node || this.isSelected(node)){
13082             return; // already selected.
13083         }
13084         if(!keepExisting){
13085             this.clearSelections(true);
13086         }
13087         
13088         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13089             Roo.fly(node).addClass(this.selectedClass);
13090             this.selections.push(node);
13091             if(!suppressEvent){
13092                 this.fireEvent("selectionchange", this, this.selections);
13093             }
13094         }
13095         
13096         
13097     },
13098       /**
13099      * Unselects nodes.
13100      * @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
13101      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13102      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13103      */
13104     unselect : function(nodeInfo, keepExisting, suppressEvent)
13105     {
13106         if(nodeInfo instanceof Array){
13107             Roo.each(this.selections, function(s) {
13108                 this.unselect(s, nodeInfo);
13109             }, this);
13110             return;
13111         }
13112         var node = this.getNode(nodeInfo);
13113         if(!node || !this.isSelected(node)){
13114             //Roo.log("not selected");
13115             return; // not selected.
13116         }
13117         // fireevent???
13118         var ns = [];
13119         Roo.each(this.selections, function(s) {
13120             if (s == node ) {
13121                 Roo.fly(node).removeClass(this.selectedClass);
13122
13123                 return;
13124             }
13125             ns.push(s);
13126         },this);
13127         
13128         this.selections= ns;
13129         this.fireEvent("selectionchange", this, this.selections);
13130     },
13131
13132     /**
13133      * Gets a template node.
13134      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13135      * @return {HTMLElement} The node or null if it wasn't found
13136      */
13137     getNode : function(nodeInfo){
13138         if(typeof nodeInfo == "string"){
13139             return document.getElementById(nodeInfo);
13140         }else if(typeof nodeInfo == "number"){
13141             return this.nodes[nodeInfo];
13142         }
13143         return nodeInfo;
13144     },
13145
13146     /**
13147      * Gets a range template nodes.
13148      * @param {Number} startIndex
13149      * @param {Number} endIndex
13150      * @return {Array} An array of nodes
13151      */
13152     getNodes : function(start, end){
13153         var ns = this.nodes;
13154         start = start || 0;
13155         end = typeof end == "undefined" ? ns.length - 1 : end;
13156         var nodes = [];
13157         if(start <= end){
13158             for(var i = start; i <= end; i++){
13159                 nodes.push(ns[i]);
13160             }
13161         } else{
13162             for(var i = start; i >= end; i--){
13163                 nodes.push(ns[i]);
13164             }
13165         }
13166         return nodes;
13167     },
13168
13169     /**
13170      * Finds the index of the passed node
13171      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13172      * @return {Number} The index of the node or -1
13173      */
13174     indexOf : function(node){
13175         node = this.getNode(node);
13176         if(typeof node.nodeIndex == "number"){
13177             return node.nodeIndex;
13178         }
13179         var ns = this.nodes;
13180         for(var i = 0, len = ns.length; i < len; i++){
13181             if(ns[i] == node){
13182                 return i;
13183             }
13184         }
13185         return -1;
13186     }
13187 });
13188 /*
13189  * - LGPL
13190  *
13191  * based on jquery fullcalendar
13192  * 
13193  */
13194
13195 Roo.bootstrap = Roo.bootstrap || {};
13196 /**
13197  * @class Roo.bootstrap.Calendar
13198  * @extends Roo.bootstrap.Component
13199  * Bootstrap Calendar class
13200  * @cfg {Boolean} loadMask (true|false) default false
13201  * @cfg {Object} header generate the user specific header of the calendar, default false
13202
13203  * @constructor
13204  * Create a new Container
13205  * @param {Object} config The config object
13206  */
13207
13208
13209
13210 Roo.bootstrap.Calendar = function(config){
13211     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13212      this.addEvents({
13213         /**
13214              * @event select
13215              * Fires when a date is selected
13216              * @param {DatePicker} this
13217              * @param {Date} date The selected date
13218              */
13219         'select': true,
13220         /**
13221              * @event monthchange
13222              * Fires when the displayed month changes 
13223              * @param {DatePicker} this
13224              * @param {Date} date The selected month
13225              */
13226         'monthchange': true,
13227         /**
13228              * @event evententer
13229              * Fires when mouse over an event
13230              * @param {Calendar} this
13231              * @param {event} Event
13232              */
13233         'evententer': true,
13234         /**
13235              * @event eventleave
13236              * Fires when the mouse leaves an
13237              * @param {Calendar} this
13238              * @param {event}
13239              */
13240         'eventleave': true,
13241         /**
13242              * @event eventclick
13243              * Fires when the mouse click an
13244              * @param {Calendar} this
13245              * @param {event}
13246              */
13247         'eventclick': true
13248         
13249     });
13250
13251 };
13252
13253 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13254     
13255      /**
13256      * @cfg {Number} startDay
13257      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13258      */
13259     startDay : 0,
13260     
13261     loadMask : false,
13262     
13263     header : false,
13264       
13265     getAutoCreate : function(){
13266         
13267         
13268         var fc_button = function(name, corner, style, content ) {
13269             return Roo.apply({},{
13270                 tag : 'span',
13271                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13272                          (corner.length ?
13273                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13274                             ''
13275                         ),
13276                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13277                 unselectable: 'on'
13278             });
13279         };
13280         
13281         var header = {};
13282         
13283         if(!this.header){
13284             header = {
13285                 tag : 'table',
13286                 cls : 'fc-header',
13287                 style : 'width:100%',
13288                 cn : [
13289                     {
13290                         tag: 'tr',
13291                         cn : [
13292                             {
13293                                 tag : 'td',
13294                                 cls : 'fc-header-left',
13295                                 cn : [
13296                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13297                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13298                                     { tag: 'span', cls: 'fc-header-space' },
13299                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13300
13301
13302                                 ]
13303                             },
13304
13305                             {
13306                                 tag : 'td',
13307                                 cls : 'fc-header-center',
13308                                 cn : [
13309                                     {
13310                                         tag: 'span',
13311                                         cls: 'fc-header-title',
13312                                         cn : {
13313                                             tag: 'H2',
13314                                             html : 'month / year'
13315                                         }
13316                                     }
13317
13318                                 ]
13319                             },
13320                             {
13321                                 tag : 'td',
13322                                 cls : 'fc-header-right',
13323                                 cn : [
13324                               /*      fc_button('month', 'left', '', 'month' ),
13325                                     fc_button('week', '', '', 'week' ),
13326                                     fc_button('day', 'right', '', 'day' )
13327                                 */    
13328
13329                                 ]
13330                             }
13331
13332                         ]
13333                     }
13334                 ]
13335             };
13336         }
13337         
13338         header = this.header;
13339         
13340        
13341         var cal_heads = function() {
13342             var ret = [];
13343             // fixme - handle this.
13344             
13345             for (var i =0; i < Date.dayNames.length; i++) {
13346                 var d = Date.dayNames[i];
13347                 ret.push({
13348                     tag: 'th',
13349                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13350                     html : d.substring(0,3)
13351                 });
13352                 
13353             }
13354             ret[0].cls += ' fc-first';
13355             ret[6].cls += ' fc-last';
13356             return ret;
13357         };
13358         var cal_cell = function(n) {
13359             return  {
13360                 tag: 'td',
13361                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13362                 cn : [
13363                     {
13364                         cn : [
13365                             {
13366                                 cls: 'fc-day-number',
13367                                 html: 'D'
13368                             },
13369                             {
13370                                 cls: 'fc-day-content',
13371                              
13372                                 cn : [
13373                                      {
13374                                         style: 'position: relative;' // height: 17px;
13375                                     }
13376                                 ]
13377                             }
13378                             
13379                             
13380                         ]
13381                     }
13382                 ]
13383                 
13384             }
13385         };
13386         var cal_rows = function() {
13387             
13388             var ret = [];
13389             for (var r = 0; r < 6; r++) {
13390                 var row= {
13391                     tag : 'tr',
13392                     cls : 'fc-week',
13393                     cn : []
13394                 };
13395                 
13396                 for (var i =0; i < Date.dayNames.length; i++) {
13397                     var d = Date.dayNames[i];
13398                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13399
13400                 }
13401                 row.cn[0].cls+=' fc-first';
13402                 row.cn[0].cn[0].style = 'min-height:90px';
13403                 row.cn[6].cls+=' fc-last';
13404                 ret.push(row);
13405                 
13406             }
13407             ret[0].cls += ' fc-first';
13408             ret[4].cls += ' fc-prev-last';
13409             ret[5].cls += ' fc-last';
13410             return ret;
13411             
13412         };
13413         
13414         var cal_table = {
13415             tag: 'table',
13416             cls: 'fc-border-separate',
13417             style : 'width:100%',
13418             cellspacing  : 0,
13419             cn : [
13420                 { 
13421                     tag: 'thead',
13422                     cn : [
13423                         { 
13424                             tag: 'tr',
13425                             cls : 'fc-first fc-last',
13426                             cn : cal_heads()
13427                         }
13428                     ]
13429                 },
13430                 { 
13431                     tag: 'tbody',
13432                     cn : cal_rows()
13433                 }
13434                   
13435             ]
13436         };
13437          
13438          var cfg = {
13439             cls : 'fc fc-ltr',
13440             cn : [
13441                 header,
13442                 {
13443                     cls : 'fc-content',
13444                     style : "position: relative;",
13445                     cn : [
13446                         {
13447                             cls : 'fc-view fc-view-month fc-grid',
13448                             style : 'position: relative',
13449                             unselectable : 'on',
13450                             cn : [
13451                                 {
13452                                     cls : 'fc-event-container',
13453                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13454                                 },
13455                                 cal_table
13456                             ]
13457                         }
13458                     ]
13459     
13460                 }
13461            ] 
13462             
13463         };
13464         
13465          
13466         
13467         return cfg;
13468     },
13469     
13470     
13471     initEvents : function()
13472     {
13473         if(!this.store){
13474             throw "can not find store for calendar";
13475         }
13476         
13477         var mark = {
13478             tag: "div",
13479             cls:"x-dlg-mask",
13480             style: "text-align:center",
13481             cn: [
13482                 {
13483                     tag: "div",
13484                     style: "background-color:white;width:50%;margin:250 auto",
13485                     cn: [
13486                         {
13487                             tag: "img",
13488                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13489                         },
13490                         {
13491                             tag: "span",
13492                             html: "Loading"
13493                         }
13494                         
13495                     ]
13496                 }
13497             ]
13498         }
13499         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13500         
13501         var size = this.el.select('.fc-content', true).first().getSize();
13502         this.maskEl.setSize(size.width, size.height);
13503         this.maskEl.enableDisplayMode("block");
13504         if(!this.loadMask){
13505             this.maskEl.hide();
13506         }
13507         
13508         this.store = Roo.factory(this.store, Roo.data);
13509         this.store.on('load', this.onLoad, this);
13510         this.store.on('beforeload', this.onBeforeLoad, this);
13511         
13512         this.resize();
13513         
13514         this.cells = this.el.select('.fc-day',true);
13515         //Roo.log(this.cells);
13516         this.textNodes = this.el.query('.fc-day-number');
13517         this.cells.addClassOnOver('fc-state-hover');
13518         
13519         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13520         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13521         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13522         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13523         
13524         this.on('monthchange', this.onMonthChange, this);
13525         
13526         this.update(new Date().clearTime());
13527     },
13528     
13529     resize : function() {
13530         var sz  = this.el.getSize();
13531         
13532         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13533         this.el.select('.fc-day-content div',true).setHeight(34);
13534     },
13535     
13536     
13537     // private
13538     showPrevMonth : function(e){
13539         this.update(this.activeDate.add("mo", -1));
13540     },
13541     showToday : function(e){
13542         this.update(new Date().clearTime());
13543     },
13544     // private
13545     showNextMonth : function(e){
13546         this.update(this.activeDate.add("mo", 1));
13547     },
13548
13549     // private
13550     showPrevYear : function(){
13551         this.update(this.activeDate.add("y", -1));
13552     },
13553
13554     // private
13555     showNextYear : function(){
13556         this.update(this.activeDate.add("y", 1));
13557     },
13558
13559     
13560    // private
13561     update : function(date)
13562     {
13563         var vd = this.activeDate;
13564         this.activeDate = date;
13565 //        if(vd && this.el){
13566 //            var t = date.getTime();
13567 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13568 //                Roo.log('using add remove');
13569 //                
13570 //                this.fireEvent('monthchange', this, date);
13571 //                
13572 //                this.cells.removeClass("fc-state-highlight");
13573 //                this.cells.each(function(c){
13574 //                   if(c.dateValue == t){
13575 //                       c.addClass("fc-state-highlight");
13576 //                       setTimeout(function(){
13577 //                            try{c.dom.firstChild.focus();}catch(e){}
13578 //                       }, 50);
13579 //                       return false;
13580 //                   }
13581 //                   return true;
13582 //                });
13583 //                return;
13584 //            }
13585 //        }
13586         
13587         var days = date.getDaysInMonth();
13588         
13589         var firstOfMonth = date.getFirstDateOfMonth();
13590         var startingPos = firstOfMonth.getDay()-this.startDay;
13591         
13592         if(startingPos < this.startDay){
13593             startingPos += 7;
13594         }
13595         
13596         var pm = date.add(Date.MONTH, -1);
13597         var prevStart = pm.getDaysInMonth()-startingPos;
13598 //        
13599         this.cells = this.el.select('.fc-day',true);
13600         this.textNodes = this.el.query('.fc-day-number');
13601         this.cells.addClassOnOver('fc-state-hover');
13602         
13603         var cells = this.cells.elements;
13604         var textEls = this.textNodes;
13605         
13606         Roo.each(cells, function(cell){
13607             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13608         });
13609         
13610         days += startingPos;
13611
13612         // convert everything to numbers so it's fast
13613         var day = 86400000;
13614         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13615         //Roo.log(d);
13616         //Roo.log(pm);
13617         //Roo.log(prevStart);
13618         
13619         var today = new Date().clearTime().getTime();
13620         var sel = date.clearTime().getTime();
13621         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13622         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13623         var ddMatch = this.disabledDatesRE;
13624         var ddText = this.disabledDatesText;
13625         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13626         var ddaysText = this.disabledDaysText;
13627         var format = this.format;
13628         
13629         var setCellClass = function(cal, cell){
13630             cell.row = 0;
13631             cell.events = [];
13632             cell.more = [];
13633             //Roo.log('set Cell Class');
13634             cell.title = "";
13635             var t = d.getTime();
13636             
13637             //Roo.log(d);
13638             
13639             cell.dateValue = t;
13640             if(t == today){
13641                 cell.className += " fc-today";
13642                 cell.className += " fc-state-highlight";
13643                 cell.title = cal.todayText;
13644             }
13645             if(t == sel){
13646                 // disable highlight in other month..
13647                 //cell.className += " fc-state-highlight";
13648                 
13649             }
13650             // disabling
13651             if(t < min) {
13652                 cell.className = " fc-state-disabled";
13653                 cell.title = cal.minText;
13654                 return;
13655             }
13656             if(t > max) {
13657                 cell.className = " fc-state-disabled";
13658                 cell.title = cal.maxText;
13659                 return;
13660             }
13661             if(ddays){
13662                 if(ddays.indexOf(d.getDay()) != -1){
13663                     cell.title = ddaysText;
13664                     cell.className = " fc-state-disabled";
13665                 }
13666             }
13667             if(ddMatch && format){
13668                 var fvalue = d.dateFormat(format);
13669                 if(ddMatch.test(fvalue)){
13670                     cell.title = ddText.replace("%0", fvalue);
13671                     cell.className = " fc-state-disabled";
13672                 }
13673             }
13674             
13675             if (!cell.initialClassName) {
13676                 cell.initialClassName = cell.dom.className;
13677             }
13678             
13679             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13680         };
13681
13682         var i = 0;
13683         
13684         for(; i < startingPos; i++) {
13685             textEls[i].innerHTML = (++prevStart);
13686             d.setDate(d.getDate()+1);
13687             
13688             cells[i].className = "fc-past fc-other-month";
13689             setCellClass(this, cells[i]);
13690         }
13691         
13692         var intDay = 0;
13693         
13694         for(; i < days; i++){
13695             intDay = i - startingPos + 1;
13696             textEls[i].innerHTML = (intDay);
13697             d.setDate(d.getDate()+1);
13698             
13699             cells[i].className = ''; // "x-date-active";
13700             setCellClass(this, cells[i]);
13701         }
13702         var extraDays = 0;
13703         
13704         for(; i < 42; i++) {
13705             textEls[i].innerHTML = (++extraDays);
13706             d.setDate(d.getDate()+1);
13707             
13708             cells[i].className = "fc-future fc-other-month";
13709             setCellClass(this, cells[i]);
13710         }
13711         
13712         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13713         
13714         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13715         
13716         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13717         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13718         
13719         if(totalRows != 6){
13720             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13721             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13722         }
13723         
13724         this.fireEvent('monthchange', this, date);
13725         
13726         
13727         /*
13728         if(!this.internalRender){
13729             var main = this.el.dom.firstChild;
13730             var w = main.offsetWidth;
13731             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13732             Roo.fly(main).setWidth(w);
13733             this.internalRender = true;
13734             // opera does not respect the auto grow header center column
13735             // then, after it gets a width opera refuses to recalculate
13736             // without a second pass
13737             if(Roo.isOpera && !this.secondPass){
13738                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13739                 this.secondPass = true;
13740                 this.update.defer(10, this, [date]);
13741             }
13742         }
13743         */
13744         
13745     },
13746     
13747     findCell : function(dt) {
13748         dt = dt.clearTime().getTime();
13749         var ret = false;
13750         this.cells.each(function(c){
13751             //Roo.log("check " +c.dateValue + '?=' + dt);
13752             if(c.dateValue == dt){
13753                 ret = c;
13754                 return false;
13755             }
13756             return true;
13757         });
13758         
13759         return ret;
13760     },
13761     
13762     findCells : function(ev) {
13763         var s = ev.start.clone().clearTime().getTime();
13764        // Roo.log(s);
13765         var e= ev.end.clone().clearTime().getTime();
13766        // Roo.log(e);
13767         var ret = [];
13768         this.cells.each(function(c){
13769              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13770             
13771             if(c.dateValue > e){
13772                 return ;
13773             }
13774             if(c.dateValue < s){
13775                 return ;
13776             }
13777             ret.push(c);
13778         });
13779         
13780         return ret;    
13781     },
13782     
13783 //    findBestRow: function(cells)
13784 //    {
13785 //        var ret = 0;
13786 //        
13787 //        for (var i =0 ; i < cells.length;i++) {
13788 //            ret  = Math.max(cells[i].rows || 0,ret);
13789 //        }
13790 //        return ret;
13791 //        
13792 //    },
13793     
13794     
13795     addItem : function(ev)
13796     {
13797         // look for vertical location slot in
13798         var cells = this.findCells(ev);
13799         
13800 //        ev.row = this.findBestRow(cells);
13801         
13802         // work out the location.
13803         
13804         var crow = false;
13805         var rows = [];
13806         for(var i =0; i < cells.length; i++) {
13807             
13808             cells[i].row = cells[0].row;
13809             
13810             if(i == 0){
13811                 cells[i].row = cells[i].row + 1;
13812             }
13813             
13814             if (!crow) {
13815                 crow = {
13816                     start : cells[i],
13817                     end :  cells[i]
13818                 };
13819                 continue;
13820             }
13821             if (crow.start.getY() == cells[i].getY()) {
13822                 // on same row.
13823                 crow.end = cells[i];
13824                 continue;
13825             }
13826             // different row.
13827             rows.push(crow);
13828             crow = {
13829                 start: cells[i],
13830                 end : cells[i]
13831             };
13832             
13833         }
13834         
13835         rows.push(crow);
13836         ev.els = [];
13837         ev.rows = rows;
13838         ev.cells = cells;
13839         
13840         cells[0].events.push(ev);
13841         
13842         this.calevents.push(ev);
13843     },
13844     
13845     clearEvents: function() {
13846         
13847         if(!this.calevents){
13848             return;
13849         }
13850         
13851         Roo.each(this.cells.elements, function(c){
13852             c.row = 0;
13853             c.events = [];
13854             c.more = [];
13855         });
13856         
13857         Roo.each(this.calevents, function(e) {
13858             Roo.each(e.els, function(el) {
13859                 el.un('mouseenter' ,this.onEventEnter, this);
13860                 el.un('mouseleave' ,this.onEventLeave, this);
13861                 el.remove();
13862             },this);
13863         },this);
13864         
13865         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13866             e.remove();
13867         });
13868         
13869     },
13870     
13871     renderEvents: function()
13872     {   
13873         var _this = this;
13874         
13875         this.cells.each(function(c) {
13876             
13877             if(c.row < 5){
13878                 return;
13879             }
13880             
13881             var ev = c.events;
13882             
13883             var r = 4;
13884             if(c.row != c.events.length){
13885                 r = 4 - (4 - (c.row - c.events.length));
13886             }
13887             
13888             c.events = ev.slice(0, r);
13889             c.more = ev.slice(r);
13890             
13891             if(c.more.length && c.more.length == 1){
13892                 c.events.push(c.more.pop());
13893             }
13894             
13895             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13896             
13897         });
13898             
13899         this.cells.each(function(c) {
13900             
13901             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13902             
13903             
13904             for (var e = 0; e < c.events.length; e++){
13905                 var ev = c.events[e];
13906                 var rows = ev.rows;
13907                 
13908                 for(var i = 0; i < rows.length; i++) {
13909                 
13910                     // how many rows should it span..
13911
13912                     var  cfg = {
13913                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13914                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13915
13916                         unselectable : "on",
13917                         cn : [
13918                             {
13919                                 cls: 'fc-event-inner',
13920                                 cn : [
13921     //                                {
13922     //                                  tag:'span',
13923     //                                  cls: 'fc-event-time',
13924     //                                  html : cells.length > 1 ? '' : ev.time
13925     //                                },
13926                                     {
13927                                       tag:'span',
13928                                       cls: 'fc-event-title',
13929                                       html : String.format('{0}', ev.title)
13930                                     }
13931
13932
13933                                 ]
13934                             },
13935                             {
13936                                 cls: 'ui-resizable-handle ui-resizable-e',
13937                                 html : '&nbsp;&nbsp;&nbsp'
13938                             }
13939
13940                         ]
13941                     };
13942
13943                     if (i == 0) {
13944                         cfg.cls += ' fc-event-start';
13945                     }
13946                     if ((i+1) == rows.length) {
13947                         cfg.cls += ' fc-event-end';
13948                     }
13949
13950                     var ctr = _this.el.select('.fc-event-container',true).first();
13951                     var cg = ctr.createChild(cfg);
13952
13953                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13954                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13955
13956                     var r = (c.more.length) ? 1 : 0;
13957                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13958                     cg.setWidth(ebox.right - sbox.x -2);
13959
13960                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13961                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13962                     cg.on('click', _this.onEventClick, _this, ev);
13963
13964                     ev.els.push(cg);
13965                     
13966                 }
13967                 
13968             }
13969             
13970             
13971             if(c.more.length){
13972                 var  cfg = {
13973                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13974                     style : 'position: absolute',
13975                     unselectable : "on",
13976                     cn : [
13977                         {
13978                             cls: 'fc-event-inner',
13979                             cn : [
13980                                 {
13981                                   tag:'span',
13982                                   cls: 'fc-event-title',
13983                                   html : 'More'
13984                                 }
13985
13986
13987                             ]
13988                         },
13989                         {
13990                             cls: 'ui-resizable-handle ui-resizable-e',
13991                             html : '&nbsp;&nbsp;&nbsp'
13992                         }
13993
13994                     ]
13995                 };
13996
13997                 var ctr = _this.el.select('.fc-event-container',true).first();
13998                 var cg = ctr.createChild(cfg);
13999
14000                 var sbox = c.select('.fc-day-content',true).first().getBox();
14001                 var ebox = c.select('.fc-day-content',true).first().getBox();
14002                 //Roo.log(cg);
14003                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14004                 cg.setWidth(ebox.right - sbox.x -2);
14005
14006                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14007                 
14008             }
14009             
14010         });
14011         
14012         
14013         
14014     },
14015     
14016     onEventEnter: function (e, el,event,d) {
14017         this.fireEvent('evententer', this, el, event);
14018     },
14019     
14020     onEventLeave: function (e, el,event,d) {
14021         this.fireEvent('eventleave', this, el, event);
14022     },
14023     
14024     onEventClick: function (e, el,event,d) {
14025         this.fireEvent('eventclick', this, el, event);
14026     },
14027     
14028     onMonthChange: function () {
14029         this.store.load();
14030     },
14031     
14032     onMoreEventClick: function(e, el, more)
14033     {
14034         var _this = this;
14035         
14036         this.calpopover.placement = 'right';
14037         this.calpopover.setTitle('More');
14038         
14039         this.calpopover.setContent('');
14040         
14041         var ctr = this.calpopover.el.select('.popover-content', true).first();
14042         
14043         Roo.each(more, function(m){
14044             var cfg = {
14045                 cls : 'fc-event-hori fc-event-draggable',
14046                 html : m.title
14047             }
14048             var cg = ctr.createChild(cfg);
14049             
14050             cg.on('click', _this.onEventClick, _this, m);
14051         });
14052         
14053         this.calpopover.show(el);
14054         
14055         
14056     },
14057     
14058     onLoad: function () 
14059     {   
14060         this.calevents = [];
14061         var cal = this;
14062         
14063         if(this.store.getCount() > 0){
14064             this.store.data.each(function(d){
14065                cal.addItem({
14066                     id : d.data.id,
14067                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14068                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14069                     time : d.data.start_time,
14070                     title : d.data.title,
14071                     description : d.data.description,
14072                     venue : d.data.venue
14073                 });
14074             });
14075         }
14076         
14077         this.renderEvents();
14078         
14079         if(this.calevents.length && this.loadMask){
14080             this.maskEl.hide();
14081         }
14082     },
14083     
14084     onBeforeLoad: function()
14085     {
14086         this.clearEvents();
14087         if(this.loadMask){
14088             this.maskEl.show();
14089         }
14090     }
14091 });
14092
14093  
14094  /*
14095  * - LGPL
14096  *
14097  * element
14098  * 
14099  */
14100
14101 /**
14102  * @class Roo.bootstrap.Popover
14103  * @extends Roo.bootstrap.Component
14104  * Bootstrap Popover class
14105  * @cfg {String} html contents of the popover   (or false to use children..)
14106  * @cfg {String} title of popover (or false to hide)
14107  * @cfg {String} placement how it is placed
14108  * @cfg {String} trigger click || hover (or false to trigger manually)
14109  * @cfg {String} over what (parent or false to trigger manually.)
14110  * @cfg {Number} delay - delay before showing
14111  
14112  * @constructor
14113  * Create a new Popover
14114  * @param {Object} config The config object
14115  */
14116
14117 Roo.bootstrap.Popover = function(config){
14118     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14119 };
14120
14121 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14122     
14123     title: 'Fill in a title',
14124     html: false,
14125     
14126     placement : 'right',
14127     trigger : 'hover', // hover
14128     
14129     delay : 0,
14130     
14131     over: 'parent',
14132     
14133     can_build_overlaid : false,
14134     
14135     getChildContainer : function()
14136     {
14137         return this.el.select('.popover-content',true).first();
14138     },
14139     
14140     getAutoCreate : function(){
14141          Roo.log('make popover?');
14142         var cfg = {
14143            cls : 'popover roo-dynamic',
14144            style: 'display:block',
14145            cn : [
14146                 {
14147                     cls : 'arrow'
14148                 },
14149                 {
14150                     cls : 'popover-inner',
14151                     cn : [
14152                         {
14153                             tag: 'h3',
14154                             cls: 'popover-title',
14155                             html : this.title
14156                         },
14157                         {
14158                             cls : 'popover-content',
14159                             html : this.html
14160                         }
14161                     ]
14162                     
14163                 }
14164            ]
14165         };
14166         
14167         return cfg;
14168     },
14169     setTitle: function(str)
14170     {
14171         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14172     },
14173     setContent: function(str)
14174     {
14175         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14176     },
14177     // as it get's added to the bottom of the page.
14178     onRender : function(ct, position)
14179     {
14180         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14181         if(!this.el){
14182             var cfg = Roo.apply({},  this.getAutoCreate());
14183             cfg.id = Roo.id();
14184             
14185             if (this.cls) {
14186                 cfg.cls += ' ' + this.cls;
14187             }
14188             if (this.style) {
14189                 cfg.style = this.style;
14190             }
14191             Roo.log("adding to ")
14192             this.el = Roo.get(document.body).createChild(cfg, position);
14193             Roo.log(this.el);
14194         }
14195         this.initEvents();
14196     },
14197     
14198     initEvents : function()
14199     {
14200         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14201         this.el.enableDisplayMode('block');
14202         this.el.hide();
14203         if (this.over === false) {
14204             return; 
14205         }
14206         if (this.triggers === false) {
14207             return;
14208         }
14209         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14210         var triggers = this.trigger ? this.trigger.split(' ') : [];
14211         Roo.each(triggers, function(trigger) {
14212         
14213             if (trigger == 'click') {
14214                 on_el.on('click', this.toggle, this);
14215             } else if (trigger != 'manual') {
14216                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14217                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14218       
14219                 on_el.on(eventIn  ,this.enter, this);
14220                 on_el.on(eventOut, this.leave, this);
14221             }
14222         }, this);
14223         
14224     },
14225     
14226     
14227     // private
14228     timeout : null,
14229     hoverState : null,
14230     
14231     toggle : function () {
14232         this.hoverState == 'in' ? this.leave() : this.enter();
14233     },
14234     
14235     enter : function () {
14236        
14237     
14238         clearTimeout(this.timeout);
14239     
14240         this.hoverState = 'in';
14241     
14242         if (!this.delay || !this.delay.show) {
14243             this.show();
14244             return;
14245         }
14246         var _t = this;
14247         this.timeout = setTimeout(function () {
14248             if (_t.hoverState == 'in') {
14249                 _t.show();
14250             }
14251         }, this.delay.show)
14252     },
14253     leave : function() {
14254         clearTimeout(this.timeout);
14255     
14256         this.hoverState = 'out';
14257     
14258         if (!this.delay || !this.delay.hide) {
14259             this.hide();
14260             return;
14261         }
14262         var _t = this;
14263         this.timeout = setTimeout(function () {
14264             if (_t.hoverState == 'out') {
14265                 _t.hide();
14266             }
14267         }, this.delay.hide)
14268     },
14269     
14270     show : function (on_el)
14271     {
14272         if (!on_el) {
14273             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14274         }
14275         // set content.
14276         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14277         if (this.html !== false) {
14278             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14279         }
14280         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14281         if (!this.title.length) {
14282             this.el.select('.popover-title',true).hide();
14283         }
14284         
14285         var placement = typeof this.placement == 'function' ?
14286             this.placement.call(this, this.el, on_el) :
14287             this.placement;
14288             
14289         var autoToken = /\s?auto?\s?/i;
14290         var autoPlace = autoToken.test(placement);
14291         if (autoPlace) {
14292             placement = placement.replace(autoToken, '') || 'top';
14293         }
14294         
14295         //this.el.detach()
14296         //this.el.setXY([0,0]);
14297         this.el.show();
14298         this.el.dom.style.display='block';
14299         this.el.addClass(placement);
14300         
14301         //this.el.appendTo(on_el);
14302         
14303         var p = this.getPosition();
14304         var box = this.el.getBox();
14305         
14306         if (autoPlace) {
14307             // fixme..
14308         }
14309         var align = Roo.bootstrap.Popover.alignment[placement];
14310         this.el.alignTo(on_el, align[0],align[1]);
14311         //var arrow = this.el.select('.arrow',true).first();
14312         //arrow.set(align[2], 
14313         
14314         this.el.addClass('in');
14315         this.hoverState = null;
14316         
14317         if (this.el.hasClass('fade')) {
14318             // fade it?
14319         }
14320         
14321     },
14322     hide : function()
14323     {
14324         this.el.setXY([0,0]);
14325         this.el.removeClass('in');
14326         this.el.hide();
14327         
14328     }
14329     
14330 });
14331
14332 Roo.bootstrap.Popover.alignment = {
14333     'left' : ['r-l', [-10,0], 'right'],
14334     'right' : ['l-r', [10,0], 'left'],
14335     'bottom' : ['t-b', [0,10], 'top'],
14336     'top' : [ 'b-t', [0,-10], 'bottom']
14337 };
14338
14339  /*
14340  * - LGPL
14341  *
14342  * Progress
14343  * 
14344  */
14345
14346 /**
14347  * @class Roo.bootstrap.Progress
14348  * @extends Roo.bootstrap.Component
14349  * Bootstrap Progress class
14350  * @cfg {Boolean} striped striped of the progress bar
14351  * @cfg {Boolean} active animated of the progress bar
14352  * 
14353  * 
14354  * @constructor
14355  * Create a new Progress
14356  * @param {Object} config The config object
14357  */
14358
14359 Roo.bootstrap.Progress = function(config){
14360     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14361 };
14362
14363 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14364     
14365     striped : false,
14366     active: false,
14367     
14368     getAutoCreate : function(){
14369         var cfg = {
14370             tag: 'div',
14371             cls: 'progress'
14372         };
14373         
14374         
14375         if(this.striped){
14376             cfg.cls += ' progress-striped';
14377         }
14378       
14379         if(this.active){
14380             cfg.cls += ' active';
14381         }
14382         
14383         
14384         return cfg;
14385     }
14386    
14387 });
14388
14389  
14390
14391  /*
14392  * - LGPL
14393  *
14394  * ProgressBar
14395  * 
14396  */
14397
14398 /**
14399  * @class Roo.bootstrap.ProgressBar
14400  * @extends Roo.bootstrap.Component
14401  * Bootstrap ProgressBar class
14402  * @cfg {Number} aria_valuenow aria-value now
14403  * @cfg {Number} aria_valuemin aria-value min
14404  * @cfg {Number} aria_valuemax aria-value max
14405  * @cfg {String} label label for the progress bar
14406  * @cfg {String} panel (success | info | warning | danger )
14407  * @cfg {String} role role of the progress bar
14408  * @cfg {String} sr_only text
14409  * 
14410  * 
14411  * @constructor
14412  * Create a new ProgressBar
14413  * @param {Object} config The config object
14414  */
14415
14416 Roo.bootstrap.ProgressBar = function(config){
14417     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14418 };
14419
14420 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14421     
14422     aria_valuenow : 0,
14423     aria_valuemin : 0,
14424     aria_valuemax : 100,
14425     label : false,
14426     panel : false,
14427     role : false,
14428     sr_only: false,
14429     
14430     getAutoCreate : function()
14431     {
14432         
14433         var cfg = {
14434             tag: 'div',
14435             cls: 'progress-bar',
14436             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14437         };
14438         
14439         if(this.sr_only){
14440             cfg.cn = {
14441                 tag: 'span',
14442                 cls: 'sr-only',
14443                 html: this.sr_only
14444             }
14445         }
14446         
14447         if(this.role){
14448             cfg.role = this.role;
14449         }
14450         
14451         if(this.aria_valuenow){
14452             cfg['aria-valuenow'] = this.aria_valuenow;
14453         }
14454         
14455         if(this.aria_valuemin){
14456             cfg['aria-valuemin'] = this.aria_valuemin;
14457         }
14458         
14459         if(this.aria_valuemax){
14460             cfg['aria-valuemax'] = this.aria_valuemax;
14461         }
14462         
14463         if(this.label && !this.sr_only){
14464             cfg.html = this.label;
14465         }
14466         
14467         if(this.panel){
14468             cfg.cls += ' progress-bar-' + this.panel;
14469         }
14470         
14471         return cfg;
14472     },
14473     
14474     update : function(aria_valuenow)
14475     {
14476         this.aria_valuenow = aria_valuenow;
14477         
14478         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14479     }
14480    
14481 });
14482
14483  
14484
14485  /*
14486  * - LGPL
14487  *
14488  * column
14489  * 
14490  */
14491
14492 /**
14493  * @class Roo.bootstrap.TabGroup
14494  * @extends Roo.bootstrap.Column
14495  * Bootstrap Column class
14496  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14497  * @cfg {Boolean} carousel true to make the group behave like a carousel
14498  * 
14499  * @constructor
14500  * Create a new TabGroup
14501  * @param {Object} config The config object
14502  */
14503
14504 Roo.bootstrap.TabGroup = function(config){
14505     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14506     if (!this.navId) {
14507         this.navId = Roo.id();
14508     }
14509     this.tabs = [];
14510     Roo.bootstrap.TabGroup.register(this);
14511     
14512 };
14513
14514 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14515     
14516     carousel : false,
14517     transition : false,
14518      
14519     getAutoCreate : function()
14520     {
14521         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14522         
14523         cfg.cls += ' tab-content';
14524         
14525         if (this.carousel) {
14526             cfg.cls += ' carousel slide';
14527             cfg.cn = [{
14528                cls : 'carousel-inner'
14529             }]
14530         }
14531         
14532         
14533         return cfg;
14534     },
14535     getChildContainer : function()
14536     {
14537         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14538     },
14539     
14540     /**
14541     * register a Navigation item
14542     * @param {Roo.bootstrap.NavItem} the navitem to add
14543     */
14544     register : function(item)
14545     {
14546         this.tabs.push( item);
14547         item.navId = this.navId; // not really needed..
14548     
14549     },
14550     
14551     getActivePanel : function()
14552     {
14553         var r = false;
14554         Roo.each(this.tabs, function(t) {
14555             if (t.active) {
14556                 r = t;
14557                 return false;
14558             }
14559             return null;
14560         });
14561         return r;
14562         
14563     },
14564     getPanelByName : function(n)
14565     {
14566         var r = false;
14567         Roo.each(this.tabs, function(t) {
14568             if (t.tabId == n) {
14569                 r = t;
14570                 return false;
14571             }
14572             return null;
14573         });
14574         return r;
14575     },
14576     indexOfPanel : function(p)
14577     {
14578         var r = false;
14579         Roo.each(this.tabs, function(t,i) {
14580             if (t.tabId == p.tabId) {
14581                 r = i;
14582                 return false;
14583             }
14584             return null;
14585         });
14586         return r;
14587     },
14588     /**
14589      * show a specific panel
14590      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14591      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14592      */
14593     showPanel : function (pan)
14594     {
14595         
14596         if (typeof(pan) == 'number') {
14597             pan = this.tabs[pan];
14598         }
14599         if (typeof(pan) == 'string') {
14600             pan = this.getPanelByName(pan);
14601         }
14602         if (pan.tabId == this.getActivePanel().tabId) {
14603             return true;
14604         }
14605         var cur = this.getActivePanel();
14606         
14607         if (false === cur.fireEvent('beforedeactivate')) {
14608             return false;
14609         }
14610         
14611         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14612             
14613             this.transition = true;
14614             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14615             var lr = dir == 'next' ? 'left' : 'right';
14616             pan.el.addClass(dir); // or prev
14617             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14618             cur.el.addClass(lr); // or right
14619             pan.el.addClass(lr);
14620             
14621             var _this = this;
14622             cur.el.on('transitionend', function() {
14623                 Roo.log("trans end?");
14624                 
14625                 pan.el.removeClass([lr,dir]);
14626                 pan.setActive(true);
14627                 
14628                 cur.el.removeClass([lr]);
14629                 cur.setActive(false);
14630                 
14631                 _this.transition = false;
14632                 
14633             }, this, { single:  true } );
14634             return true;
14635         }
14636         
14637         cur.setActive(false);
14638         pan.setActive(true);
14639         return true;
14640         
14641     },
14642     showPanelNext : function()
14643     {
14644         var i = this.indexOfPanel(this.getActivePanel());
14645         if (i > this.tabs.length) {
14646             return;
14647         }
14648         this.showPanel(this.tabs[i+1]);
14649     },
14650     showPanelPrev : function()
14651     {
14652         var i = this.indexOfPanel(this.getActivePanel());
14653         if (i  < 1) {
14654             return;
14655         }
14656         this.showPanel(this.tabs[i-1]);
14657     }
14658     
14659     
14660   
14661 });
14662
14663  
14664
14665  
14666  
14667 Roo.apply(Roo.bootstrap.TabGroup, {
14668     
14669     groups: {},
14670      /**
14671     * register a Navigation Group
14672     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14673     */
14674     register : function(navgrp)
14675     {
14676         this.groups[navgrp.navId] = navgrp;
14677         
14678     },
14679     /**
14680     * fetch a Navigation Group based on the navigation ID
14681     * if one does not exist , it will get created.
14682     * @param {string} the navgroup to add
14683     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14684     */
14685     get: function(navId) {
14686         if (typeof(this.groups[navId]) == 'undefined') {
14687             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14688         }
14689         return this.groups[navId] ;
14690     }
14691     
14692     
14693     
14694 });
14695
14696  /*
14697  * - LGPL
14698  *
14699  * TabPanel
14700  * 
14701  */
14702
14703 /**
14704  * @class Roo.bootstrap.TabPanel
14705  * @extends Roo.bootstrap.Component
14706  * Bootstrap TabPanel class
14707  * @cfg {Boolean} active panel active
14708  * @cfg {String} html panel content
14709  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14710  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14711  * 
14712  * 
14713  * @constructor
14714  * Create a new TabPanel
14715  * @param {Object} config The config object
14716  */
14717
14718 Roo.bootstrap.TabPanel = function(config){
14719     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14720     this.addEvents({
14721         /**
14722              * @event changed
14723              * Fires when the active status changes
14724              * @param {Roo.bootstrap.TabPanel} this
14725              * @param {Boolean} state the new state
14726             
14727          */
14728         'changed': true,
14729         /**
14730              * @event beforedeactivate
14731              * Fires before a tab is de-activated - can be used to do validation on a form.
14732              * @param {Roo.bootstrap.TabPanel} this
14733              * @return {Boolean} false if there is an error
14734             
14735          */
14736         'beforedeactivate': true
14737      });
14738     
14739     this.tabId = this.tabId || Roo.id();
14740   
14741 };
14742
14743 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14744     
14745     active: false,
14746     html: false,
14747     tabId: false,
14748     navId : false,
14749     
14750     getAutoCreate : function(){
14751         var cfg = {
14752             tag: 'div',
14753             // item is needed for carousel - not sure if it has any effect otherwise
14754             cls: 'tab-pane item',
14755             html: this.html || ''
14756         };
14757         
14758         if(this.active){
14759             cfg.cls += ' active';
14760         }
14761         
14762         if(this.tabId){
14763             cfg.tabId = this.tabId;
14764         }
14765         
14766         
14767         return cfg;
14768     },
14769     
14770     initEvents:  function()
14771     {
14772         Roo.log('-------- init events on tab panel ---------');
14773         
14774         var p = this.parent();
14775         this.navId = this.navId || p.navId;
14776         
14777         if (typeof(this.navId) != 'undefined') {
14778             // not really needed.. but just in case.. parent should be a NavGroup.
14779             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14780             Roo.log(['register', tg, this]);
14781             tg.register(this);
14782         }
14783     },
14784     
14785     
14786     onRender : function(ct, position)
14787     {
14788        // Roo.log("Call onRender: " + this.xtype);
14789         
14790         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14791         
14792         
14793         
14794         
14795         
14796     },
14797     
14798     setActive: function(state)
14799     {
14800         Roo.log("panel - set active " + this.tabId + "=" + state);
14801         
14802         this.active = state;
14803         if (!state) {
14804             this.el.removeClass('active');
14805             
14806         } else  if (!this.el.hasClass('active')) {
14807             this.el.addClass('active');
14808         }
14809         this.fireEvent('changed', this, state);
14810     }
14811     
14812     
14813 });
14814  
14815
14816  
14817
14818  /*
14819  * - LGPL
14820  *
14821  * DateField
14822  * 
14823  */
14824
14825 /**
14826  * @class Roo.bootstrap.DateField
14827  * @extends Roo.bootstrap.Input
14828  * Bootstrap DateField class
14829  * @cfg {Number} weekStart default 0
14830  * @cfg {String} viewMode default empty, (months|years)
14831  * @cfg {String} minViewMode default empty, (months|years)
14832  * @cfg {Number} startDate default -Infinity
14833  * @cfg {Number} endDate default Infinity
14834  * @cfg {Boolean} todayHighlight default false
14835  * @cfg {Boolean} todayBtn default false
14836  * @cfg {Boolean} calendarWeeks default false
14837  * @cfg {Object} daysOfWeekDisabled default empty
14838  * @cfg {Boolean} singleMode default false (true | false)
14839  * 
14840  * @cfg {Boolean} keyboardNavigation default true
14841  * @cfg {String} language default en
14842  * 
14843  * @constructor
14844  * Create a new DateField
14845  * @param {Object} config The config object
14846  */
14847
14848 Roo.bootstrap.DateField = function(config){
14849     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14850      this.addEvents({
14851             /**
14852              * @event show
14853              * Fires when this field show.
14854              * @param {Roo.bootstrap.DateField} this
14855              * @param {Mixed} date The date value
14856              */
14857             show : true,
14858             /**
14859              * @event show
14860              * Fires when this field hide.
14861              * @param {Roo.bootstrap.DateField} this
14862              * @param {Mixed} date The date value
14863              */
14864             hide : true,
14865             /**
14866              * @event select
14867              * Fires when select a date.
14868              * @param {Roo.bootstrap.DateField} this
14869              * @param {Mixed} date The date value
14870              */
14871             select : true
14872         });
14873 };
14874
14875 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14876     
14877     /**
14878      * @cfg {String} format
14879      * The default date format string which can be overriden for localization support.  The format must be
14880      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14881      */
14882     format : "m/d/y",
14883     /**
14884      * @cfg {String} altFormats
14885      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14886      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14887      */
14888     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14889     
14890     weekStart : 0,
14891     
14892     viewMode : '',
14893     
14894     minViewMode : '',
14895     
14896     todayHighlight : false,
14897     
14898     todayBtn: false,
14899     
14900     language: 'en',
14901     
14902     keyboardNavigation: true,
14903     
14904     calendarWeeks: false,
14905     
14906     startDate: -Infinity,
14907     
14908     endDate: Infinity,
14909     
14910     daysOfWeekDisabled: [],
14911     
14912     _events: [],
14913     
14914     singleMode : false,
14915     
14916     UTCDate: function()
14917     {
14918         return new Date(Date.UTC.apply(Date, arguments));
14919     },
14920     
14921     UTCToday: function()
14922     {
14923         var today = new Date();
14924         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14925     },
14926     
14927     getDate: function() {
14928             var d = this.getUTCDate();
14929             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14930     },
14931     
14932     getUTCDate: function() {
14933             return this.date;
14934     },
14935     
14936     setDate: function(d) {
14937             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14938     },
14939     
14940     setUTCDate: function(d) {
14941             this.date = d;
14942             this.setValue(this.formatDate(this.date));
14943     },
14944         
14945     onRender: function(ct, position)
14946     {
14947         
14948         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14949         
14950         this.language = this.language || 'en';
14951         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14952         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14953         
14954         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14955         this.format = this.format || 'm/d/y';
14956         this.isInline = false;
14957         this.isInput = true;
14958         this.component = this.el.select('.add-on', true).first() || false;
14959         this.component = (this.component && this.component.length === 0) ? false : this.component;
14960         this.hasInput = this.component && this.inputEL().length;
14961         
14962         if (typeof(this.minViewMode === 'string')) {
14963             switch (this.minViewMode) {
14964                 case 'months':
14965                     this.minViewMode = 1;
14966                     break;
14967                 case 'years':
14968                     this.minViewMode = 2;
14969                     break;
14970                 default:
14971                     this.minViewMode = 0;
14972                     break;
14973             }
14974         }
14975         
14976         if (typeof(this.viewMode === 'string')) {
14977             switch (this.viewMode) {
14978                 case 'months':
14979                     this.viewMode = 1;
14980                     break;
14981                 case 'years':
14982                     this.viewMode = 2;
14983                     break;
14984                 default:
14985                     this.viewMode = 0;
14986                     break;
14987             }
14988         }
14989                 
14990         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14991         
14992 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14993         
14994         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14995         
14996         this.picker().on('mousedown', this.onMousedown, this);
14997         this.picker().on('click', this.onClick, this);
14998         
14999         this.picker().addClass('datepicker-dropdown');
15000         
15001         this.startViewMode = this.viewMode;
15002         
15003         if(this.singleMode){
15004             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15005                 v.setVisibilityMode(Roo.Element.DISPLAY)
15006                 v.hide();
15007             });
15008             
15009             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15010                 v.setStyle('width', '189px');
15011             });
15012         }
15013         
15014         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15015             if(!this.calendarWeeks){
15016                 v.remove();
15017                 return;
15018             }
15019             
15020             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15021             v.attr('colspan', function(i, val){
15022                 return parseInt(val) + 1;
15023             });
15024         })
15025                         
15026         
15027         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15028         
15029         this.setStartDate(this.startDate);
15030         this.setEndDate(this.endDate);
15031         
15032         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15033         
15034         this.fillDow();
15035         this.fillMonths();
15036         this.update();
15037         this.showMode();
15038         
15039         if(this.isInline) {
15040             this.show();
15041         }
15042     },
15043     
15044     picker : function()
15045     {
15046         return this.pickerEl;
15047 //        return this.el.select('.datepicker', true).first();
15048     },
15049     
15050     fillDow: function()
15051     {
15052         var dowCnt = this.weekStart;
15053         
15054         var dow = {
15055             tag: 'tr',
15056             cn: [
15057                 
15058             ]
15059         };
15060         
15061         if(this.calendarWeeks){
15062             dow.cn.push({
15063                 tag: 'th',
15064                 cls: 'cw',
15065                 html: '&nbsp;'
15066             })
15067         }
15068         
15069         while (dowCnt < this.weekStart + 7) {
15070             dow.cn.push({
15071                 tag: 'th',
15072                 cls: 'dow',
15073                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15074             });
15075         }
15076         
15077         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15078     },
15079     
15080     fillMonths: function()
15081     {    
15082         var i = 0;
15083         var months = this.picker().select('>.datepicker-months td', true).first();
15084         
15085         months.dom.innerHTML = '';
15086         
15087         while (i < 12) {
15088             var month = {
15089                 tag: 'span',
15090                 cls: 'month',
15091                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15092             }
15093             
15094             months.createChild(month);
15095         }
15096         
15097     },
15098     
15099     update: function()
15100     {
15101         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;
15102         
15103         if (this.date < this.startDate) {
15104             this.viewDate = new Date(this.startDate);
15105         } else if (this.date > this.endDate) {
15106             this.viewDate = new Date(this.endDate);
15107         } else {
15108             this.viewDate = new Date(this.date);
15109         }
15110         
15111         this.fill();
15112     },
15113     
15114     fill: function() 
15115     {
15116         var d = new Date(this.viewDate),
15117                 year = d.getUTCFullYear(),
15118                 month = d.getUTCMonth(),
15119                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15120                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15121                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15122                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15123                 currentDate = this.date && this.date.valueOf(),
15124                 today = this.UTCToday();
15125         
15126         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15127         
15128 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15129         
15130 //        this.picker.select('>tfoot th.today').
15131 //                                              .text(dates[this.language].today)
15132 //                                              .toggle(this.todayBtn !== false);
15133     
15134         this.updateNavArrows();
15135         this.fillMonths();
15136                                                 
15137         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15138         
15139         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15140          
15141         prevMonth.setUTCDate(day);
15142         
15143         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15144         
15145         var nextMonth = new Date(prevMonth);
15146         
15147         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15148         
15149         nextMonth = nextMonth.valueOf();
15150         
15151         var fillMonths = false;
15152         
15153         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15154         
15155         while(prevMonth.valueOf() < nextMonth) {
15156             var clsName = '';
15157             
15158             if (prevMonth.getUTCDay() === this.weekStart) {
15159                 if(fillMonths){
15160                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15161                 }
15162                     
15163                 fillMonths = {
15164                     tag: 'tr',
15165                     cn: []
15166                 };
15167                 
15168                 if(this.calendarWeeks){
15169                     // ISO 8601: First week contains first thursday.
15170                     // ISO also states week starts on Monday, but we can be more abstract here.
15171                     var
15172                     // Start of current week: based on weekstart/current date
15173                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15174                     // Thursday of this week
15175                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15176                     // First Thursday of year, year from thursday
15177                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15178                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15179                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15180                     
15181                     fillMonths.cn.push({
15182                         tag: 'td',
15183                         cls: 'cw',
15184                         html: calWeek
15185                     });
15186                 }
15187             }
15188             
15189             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15190                 clsName += ' old';
15191             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15192                 clsName += ' new';
15193             }
15194             if (this.todayHighlight &&
15195                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15196                 prevMonth.getUTCMonth() == today.getMonth() &&
15197                 prevMonth.getUTCDate() == today.getDate()) {
15198                 clsName += ' today';
15199             }
15200             
15201             if (currentDate && prevMonth.valueOf() === currentDate) {
15202                 clsName += ' active';
15203             }
15204             
15205             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15206                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15207                     clsName += ' disabled';
15208             }
15209             
15210             fillMonths.cn.push({
15211                 tag: 'td',
15212                 cls: 'day ' + clsName,
15213                 html: prevMonth.getDate()
15214             })
15215             
15216             prevMonth.setDate(prevMonth.getDate()+1);
15217         }
15218           
15219         var currentYear = this.date && this.date.getUTCFullYear();
15220         var currentMonth = this.date && this.date.getUTCMonth();
15221         
15222         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15223         
15224         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15225             v.removeClass('active');
15226             
15227             if(currentYear === year && k === currentMonth){
15228                 v.addClass('active');
15229             }
15230             
15231             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15232                 v.addClass('disabled');
15233             }
15234             
15235         });
15236         
15237         
15238         year = parseInt(year/10, 10) * 10;
15239         
15240         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15241         
15242         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15243         
15244         year -= 1;
15245         for (var i = -1; i < 11; i++) {
15246             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15247                 tag: 'span',
15248                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15249                 html: year
15250             })
15251             
15252             year += 1;
15253         }
15254     },
15255     
15256     showMode: function(dir) 
15257     {
15258         if (dir) {
15259             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15260         }
15261         
15262         Roo.each(this.picker().select('>div',true).elements, function(v){
15263             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15264             v.hide();
15265         });
15266         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15267     },
15268     
15269     place: function()
15270     {
15271         if(this.isInline) return;
15272         
15273         this.picker().removeClass(['bottom', 'top']);
15274         
15275         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15276             /*
15277              * place to the top of element!
15278              *
15279              */
15280             
15281             this.picker().addClass('top');
15282             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15283             
15284             return;
15285         }
15286         
15287         this.picker().addClass('bottom');
15288         
15289         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15290     },
15291     
15292     parseDate : function(value)
15293     {
15294         if(!value || value instanceof Date){
15295             return value;
15296         }
15297         var v = Date.parseDate(value, this.format);
15298         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15299             v = Date.parseDate(value, 'Y-m-d');
15300         }
15301         if(!v && this.altFormats){
15302             if(!this.altFormatsArray){
15303                 this.altFormatsArray = this.altFormats.split("|");
15304             }
15305             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15306                 v = Date.parseDate(value, this.altFormatsArray[i]);
15307             }
15308         }
15309         return v;
15310     },
15311     
15312     formatDate : function(date, fmt)
15313     {   
15314         return (!date || !(date instanceof Date)) ?
15315         date : date.dateFormat(fmt || this.format);
15316     },
15317     
15318     onFocus : function()
15319     {
15320         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15321         this.show();
15322     },
15323     
15324     onBlur : function()
15325     {
15326         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15327         
15328         var d = this.inputEl().getValue();
15329         
15330         this.setValue(d);
15331                 
15332         this.hide();
15333     },
15334     
15335     show : function()
15336     {
15337         this.picker().show();
15338         this.update();
15339         this.place();
15340         
15341         this.fireEvent('show', this, this.date);
15342     },
15343     
15344     hide : function()
15345     {
15346         if(this.isInline) return;
15347         this.picker().hide();
15348         this.viewMode = this.startViewMode;
15349         this.showMode();
15350         
15351         this.fireEvent('hide', this, this.date);
15352         
15353     },
15354     
15355     onMousedown: function(e)
15356     {
15357         e.stopPropagation();
15358         e.preventDefault();
15359     },
15360     
15361     keyup: function(e)
15362     {
15363         Roo.bootstrap.DateField.superclass.keyup.call(this);
15364         this.update();
15365     },
15366
15367     setValue: function(v)
15368     {
15369         
15370         // v can be a string or a date..
15371         
15372         
15373         var d = new Date(this.parseDate(v) ).clearTime();
15374         
15375         if(isNaN(d.getTime())){
15376             this.date = this.viewDate = '';
15377             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15378             return;
15379         }
15380         
15381         v = this.formatDate(d);
15382         
15383         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15384         
15385         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15386      
15387         this.update();
15388
15389         this.fireEvent('select', this, this.date);
15390         
15391     },
15392     
15393     getValue: function()
15394     {
15395         return this.formatDate(this.date);
15396     },
15397     
15398     fireKey: function(e)
15399     {
15400         if (!this.picker().isVisible()){
15401             if (e.keyCode == 27) // allow escape to hide and re-show picker
15402                 this.show();
15403             return;
15404         }
15405         
15406         var dateChanged = false,
15407         dir, day, month,
15408         newDate, newViewDate;
15409         
15410         switch(e.keyCode){
15411             case 27: // escape
15412                 this.hide();
15413                 e.preventDefault();
15414                 break;
15415             case 37: // left
15416             case 39: // right
15417                 if (!this.keyboardNavigation) break;
15418                 dir = e.keyCode == 37 ? -1 : 1;
15419                 
15420                 if (e.ctrlKey){
15421                     newDate = this.moveYear(this.date, dir);
15422                     newViewDate = this.moveYear(this.viewDate, dir);
15423                 } else if (e.shiftKey){
15424                     newDate = this.moveMonth(this.date, dir);
15425                     newViewDate = this.moveMonth(this.viewDate, dir);
15426                 } else {
15427                     newDate = new Date(this.date);
15428                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15429                     newViewDate = new Date(this.viewDate);
15430                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15431                 }
15432                 if (this.dateWithinRange(newDate)){
15433                     this.date = newDate;
15434                     this.viewDate = newViewDate;
15435                     this.setValue(this.formatDate(this.date));
15436 //                    this.update();
15437                     e.preventDefault();
15438                     dateChanged = true;
15439                 }
15440                 break;
15441             case 38: // up
15442             case 40: // down
15443                 if (!this.keyboardNavigation) break;
15444                 dir = e.keyCode == 38 ? -1 : 1;
15445                 if (e.ctrlKey){
15446                     newDate = this.moveYear(this.date, dir);
15447                     newViewDate = this.moveYear(this.viewDate, dir);
15448                 } else if (e.shiftKey){
15449                     newDate = this.moveMonth(this.date, dir);
15450                     newViewDate = this.moveMonth(this.viewDate, dir);
15451                 } else {
15452                     newDate = new Date(this.date);
15453                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15454                     newViewDate = new Date(this.viewDate);
15455                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15456                 }
15457                 if (this.dateWithinRange(newDate)){
15458                     this.date = newDate;
15459                     this.viewDate = newViewDate;
15460                     this.setValue(this.formatDate(this.date));
15461 //                    this.update();
15462                     e.preventDefault();
15463                     dateChanged = true;
15464                 }
15465                 break;
15466             case 13: // enter
15467                 this.setValue(this.formatDate(this.date));
15468                 this.hide();
15469                 e.preventDefault();
15470                 break;
15471             case 9: // tab
15472                 this.setValue(this.formatDate(this.date));
15473                 this.hide();
15474                 break;
15475             case 16: // shift
15476             case 17: // ctrl
15477             case 18: // alt
15478                 break;
15479             default :
15480                 this.hide();
15481                 
15482         }
15483     },
15484     
15485     
15486     onClick: function(e) 
15487     {
15488         e.stopPropagation();
15489         e.preventDefault();
15490         
15491         var target = e.getTarget();
15492         
15493         if(target.nodeName.toLowerCase() === 'i'){
15494             target = Roo.get(target).dom.parentNode;
15495         }
15496         
15497         var nodeName = target.nodeName;
15498         var className = target.className;
15499         var html = target.innerHTML;
15500         //Roo.log(nodeName);
15501         
15502         switch(nodeName.toLowerCase()) {
15503             case 'th':
15504                 switch(className) {
15505                     case 'switch':
15506                         this.showMode(1);
15507                         break;
15508                     case 'prev':
15509                     case 'next':
15510                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15511                         switch(this.viewMode){
15512                                 case 0:
15513                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15514                                         break;
15515                                 case 1:
15516                                 case 2:
15517                                         this.viewDate = this.moveYear(this.viewDate, dir);
15518                                         break;
15519                         }
15520                         this.fill();
15521                         break;
15522                     case 'today':
15523                         var date = new Date();
15524                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15525 //                        this.fill()
15526                         this.setValue(this.formatDate(this.date));
15527                         
15528                         this.hide();
15529                         break;
15530                 }
15531                 break;
15532             case 'span':
15533                 if (className.indexOf('disabled') < 0) {
15534                     this.viewDate.setUTCDate(1);
15535                     if (className.indexOf('month') > -1) {
15536                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15537                     } else {
15538                         var year = parseInt(html, 10) || 0;
15539                         this.viewDate.setUTCFullYear(year);
15540                         
15541                     }
15542                     
15543                     if(this.singleMode){
15544                         this.setValue(this.formatDate(this.viewDate));
15545                         this.hide();
15546                         return;
15547                     }
15548                     
15549                     this.showMode(-1);
15550                     this.fill();
15551                 }
15552                 break;
15553                 
15554             case 'td':
15555                 //Roo.log(className);
15556                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15557                     var day = parseInt(html, 10) || 1;
15558                     var year = this.viewDate.getUTCFullYear(),
15559                         month = this.viewDate.getUTCMonth();
15560
15561                     if (className.indexOf('old') > -1) {
15562                         if(month === 0 ){
15563                             month = 11;
15564                             year -= 1;
15565                         }else{
15566                             month -= 1;
15567                         }
15568                     } else if (className.indexOf('new') > -1) {
15569                         if (month == 11) {
15570                             month = 0;
15571                             year += 1;
15572                         } else {
15573                             month += 1;
15574                         }
15575                     }
15576                     //Roo.log([year,month,day]);
15577                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15578                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15579 //                    this.fill();
15580                     //Roo.log(this.formatDate(this.date));
15581                     this.setValue(this.formatDate(this.date));
15582                     this.hide();
15583                 }
15584                 break;
15585         }
15586     },
15587     
15588     setStartDate: function(startDate)
15589     {
15590         this.startDate = startDate || -Infinity;
15591         if (this.startDate !== -Infinity) {
15592             this.startDate = this.parseDate(this.startDate);
15593         }
15594         this.update();
15595         this.updateNavArrows();
15596     },
15597
15598     setEndDate: function(endDate)
15599     {
15600         this.endDate = endDate || Infinity;
15601         if (this.endDate !== Infinity) {
15602             this.endDate = this.parseDate(this.endDate);
15603         }
15604         this.update();
15605         this.updateNavArrows();
15606     },
15607     
15608     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15609     {
15610         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15611         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15612             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15613         }
15614         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15615             return parseInt(d, 10);
15616         });
15617         this.update();
15618         this.updateNavArrows();
15619     },
15620     
15621     updateNavArrows: function() 
15622     {
15623         if(this.singleMode){
15624             return;
15625         }
15626         
15627         var d = new Date(this.viewDate),
15628         year = d.getUTCFullYear(),
15629         month = d.getUTCMonth();
15630         
15631         Roo.each(this.picker().select('.prev', true).elements, function(v){
15632             v.show();
15633             switch (this.viewMode) {
15634                 case 0:
15635
15636                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15637                         v.hide();
15638                     }
15639                     break;
15640                 case 1:
15641                 case 2:
15642                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15643                         v.hide();
15644                     }
15645                     break;
15646             }
15647         });
15648         
15649         Roo.each(this.picker().select('.next', true).elements, function(v){
15650             v.show();
15651             switch (this.viewMode) {
15652                 case 0:
15653
15654                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15655                         v.hide();
15656                     }
15657                     break;
15658                 case 1:
15659                 case 2:
15660                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15661                         v.hide();
15662                     }
15663                     break;
15664             }
15665         })
15666     },
15667     
15668     moveMonth: function(date, dir)
15669     {
15670         if (!dir) return date;
15671         var new_date = new Date(date.valueOf()),
15672         day = new_date.getUTCDate(),
15673         month = new_date.getUTCMonth(),
15674         mag = Math.abs(dir),
15675         new_month, test;
15676         dir = dir > 0 ? 1 : -1;
15677         if (mag == 1){
15678             test = dir == -1
15679             // If going back one month, make sure month is not current month
15680             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15681             ? function(){
15682                 return new_date.getUTCMonth() == month;
15683             }
15684             // If going forward one month, make sure month is as expected
15685             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15686             : function(){
15687                 return new_date.getUTCMonth() != new_month;
15688             };
15689             new_month = month + dir;
15690             new_date.setUTCMonth(new_month);
15691             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15692             if (new_month < 0 || new_month > 11)
15693                 new_month = (new_month + 12) % 12;
15694         } else {
15695             // For magnitudes >1, move one month at a time...
15696             for (var i=0; i<mag; i++)
15697                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15698                 new_date = this.moveMonth(new_date, dir);
15699             // ...then reset the day, keeping it in the new month
15700             new_month = new_date.getUTCMonth();
15701             new_date.setUTCDate(day);
15702             test = function(){
15703                 return new_month != new_date.getUTCMonth();
15704             };
15705         }
15706         // Common date-resetting loop -- if date is beyond end of month, make it
15707         // end of month
15708         while (test()){
15709             new_date.setUTCDate(--day);
15710             new_date.setUTCMonth(new_month);
15711         }
15712         return new_date;
15713     },
15714
15715     moveYear: function(date, dir)
15716     {
15717         return this.moveMonth(date, dir*12);
15718     },
15719
15720     dateWithinRange: function(date)
15721     {
15722         return date >= this.startDate && date <= this.endDate;
15723     },
15724
15725     
15726     remove: function() 
15727     {
15728         this.picker().remove();
15729     }
15730    
15731 });
15732
15733 Roo.apply(Roo.bootstrap.DateField,  {
15734     
15735     head : {
15736         tag: 'thead',
15737         cn: [
15738         {
15739             tag: 'tr',
15740             cn: [
15741             {
15742                 tag: 'th',
15743                 cls: 'prev',
15744                 html: '<i class="fa fa-arrow-left"/>'
15745             },
15746             {
15747                 tag: 'th',
15748                 cls: 'switch',
15749                 colspan: '5'
15750             },
15751             {
15752                 tag: 'th',
15753                 cls: 'next',
15754                 html: '<i class="fa fa-arrow-right"/>'
15755             }
15756
15757             ]
15758         }
15759         ]
15760     },
15761     
15762     content : {
15763         tag: 'tbody',
15764         cn: [
15765         {
15766             tag: 'tr',
15767             cn: [
15768             {
15769                 tag: 'td',
15770                 colspan: '7'
15771             }
15772             ]
15773         }
15774         ]
15775     },
15776     
15777     footer : {
15778         tag: 'tfoot',
15779         cn: [
15780         {
15781             tag: 'tr',
15782             cn: [
15783             {
15784                 tag: 'th',
15785                 colspan: '7',
15786                 cls: 'today'
15787             }
15788                     
15789             ]
15790         }
15791         ]
15792     },
15793     
15794     dates:{
15795         en: {
15796             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15797             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15798             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15799             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15800             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15801             today: "Today"
15802         }
15803     },
15804     
15805     modes: [
15806     {
15807         clsName: 'days',
15808         navFnc: 'Month',
15809         navStep: 1
15810     },
15811     {
15812         clsName: 'months',
15813         navFnc: 'FullYear',
15814         navStep: 1
15815     },
15816     {
15817         clsName: 'years',
15818         navFnc: 'FullYear',
15819         navStep: 10
15820     }]
15821 });
15822
15823 Roo.apply(Roo.bootstrap.DateField,  {
15824   
15825     template : {
15826         tag: 'div',
15827         cls: 'datepicker dropdown-menu roo-dynamic',
15828         cn: [
15829         {
15830             tag: 'div',
15831             cls: 'datepicker-days',
15832             cn: [
15833             {
15834                 tag: 'table',
15835                 cls: 'table-condensed',
15836                 cn:[
15837                 Roo.bootstrap.DateField.head,
15838                 {
15839                     tag: 'tbody'
15840                 },
15841                 Roo.bootstrap.DateField.footer
15842                 ]
15843             }
15844             ]
15845         },
15846         {
15847             tag: 'div',
15848             cls: 'datepicker-months',
15849             cn: [
15850             {
15851                 tag: 'table',
15852                 cls: 'table-condensed',
15853                 cn:[
15854                 Roo.bootstrap.DateField.head,
15855                 Roo.bootstrap.DateField.content,
15856                 Roo.bootstrap.DateField.footer
15857                 ]
15858             }
15859             ]
15860         },
15861         {
15862             tag: 'div',
15863             cls: 'datepicker-years',
15864             cn: [
15865             {
15866                 tag: 'table',
15867                 cls: 'table-condensed',
15868                 cn:[
15869                 Roo.bootstrap.DateField.head,
15870                 Roo.bootstrap.DateField.content,
15871                 Roo.bootstrap.DateField.footer
15872                 ]
15873             }
15874             ]
15875         }
15876         ]
15877     }
15878 });
15879
15880  
15881
15882  /*
15883  * - LGPL
15884  *
15885  * TimeField
15886  * 
15887  */
15888
15889 /**
15890  * @class Roo.bootstrap.TimeField
15891  * @extends Roo.bootstrap.Input
15892  * Bootstrap DateField class
15893  * 
15894  * 
15895  * @constructor
15896  * Create a new TimeField
15897  * @param {Object} config The config object
15898  */
15899
15900 Roo.bootstrap.TimeField = function(config){
15901     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15902     this.addEvents({
15903             /**
15904              * @event show
15905              * Fires when this field show.
15906              * @param {Roo.bootstrap.DateField} thisthis
15907              * @param {Mixed} date The date value
15908              */
15909             show : true,
15910             /**
15911              * @event show
15912              * Fires when this field hide.
15913              * @param {Roo.bootstrap.DateField} this
15914              * @param {Mixed} date The date value
15915              */
15916             hide : true,
15917             /**
15918              * @event select
15919              * Fires when select a date.
15920              * @param {Roo.bootstrap.DateField} this
15921              * @param {Mixed} date The date value
15922              */
15923             select : true
15924         });
15925 };
15926
15927 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15928     
15929     /**
15930      * @cfg {String} format
15931      * The default time format string which can be overriden for localization support.  The format must be
15932      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15933      */
15934     format : "H:i",
15935        
15936     onRender: function(ct, position)
15937     {
15938         
15939         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15940                 
15941         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15942         
15943         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15944         
15945         this.pop = this.picker().select('>.datepicker-time',true).first();
15946         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15947         
15948         this.picker().on('mousedown', this.onMousedown, this);
15949         this.picker().on('click', this.onClick, this);
15950         
15951         this.picker().addClass('datepicker-dropdown');
15952     
15953         this.fillTime();
15954         this.update();
15955             
15956         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15957         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15958         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15959         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15960         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15961         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15962
15963     },
15964     
15965     fireKey: function(e){
15966         if (!this.picker().isVisible()){
15967             if (e.keyCode == 27) { // allow escape to hide and re-show picker
15968                 this.show();
15969             }
15970             return;
15971         }
15972
15973         e.preventDefault();
15974         
15975         switch(e.keyCode){
15976             case 27: // escape
15977                 this.hide();
15978                 break;
15979             case 37: // left
15980             case 39: // right
15981                 this.onTogglePeriod();
15982                 break;
15983             case 38: // up
15984                 this.onIncrementMinutes();
15985                 break;
15986             case 40: // down
15987                 this.onDecrementMinutes();
15988                 break;
15989             case 13: // enter
15990             case 9: // tab
15991                 this.setTime();
15992                 break;
15993         }
15994     },
15995     
15996     onClick: function(e) {
15997         e.stopPropagation();
15998         e.preventDefault();
15999     },
16000     
16001     picker : function()
16002     {
16003         return this.el.select('.datepicker', true).first();
16004     },
16005     
16006     fillTime: function()
16007     {    
16008         var time = this.pop.select('tbody', true).first();
16009         
16010         time.dom.innerHTML = '';
16011         
16012         time.createChild({
16013             tag: 'tr',
16014             cn: [
16015                 {
16016                     tag: 'td',
16017                     cn: [
16018                         {
16019                             tag: 'a',
16020                             href: '#',
16021                             cls: 'btn',
16022                             cn: [
16023                                 {
16024                                     tag: 'span',
16025                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16026                                 }
16027                             ]
16028                         } 
16029                     ]
16030                 },
16031                 {
16032                     tag: 'td',
16033                     cls: 'separator'
16034                 },
16035                 {
16036                     tag: 'td',
16037                     cn: [
16038                         {
16039                             tag: 'a',
16040                             href: '#',
16041                             cls: 'btn',
16042                             cn: [
16043                                 {
16044                                     tag: 'span',
16045                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16046                                 }
16047                             ]
16048                         }
16049                     ]
16050                 },
16051                 {
16052                     tag: 'td',
16053                     cls: 'separator'
16054                 }
16055             ]
16056         });
16057         
16058         time.createChild({
16059             tag: 'tr',
16060             cn: [
16061                 {
16062                     tag: 'td',
16063                     cn: [
16064                         {
16065                             tag: 'span',
16066                             cls: 'timepicker-hour',
16067                             html: '00'
16068                         }  
16069                     ]
16070                 },
16071                 {
16072                     tag: 'td',
16073                     cls: 'separator',
16074                     html: ':'
16075                 },
16076                 {
16077                     tag: 'td',
16078                     cn: [
16079                         {
16080                             tag: 'span',
16081                             cls: 'timepicker-minute',
16082                             html: '00'
16083                         }  
16084                     ]
16085                 },
16086                 {
16087                     tag: 'td',
16088                     cls: 'separator'
16089                 },
16090                 {
16091                     tag: 'td',
16092                     cn: [
16093                         {
16094                             tag: 'button',
16095                             type: 'button',
16096                             cls: 'btn btn-primary period',
16097                             html: 'AM'
16098                             
16099                         }
16100                     ]
16101                 }
16102             ]
16103         });
16104         
16105         time.createChild({
16106             tag: 'tr',
16107             cn: [
16108                 {
16109                     tag: 'td',
16110                     cn: [
16111                         {
16112                             tag: 'a',
16113                             href: '#',
16114                             cls: 'btn',
16115                             cn: [
16116                                 {
16117                                     tag: 'span',
16118                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16119                                 }
16120                             ]
16121                         }
16122                     ]
16123                 },
16124                 {
16125                     tag: 'td',
16126                     cls: 'separator'
16127                 },
16128                 {
16129                     tag: 'td',
16130                     cn: [
16131                         {
16132                             tag: 'a',
16133                             href: '#',
16134                             cls: 'btn',
16135                             cn: [
16136                                 {
16137                                     tag: 'span',
16138                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16139                                 }
16140                             ]
16141                         }
16142                     ]
16143                 },
16144                 {
16145                     tag: 'td',
16146                     cls: 'separator'
16147                 }
16148             ]
16149         });
16150         
16151     },
16152     
16153     update: function()
16154     {
16155         
16156         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16157         
16158         this.fill();
16159     },
16160     
16161     fill: function() 
16162     {
16163         var hours = this.time.getHours();
16164         var minutes = this.time.getMinutes();
16165         var period = 'AM';
16166         
16167         if(hours > 11){
16168             period = 'PM';
16169         }
16170         
16171         if(hours == 0){
16172             hours = 12;
16173         }
16174         
16175         
16176         if(hours > 12){
16177             hours = hours - 12;
16178         }
16179         
16180         if(hours < 10){
16181             hours = '0' + hours;
16182         }
16183         
16184         if(minutes < 10){
16185             minutes = '0' + minutes;
16186         }
16187         
16188         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16189         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16190         this.pop.select('button', true).first().dom.innerHTML = period;
16191         
16192     },
16193     
16194     place: function()
16195     {   
16196         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16197         
16198         var cls = ['bottom'];
16199         
16200         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16201             cls.pop();
16202             cls.push('top');
16203         }
16204         
16205         cls.push('right');
16206         
16207         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16208             cls.pop();
16209             cls.push('left');
16210         }
16211         
16212         this.picker().addClass(cls.join('-'));
16213         
16214         var _this = this;
16215         
16216         Roo.each(cls, function(c){
16217             if(c == 'bottom'){
16218                 _this.picker().setTop(_this.inputEl().getHeight());
16219                 return;
16220             }
16221             if(c == 'top'){
16222                 _this.picker().setTop(0 - _this.picker().getHeight());
16223                 return;
16224             }
16225             
16226             if(c == 'left'){
16227                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16228                 return;
16229             }
16230             if(c == 'right'){
16231                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16232                 return;
16233             }
16234         });
16235         
16236     },
16237   
16238     onFocus : function()
16239     {
16240         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16241         this.show();
16242     },
16243     
16244     onBlur : function()
16245     {
16246         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16247         this.hide();
16248     },
16249     
16250     show : function()
16251     {
16252         this.picker().show();
16253         this.pop.show();
16254         this.update();
16255         this.place();
16256         
16257         this.fireEvent('show', this, this.date);
16258     },
16259     
16260     hide : function()
16261     {
16262         this.picker().hide();
16263         this.pop.hide();
16264         
16265         this.fireEvent('hide', this, this.date);
16266     },
16267     
16268     setTime : function()
16269     {
16270         this.hide();
16271         this.setValue(this.time.format(this.format));
16272         
16273         this.fireEvent('select', this, this.date);
16274         
16275         
16276     },
16277     
16278     onMousedown: function(e){
16279         e.stopPropagation();
16280         e.preventDefault();
16281     },
16282     
16283     onIncrementHours: function()
16284     {
16285         Roo.log('onIncrementHours');
16286         this.time = this.time.add(Date.HOUR, 1);
16287         this.update();
16288         
16289     },
16290     
16291     onDecrementHours: function()
16292     {
16293         Roo.log('onDecrementHours');
16294         this.time = this.time.add(Date.HOUR, -1);
16295         this.update();
16296     },
16297     
16298     onIncrementMinutes: function()
16299     {
16300         Roo.log('onIncrementMinutes');
16301         this.time = this.time.add(Date.MINUTE, 1);
16302         this.update();
16303     },
16304     
16305     onDecrementMinutes: function()
16306     {
16307         Roo.log('onDecrementMinutes');
16308         this.time = this.time.add(Date.MINUTE, -1);
16309         this.update();
16310     },
16311     
16312     onTogglePeriod: function()
16313     {
16314         Roo.log('onTogglePeriod');
16315         this.time = this.time.add(Date.HOUR, 12);
16316         this.update();
16317     }
16318     
16319    
16320 });
16321
16322 Roo.apply(Roo.bootstrap.TimeField,  {
16323     
16324     content : {
16325         tag: 'tbody',
16326         cn: [
16327             {
16328                 tag: 'tr',
16329                 cn: [
16330                 {
16331                     tag: 'td',
16332                     colspan: '7'
16333                 }
16334                 ]
16335             }
16336         ]
16337     },
16338     
16339     footer : {
16340         tag: 'tfoot',
16341         cn: [
16342             {
16343                 tag: 'tr',
16344                 cn: [
16345                 {
16346                     tag: 'th',
16347                     colspan: '7',
16348                     cls: '',
16349                     cn: [
16350                         {
16351                             tag: 'button',
16352                             cls: 'btn btn-info ok',
16353                             html: 'OK'
16354                         }
16355                     ]
16356                 }
16357
16358                 ]
16359             }
16360         ]
16361     }
16362 });
16363
16364 Roo.apply(Roo.bootstrap.TimeField,  {
16365   
16366     template : {
16367         tag: 'div',
16368         cls: 'datepicker dropdown-menu',
16369         cn: [
16370             {
16371                 tag: 'div',
16372                 cls: 'datepicker-time',
16373                 cn: [
16374                 {
16375                     tag: 'table',
16376                     cls: 'table-condensed',
16377                     cn:[
16378                     Roo.bootstrap.TimeField.content,
16379                     Roo.bootstrap.TimeField.footer
16380                     ]
16381                 }
16382                 ]
16383             }
16384         ]
16385     }
16386 });
16387
16388  
16389
16390  /*
16391  * - LGPL
16392  *
16393  * MonthField
16394  * 
16395  */
16396
16397 /**
16398  * @class Roo.bootstrap.MonthField
16399  * @extends Roo.bootstrap.Input
16400  * Bootstrap MonthField class
16401  * 
16402  * @cfg {String} language default en
16403  * 
16404  * @constructor
16405  * Create a new MonthField
16406  * @param {Object} config The config object
16407  */
16408
16409 Roo.bootstrap.MonthField = function(config){
16410     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16411     
16412     this.addEvents({
16413         /**
16414          * @event show
16415          * Fires when this field show.
16416          * @param {Roo.bootstrap.MonthField} this
16417          * @param {Mixed} date The date value
16418          */
16419         show : true,
16420         /**
16421          * @event show
16422          * Fires when this field hide.
16423          * @param {Roo.bootstrap.MonthField} this
16424          * @param {Mixed} date The date value
16425          */
16426         hide : true,
16427         /**
16428          * @event select
16429          * Fires when select a date.
16430          * @param {Roo.bootstrap.MonthField} this
16431          * @param {String} oldvalue The old value
16432          * @param {String} newvalue The new value
16433          */
16434         select : true
16435     });
16436 };
16437
16438 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16439     
16440     onRender: function(ct, position)
16441     {
16442         
16443         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16444         
16445         this.language = this.language || 'en';
16446         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16447         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16448         
16449         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16450         this.isInline = false;
16451         this.isInput = true;
16452         this.component = this.el.select('.add-on', true).first() || false;
16453         this.component = (this.component && this.component.length === 0) ? false : this.component;
16454         this.hasInput = this.component && this.inputEL().length;
16455         
16456         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16457         
16458         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16459         
16460         this.picker().on('mousedown', this.onMousedown, this);
16461         this.picker().on('click', this.onClick, this);
16462         
16463         this.picker().addClass('datepicker-dropdown');
16464         
16465         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16466             v.setStyle('width', '189px');
16467         });
16468         
16469         this.fillMonths();
16470         
16471         this.update();
16472         
16473         if(this.isInline) {
16474             this.show();
16475         }
16476         
16477     },
16478     
16479     setValue: function(v, suppressEvent)
16480     {   
16481         var o = this.getValue();
16482         
16483         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16484         
16485         this.update();
16486
16487         if(suppressEvent !== true){
16488             this.fireEvent('select', this, o, v);
16489         }
16490         
16491     },
16492     
16493     getValue: function()
16494     {
16495         return this.value;
16496     },
16497     
16498     onClick: function(e) 
16499     {
16500         e.stopPropagation();
16501         e.preventDefault();
16502         
16503         var target = e.getTarget();
16504         
16505         if(target.nodeName.toLowerCase() === 'i'){
16506             target = Roo.get(target).dom.parentNode;
16507         }
16508         
16509         var nodeName = target.nodeName;
16510         var className = target.className;
16511         var html = target.innerHTML;
16512         
16513         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16514             return;
16515         }
16516         
16517         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16518         
16519         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16520         
16521         this.hide();
16522                         
16523     },
16524     
16525     picker : function()
16526     {
16527         return this.pickerEl;
16528     },
16529     
16530     fillMonths: function()
16531     {    
16532         var i = 0;
16533         var months = this.picker().select('>.datepicker-months td', true).first();
16534         
16535         months.dom.innerHTML = '';
16536         
16537         while (i < 12) {
16538             var month = {
16539                 tag: 'span',
16540                 cls: 'month',
16541                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16542             }
16543             
16544             months.createChild(month);
16545         }
16546         
16547     },
16548     
16549     update: function()
16550     {
16551         var _this = this;
16552         
16553         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16554             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16555         }
16556         
16557         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16558             e.removeClass('active');
16559             
16560             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16561                 e.addClass('active');
16562             }
16563         })
16564     },
16565     
16566     place: function()
16567     {
16568         if(this.isInline) return;
16569         
16570         this.picker().removeClass(['bottom', 'top']);
16571         
16572         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16573             /*
16574              * place to the top of element!
16575              *
16576              */
16577             
16578             this.picker().addClass('top');
16579             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16580             
16581             return;
16582         }
16583         
16584         this.picker().addClass('bottom');
16585         
16586         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16587     },
16588     
16589     onFocus : function()
16590     {
16591         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
16592         this.show();
16593     },
16594     
16595     onBlur : function()
16596     {
16597         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
16598         
16599         var d = this.inputEl().getValue();
16600         
16601         this.setValue(d);
16602                 
16603         this.hide();
16604     },
16605     
16606     show : function()
16607     {
16608         this.picker().show();
16609         this.picker().select('>.datepicker-months', true).first().show();
16610         this.update();
16611         this.place();
16612         
16613         this.fireEvent('show', this, this.date);
16614     },
16615     
16616     hide : function()
16617     {
16618         if(this.isInline) return;
16619         this.picker().hide();
16620         this.fireEvent('hide', this, this.date);
16621         
16622     },
16623     
16624     onMousedown: function(e)
16625     {
16626         e.stopPropagation();
16627         e.preventDefault();
16628     },
16629     
16630     keyup: function(e)
16631     {
16632         Roo.bootstrap.MonthField.superclass.keyup.call(this);
16633         this.update();
16634     },
16635
16636     fireKey: function(e)
16637     {
16638         if (!this.picker().isVisible()){
16639             if (e.keyCode == 27) // allow escape to hide and re-show picker
16640                 this.show();
16641             return;
16642         }
16643         
16644         var dir;
16645         
16646         switch(e.keyCode){
16647             case 27: // escape
16648                 this.hide();
16649                 e.preventDefault();
16650                 break;
16651             case 37: // left
16652             case 39: // right
16653                 dir = e.keyCode == 37 ? -1 : 1;
16654                 
16655                 this.vIndex = this.vIndex + dir;
16656                 
16657                 if(this.vIndex < 0){
16658                     this.vIndex = 0;
16659                 }
16660                 
16661                 if(this.vIndex > 11){
16662                     this.vIndex = 11;
16663                 }
16664                 
16665                 if(isNaN(this.vIndex)){
16666                     this.vIndex = 0;
16667                 }
16668                 
16669                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16670                 
16671                 break;
16672             case 38: // up
16673             case 40: // down
16674                 
16675                 dir = e.keyCode == 38 ? -1 : 1;
16676                 
16677                 this.vIndex = this.vIndex + dir * 4;
16678                 
16679                 if(this.vIndex < 0){
16680                     this.vIndex = 0;
16681                 }
16682                 
16683                 if(this.vIndex > 11){
16684                     this.vIndex = 11;
16685                 }
16686                 
16687                 if(isNaN(this.vIndex)){
16688                     this.vIndex = 0;
16689                 }
16690                 
16691                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16692                 break;
16693                 
16694             case 13: // enter
16695                 
16696                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16697                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16698                 }
16699                 
16700                 this.hide();
16701                 e.preventDefault();
16702                 break;
16703             case 9: // tab
16704                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16705                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16706                 }
16707                 this.hide();
16708                 break;
16709             case 16: // shift
16710             case 17: // ctrl
16711             case 18: // alt
16712                 break;
16713             default :
16714                 this.hide();
16715                 
16716         }
16717     },
16718     
16719     remove: function() 
16720     {
16721         this.picker().remove();
16722     }
16723    
16724 });
16725
16726 Roo.apply(Roo.bootstrap.MonthField,  {
16727     
16728     content : {
16729         tag: 'tbody',
16730         cn: [
16731         {
16732             tag: 'tr',
16733             cn: [
16734             {
16735                 tag: 'td',
16736                 colspan: '7'
16737             }
16738             ]
16739         }
16740         ]
16741     },
16742     
16743     dates:{
16744         en: {
16745             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16746             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
16747         }
16748     }
16749 });
16750
16751 Roo.apply(Roo.bootstrap.MonthField,  {
16752   
16753     template : {
16754         tag: 'div',
16755         cls: 'datepicker dropdown-menu roo-dynamic',
16756         cn: [
16757             {
16758                 tag: 'div',
16759                 cls: 'datepicker-months',
16760                 cn: [
16761                 {
16762                     tag: 'table',
16763                     cls: 'table-condensed',
16764                     cn:[
16765                         Roo.bootstrap.DateField.content
16766                     ]
16767                 }
16768                 ]
16769             }
16770         ]
16771     }
16772 });
16773
16774  
16775
16776  
16777  /*
16778  * - LGPL
16779  *
16780  * CheckBox
16781  * 
16782  */
16783
16784 /**
16785  * @class Roo.bootstrap.CheckBox
16786  * @extends Roo.bootstrap.Input
16787  * Bootstrap CheckBox class
16788  * 
16789  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
16790  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
16791  * @cfg {String} boxLabel The text that appears beside the checkbox
16792  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
16793  * @cfg {Boolean} checked initnal the element
16794  * @cfg {Boolean} inline inline the element (default false)
16795  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
16796  * 
16797  * @constructor
16798  * Create a new CheckBox
16799  * @param {Object} config The config object
16800  */
16801
16802 Roo.bootstrap.CheckBox = function(config){
16803     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
16804    
16805     this.addEvents({
16806         /**
16807         * @event check
16808         * Fires when the element is checked or unchecked.
16809         * @param {Roo.bootstrap.CheckBox} this This input
16810         * @param {Boolean} checked The new checked value
16811         */
16812        check : true
16813     });
16814     
16815 };
16816
16817 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
16818   
16819     inputType: 'checkbox',
16820     inputValue: 1,
16821     valueOff: 0,
16822     boxLabel: false,
16823     checked: false,
16824     weight : false,
16825     inline: false,
16826     
16827     getAutoCreate : function()
16828     {
16829         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16830         
16831         var id = Roo.id();
16832         
16833         var cfg = {};
16834         
16835         cfg.cls = 'form-group ' + this.inputType; //input-group
16836         
16837         if(this.inline){
16838             cfg.cls += ' ' + this.inputType + '-inline';
16839         }
16840         
16841         var input =  {
16842             tag: 'input',
16843             id : id,
16844             type : this.inputType,
16845             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
16846             cls : 'roo-' + this.inputType, //'form-box',
16847             placeholder : this.placeholder || ''
16848             
16849         };
16850         
16851         if (this.weight) { // Validity check?
16852             cfg.cls += " " + this.inputType + "-" + this.weight;
16853         }
16854         
16855         if (this.disabled) {
16856             input.disabled=true;
16857         }
16858         
16859         if(this.checked){
16860             input.checked = this.checked;
16861         }
16862         
16863         if (this.name) {
16864             input.name = this.name;
16865         }
16866         
16867         if (this.size) {
16868             input.cls += ' input-' + this.size;
16869         }
16870         
16871         var settings=this;
16872         
16873         ['xs','sm','md','lg'].map(function(size){
16874             if (settings[size]) {
16875                 cfg.cls += ' col-' + size + '-' + settings[size];
16876             }
16877         });
16878         
16879         var inputblock = input;
16880          
16881         if (this.before || this.after) {
16882             
16883             inputblock = {
16884                 cls : 'input-group',
16885                 cn :  [] 
16886             };
16887             
16888             if (this.before) {
16889                 inputblock.cn.push({
16890                     tag :'span',
16891                     cls : 'input-group-addon',
16892                     html : this.before
16893                 });
16894             }
16895             
16896             inputblock.cn.push(input);
16897             
16898             if (this.after) {
16899                 inputblock.cn.push({
16900                     tag :'span',
16901                     cls : 'input-group-addon',
16902                     html : this.after
16903                 });
16904             }
16905             
16906         }
16907         
16908         if (align ==='left' && this.fieldLabel.length) {
16909                 Roo.log("left and has label");
16910                 cfg.cn = [
16911                     
16912                     {
16913                         tag: 'label',
16914                         'for' :  id,
16915                         cls : 'control-label col-md-' + this.labelWidth,
16916                         html : this.fieldLabel
16917                         
16918                     },
16919                     {
16920                         cls : "col-md-" + (12 - this.labelWidth), 
16921                         cn: [
16922                             inputblock
16923                         ]
16924                     }
16925                     
16926                 ];
16927         } else if ( this.fieldLabel.length) {
16928                 Roo.log(" label");
16929                 cfg.cn = [
16930                    
16931                     {
16932                         tag: this.boxLabel ? 'span' : 'label',
16933                         'for': id,
16934                         cls: 'control-label box-input-label',
16935                         //cls : 'input-group-addon',
16936                         html : this.fieldLabel
16937                         
16938                     },
16939                     
16940                     inputblock
16941                     
16942                 ];
16943
16944         } else {
16945             
16946                 Roo.log(" no label && no align");
16947                 cfg.cn = [  inputblock ] ;
16948                 
16949                 
16950         }
16951         if(this.boxLabel){
16952              var boxLabelCfg = {
16953                 tag: 'label',
16954                 //'for': id, // box label is handled by onclick - so no for...
16955                 cls: 'box-label',
16956                 html: this.boxLabel
16957             }
16958             
16959             if(this.tooltip){
16960                 boxLabelCfg.tooltip = this.tooltip;
16961             }
16962              
16963             cfg.cn.push(boxLabelCfg);
16964         }
16965         
16966         
16967        
16968         return cfg;
16969         
16970     },
16971     
16972     /**
16973      * return the real input element.
16974      */
16975     inputEl: function ()
16976     {
16977         return this.el.select('input.roo-' + this.inputType,true).first();
16978     },
16979     
16980     labelEl: function()
16981     {
16982         return this.el.select('label.control-label',true).first();
16983     },
16984     /* depricated... */
16985     
16986     label: function()
16987     {
16988         return this.labelEl();
16989     },
16990     
16991     initEvents : function()
16992     {
16993 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16994         
16995         this.inputEl().on('click', this.onClick,  this);
16996         
16997         if (this.boxLabel) { 
16998             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
16999         }
17000         
17001         this.startValue = this.getValue();
17002         
17003         if(this.groupId){
17004             Roo.bootstrap.CheckBox.register(this);
17005         }
17006     },
17007     
17008     onClick : function()
17009     {   
17010         this.setChecked(!this.checked);
17011     },
17012     
17013     setChecked : function(state,suppressEvent)
17014     {
17015         this.startValue = this.getValue();
17016         
17017         if(this.inputType == 'radio'){
17018             
17019             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17020                 e.dom.checked = false;
17021             });
17022             
17023             this.inputEl().dom.checked = true;
17024             
17025             this.inputEl().dom.value = this.inputValue;
17026             
17027             if(suppressEvent !== true){
17028                 this.fireEvent('check', this, true);
17029             }
17030             
17031             this.validate();
17032             
17033             return;
17034         }
17035         
17036         this.checked = state;
17037         
17038         this.inputEl().dom.checked = state;
17039         
17040         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17041         
17042         if(suppressEvent !== true){
17043             this.fireEvent('check', this, state);
17044         }
17045         
17046         this.validate();
17047     },
17048     
17049     getValue : function()
17050     {
17051         if(this.inputType == 'radio'){
17052             return this.getGroupValue();
17053         }
17054         
17055         return this.inputEl().getValue();
17056         
17057     },
17058     
17059     getGroupValue : function()
17060     {
17061         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17062             return '';
17063         }
17064         
17065         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17066     },
17067     
17068     setValue : function(v,suppressEvent)
17069     {
17070         if(this.inputType == 'radio'){
17071             this.setGroupValue(v, suppressEvent);
17072             return;
17073         }
17074         
17075         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17076         
17077         this.validate();
17078     },
17079     
17080     setGroupValue : function(v, suppressEvent)
17081     {
17082         this.startValue = this.getValue();
17083         
17084         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17085             e.dom.checked = false;
17086             
17087             if(e.dom.value == v){
17088                 e.dom.checked = true;
17089             }
17090         });
17091         
17092         if(suppressEvent !== true){
17093             this.fireEvent('check', this, true);
17094         }
17095
17096         this.validate();
17097         
17098         return;
17099     },
17100     
17101     validate : function()
17102     {
17103         if(
17104                 this.disabled || 
17105                 (this.inputType == 'radio' && this.validateRadio()) ||
17106                 (this.inputType == 'checkbox' && this.validateCheckbox())
17107         ){
17108             this.markValid();
17109             return true;
17110         }
17111         
17112         this.markInvalid();
17113         return false;
17114     },
17115     
17116     validateRadio : function()
17117     {
17118         var valid = false;
17119         
17120         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17121             if(!e.dom.checked){
17122                 return;
17123             }
17124             
17125             valid = true;
17126             
17127             return false;
17128         });
17129         
17130         return valid;
17131     },
17132     
17133     validateCheckbox : function()
17134     {
17135         if(!this.groupId){
17136             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17137         }
17138         
17139         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17140         
17141         if(!group){
17142             return false;
17143         }
17144         
17145         var r = false;
17146         
17147         for(var i in group){
17148             if(r){
17149                 break;
17150             }
17151             
17152             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17153         }
17154         
17155         return r;
17156     },
17157     
17158     /**
17159      * Mark this field as valid
17160      */
17161     markValid : function()
17162     {
17163         if(this.allowBlank){
17164             return;
17165         }
17166         
17167         var _this = this;
17168         
17169         this.fireEvent('valid', this);
17170         
17171         if(this.inputType == 'radio'){
17172             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17173                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17174                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17175             });
17176             
17177             return;
17178         }
17179         
17180         if(!this.groupId){
17181             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17182             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17183             return;
17184         }
17185         
17186         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17187             
17188         if(!group){
17189             return;
17190         }
17191         
17192         for(var i in group){
17193             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17194             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17195         }
17196     },
17197     
17198      /**
17199      * Mark this field as invalid
17200      * @param {String} msg The validation message
17201      */
17202     markInvalid : function(msg)
17203     {
17204         if(this.allowBlank){
17205             return;
17206         }
17207         
17208         var _this = this;
17209         
17210         this.fireEvent('invalid', this, msg);
17211         
17212         if(this.inputType == 'radio'){
17213             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17214                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17215                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17216             });
17217             
17218             return;
17219         }
17220         
17221         if(!this.groupId){
17222             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17223             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17224             return;
17225         }
17226         
17227         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17228             
17229         if(!group){
17230             return;
17231         }
17232         
17233         for(var i in group){
17234             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17235             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17236         }
17237         
17238     }
17239     
17240 });
17241
17242 Roo.apply(Roo.bootstrap.CheckBox, {
17243     
17244     groups: {},
17245     
17246      /**
17247     * register a CheckBox Group
17248     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17249     */
17250     register : function(checkbox)
17251     {
17252         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17253             this.groups[checkbox.groupId] = {};
17254         }
17255         
17256         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17257             return;
17258         }
17259         
17260         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17261         
17262     },
17263     /**
17264     * fetch a CheckBox Group based on the group ID
17265     * @param {string} the group ID
17266     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17267     */
17268     get: function(groupId) {
17269         if (typeof(this.groups[groupId]) == 'undefined') {
17270             return false;
17271         }
17272         
17273         return this.groups[groupId] ;
17274     }
17275     
17276     
17277 });
17278 /*
17279  * - LGPL
17280  *
17281  * Radio
17282  *
17283  *
17284  * not inline
17285  *<div class="radio">
17286   <label>
17287     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17288     Option one is this and that&mdash;be sure to include why it's great
17289   </label>
17290 </div>
17291  *
17292  *
17293  *inline
17294  *<span>
17295  *<label class="radio-inline">fieldLabel</label>
17296  *<label class="radio-inline">
17297   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17298 </label>
17299 <span>
17300  * 
17301  * 
17302  */
17303
17304 /**
17305  * @class Roo.bootstrap.Radio
17306  * @extends Roo.bootstrap.CheckBox
17307  * Bootstrap Radio class
17308
17309  * @constructor
17310  * Create a new Radio
17311  * @param {Object} config The config object
17312  */
17313
17314 Roo.bootstrap.Radio = function(config){
17315     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17316    
17317 };
17318
17319 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17320     
17321     inputType: 'radio',
17322     inputValue: '',
17323     valueOff: '',
17324     
17325     getAutoCreate : function()
17326     {
17327         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17328         align = align || 'left'; // default...
17329         
17330         
17331         
17332         var id = Roo.id();
17333         
17334         var cfg = {
17335                 tag : this.inline ? 'span' : 'div',
17336                 cls : '',
17337                 cn : []
17338         };
17339         
17340         var inline = this.inline ? ' radio-inline' : '';
17341         
17342         var lbl = {
17343                 tag: 'label' ,
17344                 // does not need for, as we wrap the input with it..
17345                 'for' : id,
17346                 cls : 'control-label box-label' + inline,
17347                 cn : []
17348         };
17349         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17350         
17351         var fieldLabel = {
17352             tag: 'label' ,
17353             //cls : 'control-label' + inline,
17354             html : this.fieldLabel,
17355             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17356         };
17357         
17358  
17359         
17360         
17361         var input =  {
17362             tag: 'input',
17363             id : id,
17364             type : this.inputType,
17365             //value : (!this.checked) ? this.valueOff : this.inputValue,
17366             value : this.inputValue,
17367             cls : 'roo-radio',
17368             placeholder : this.placeholder || '' // ?? needed????
17369             
17370         };
17371         if (this.weight) { // Validity check?
17372             input.cls += " radio-" + this.weight;
17373         }
17374         if (this.disabled) {
17375             input.disabled=true;
17376         }
17377         
17378         if(this.checked){
17379             input.checked = this.checked;
17380         }
17381         
17382         if (this.name) {
17383             input.name = this.name;
17384         }
17385         
17386         if (this.size) {
17387             input.cls += ' input-' + this.size;
17388         }
17389         
17390         //?? can span's inline have a width??
17391         
17392         var settings=this;
17393         ['xs','sm','md','lg'].map(function(size){
17394             if (settings[size]) {
17395                 cfg.cls += ' col-' + size + '-' + settings[size];
17396             }
17397         });
17398         
17399         var inputblock = input;
17400         
17401         if (this.before || this.after) {
17402             
17403             inputblock = {
17404                 cls : 'input-group',
17405                 tag : 'span',
17406                 cn :  [] 
17407             };
17408             if (this.before) {
17409                 inputblock.cn.push({
17410                     tag :'span',
17411                     cls : 'input-group-addon',
17412                     html : this.before
17413                 });
17414             }
17415             inputblock.cn.push(input);
17416             if (this.after) {
17417                 inputblock.cn.push({
17418                     tag :'span',
17419                     cls : 'input-group-addon',
17420                     html : this.after
17421                 });
17422             }
17423             
17424         };
17425         
17426         
17427         if (this.fieldLabel && this.fieldLabel.length) {
17428             cfg.cn.push(fieldLabel);
17429         }
17430        
17431         // normal bootstrap puts the input inside the label.
17432         // however with our styled version - it has to go after the input.
17433        
17434         //lbl.cn.push(inputblock);
17435         
17436         var lblwrap =  {
17437             tag: 'span',
17438             cls: 'radio' + inline,
17439             cn: [
17440                 inputblock,
17441                 lbl
17442             ]
17443         };
17444         
17445         cfg.cn.push( lblwrap);
17446         
17447         if(this.boxLabel){
17448             lbl.cn.push({
17449                 tag: 'span',
17450                 html: this.boxLabel
17451             })
17452         }
17453          
17454         
17455         return cfg;
17456         
17457     },
17458     
17459     initEvents : function()
17460     {
17461 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17462         
17463         this.inputEl().on('click', this.onClick,  this);
17464         if (this.boxLabel) {
17465             Roo.log('find label')
17466             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17467         }
17468         
17469     },
17470     
17471     inputEl: function ()
17472     {
17473         return this.el.select('input.roo-radio',true).first();
17474     },
17475     onClick : function()
17476     {   
17477         Roo.log("click");
17478         this.setChecked(true);
17479     },
17480     
17481     setChecked : function(state,suppressEvent)
17482     {
17483         if(state){
17484             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17485                 v.dom.checked = false;
17486             });
17487         }
17488         Roo.log(this.inputEl().dom);
17489         this.checked = state;
17490         this.inputEl().dom.checked = state;
17491         
17492         if(suppressEvent !== true){
17493             this.fireEvent('check', this, state);
17494         }
17495         
17496         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17497         
17498     },
17499     
17500     getGroupValue : function()
17501     {
17502         var value = '';
17503         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17504             if(v.dom.checked == true){
17505                 value = v.dom.value;
17506             }
17507         });
17508         
17509         return value;
17510     },
17511     
17512     /**
17513      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17514      * @return {Mixed} value The field value
17515      */
17516     getValue : function(){
17517         return this.getGroupValue();
17518     }
17519     
17520 });
17521
17522  
17523 //<script type="text/javascript">
17524
17525 /*
17526  * Based  Ext JS Library 1.1.1
17527  * Copyright(c) 2006-2007, Ext JS, LLC.
17528  * LGPL
17529  *
17530  */
17531  
17532 /**
17533  * @class Roo.HtmlEditorCore
17534  * @extends Roo.Component
17535  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17536  *
17537  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17538  */
17539
17540 Roo.HtmlEditorCore = function(config){
17541     
17542     
17543     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17544     
17545     
17546     this.addEvents({
17547         /**
17548          * @event initialize
17549          * Fires when the editor is fully initialized (including the iframe)
17550          * @param {Roo.HtmlEditorCore} this
17551          */
17552         initialize: true,
17553         /**
17554          * @event activate
17555          * Fires when the editor is first receives the focus. Any insertion must wait
17556          * until after this event.
17557          * @param {Roo.HtmlEditorCore} this
17558          */
17559         activate: true,
17560          /**
17561          * @event beforesync
17562          * Fires before the textarea is updated with content from the editor iframe. Return false
17563          * to cancel the sync.
17564          * @param {Roo.HtmlEditorCore} this
17565          * @param {String} html
17566          */
17567         beforesync: true,
17568          /**
17569          * @event beforepush
17570          * Fires before the iframe editor is updated with content from the textarea. Return false
17571          * to cancel the push.
17572          * @param {Roo.HtmlEditorCore} this
17573          * @param {String} html
17574          */
17575         beforepush: true,
17576          /**
17577          * @event sync
17578          * Fires when the textarea is updated with content from the editor iframe.
17579          * @param {Roo.HtmlEditorCore} this
17580          * @param {String} html
17581          */
17582         sync: true,
17583          /**
17584          * @event push
17585          * Fires when the iframe editor is updated with content from the textarea.
17586          * @param {Roo.HtmlEditorCore} this
17587          * @param {String} html
17588          */
17589         push: true,
17590         
17591         /**
17592          * @event editorevent
17593          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17594          * @param {Roo.HtmlEditorCore} this
17595          */
17596         editorevent: true
17597         
17598     });
17599     
17600     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
17601     
17602     // defaults : white / black...
17603     this.applyBlacklists();
17604     
17605     
17606     
17607 };
17608
17609
17610 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
17611
17612
17613      /**
17614      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
17615      */
17616     
17617     owner : false,
17618     
17619      /**
17620      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17621      *                        Roo.resizable.
17622      */
17623     resizable : false,
17624      /**
17625      * @cfg {Number} height (in pixels)
17626      */   
17627     height: 300,
17628    /**
17629      * @cfg {Number} width (in pixels)
17630      */   
17631     width: 500,
17632     
17633     /**
17634      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17635      * 
17636      */
17637     stylesheets: false,
17638     
17639     // id of frame..
17640     frameId: false,
17641     
17642     // private properties
17643     validationEvent : false,
17644     deferHeight: true,
17645     initialized : false,
17646     activated : false,
17647     sourceEditMode : false,
17648     onFocus : Roo.emptyFn,
17649     iframePad:3,
17650     hideMode:'offsets',
17651     
17652     clearUp: true,
17653     
17654     // blacklist + whitelisted elements..
17655     black: false,
17656     white: false,
17657      
17658     
17659
17660     /**
17661      * Protected method that will not generally be called directly. It
17662      * is called when the editor initializes the iframe with HTML contents. Override this method if you
17663      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
17664      */
17665     getDocMarkup : function(){
17666         // body styles..
17667         var st = '';
17668         
17669         // inherit styels from page...?? 
17670         if (this.stylesheets === false) {
17671             
17672             Roo.get(document.head).select('style').each(function(node) {
17673                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17674             });
17675             
17676             Roo.get(document.head).select('link').each(function(node) { 
17677                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17678             });
17679             
17680         } else if (!this.stylesheets.length) {
17681                 // simple..
17682                 st = '<style type="text/css">' +
17683                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17684                    '</style>';
17685         } else { 
17686             
17687         }
17688         
17689         st +=  '<style type="text/css">' +
17690             'IMG { cursor: pointer } ' +
17691         '</style>';
17692
17693         
17694         return '<html><head>' + st  +
17695             //<style type="text/css">' +
17696             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17697             //'</style>' +
17698             ' </head><body class="roo-htmleditor-body"></body></html>';
17699     },
17700
17701     // private
17702     onRender : function(ct, position)
17703     {
17704         var _t = this;
17705         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
17706         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
17707         
17708         
17709         this.el.dom.style.border = '0 none';
17710         this.el.dom.setAttribute('tabIndex', -1);
17711         this.el.addClass('x-hidden hide');
17712         
17713         
17714         
17715         if(Roo.isIE){ // fix IE 1px bogus margin
17716             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
17717         }
17718        
17719         
17720         this.frameId = Roo.id();
17721         
17722          
17723         
17724         var iframe = this.owner.wrap.createChild({
17725             tag: 'iframe',
17726             cls: 'form-control', // bootstrap..
17727             id: this.frameId,
17728             name: this.frameId,
17729             frameBorder : 'no',
17730             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
17731         }, this.el
17732         );
17733         
17734         
17735         this.iframe = iframe.dom;
17736
17737          this.assignDocWin();
17738         
17739         this.doc.designMode = 'on';
17740        
17741         this.doc.open();
17742         this.doc.write(this.getDocMarkup());
17743         this.doc.close();
17744
17745         
17746         var task = { // must defer to wait for browser to be ready
17747             run : function(){
17748                 //console.log("run task?" + this.doc.readyState);
17749                 this.assignDocWin();
17750                 if(this.doc.body || this.doc.readyState == 'complete'){
17751                     try {
17752                         this.doc.designMode="on";
17753                     } catch (e) {
17754                         return;
17755                     }
17756                     Roo.TaskMgr.stop(task);
17757                     this.initEditor.defer(10, this);
17758                 }
17759             },
17760             interval : 10,
17761             duration: 10000,
17762             scope: this
17763         };
17764         Roo.TaskMgr.start(task);
17765
17766     },
17767
17768     // private
17769     onResize : function(w, h)
17770     {
17771          Roo.log('resize: ' +w + ',' + h );
17772         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
17773         if(!this.iframe){
17774             return;
17775         }
17776         if(typeof w == 'number'){
17777             
17778             this.iframe.style.width = w + 'px';
17779         }
17780         if(typeof h == 'number'){
17781             
17782             this.iframe.style.height = h + 'px';
17783             if(this.doc){
17784                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
17785             }
17786         }
17787         
17788     },
17789
17790     /**
17791      * Toggles the editor between standard and source edit mode.
17792      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17793      */
17794     toggleSourceEdit : function(sourceEditMode){
17795         
17796         this.sourceEditMode = sourceEditMode === true;
17797         
17798         if(this.sourceEditMode){
17799  
17800             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
17801             
17802         }else{
17803             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
17804             //this.iframe.className = '';
17805             this.deferFocus();
17806         }
17807         //this.setSize(this.owner.wrap.getSize());
17808         //this.fireEvent('editmodechange', this, this.sourceEditMode);
17809     },
17810
17811     
17812   
17813
17814     /**
17815      * Protected method that will not generally be called directly. If you need/want
17816      * custom HTML cleanup, this is the method you should override.
17817      * @param {String} html The HTML to be cleaned
17818      * return {String} The cleaned HTML
17819      */
17820     cleanHtml : function(html){
17821         html = String(html);
17822         if(html.length > 5){
17823             if(Roo.isSafari){ // strip safari nonsense
17824                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
17825             }
17826         }
17827         if(html == '&nbsp;'){
17828             html = '';
17829         }
17830         return html;
17831     },
17832
17833     /**
17834      * HTML Editor -> Textarea
17835      * Protected method that will not generally be called directly. Syncs the contents
17836      * of the editor iframe with the textarea.
17837      */
17838     syncValue : function(){
17839         if(this.initialized){
17840             var bd = (this.doc.body || this.doc.documentElement);
17841             //this.cleanUpPaste(); -- this is done else where and causes havoc..
17842             var html = bd.innerHTML;
17843             if(Roo.isSafari){
17844                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
17845                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
17846                 if(m && m[1]){
17847                     html = '<div style="'+m[0]+'">' + html + '</div>';
17848                 }
17849             }
17850             html = this.cleanHtml(html);
17851             // fix up the special chars.. normaly like back quotes in word...
17852             // however we do not want to do this with chinese..
17853             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
17854                 var cc = b.charCodeAt();
17855                 if (
17856                     (cc >= 0x4E00 && cc < 0xA000 ) ||
17857                     (cc >= 0x3400 && cc < 0x4E00 ) ||
17858                     (cc >= 0xf900 && cc < 0xfb00 )
17859                 ) {
17860                         return b;
17861                 }
17862                 return "&#"+cc+";" 
17863             });
17864             if(this.owner.fireEvent('beforesync', this, html) !== false){
17865                 this.el.dom.value = html;
17866                 this.owner.fireEvent('sync', this, html);
17867             }
17868         }
17869     },
17870
17871     /**
17872      * Protected method that will not generally be called directly. Pushes the value of the textarea
17873      * into the iframe editor.
17874      */
17875     pushValue : function(){
17876         if(this.initialized){
17877             var v = this.el.dom.value.trim();
17878             
17879 //            if(v.length < 1){
17880 //                v = '&#160;';
17881 //            }
17882             
17883             if(this.owner.fireEvent('beforepush', this, v) !== false){
17884                 var d = (this.doc.body || this.doc.documentElement);
17885                 d.innerHTML = v;
17886                 this.cleanUpPaste();
17887                 this.el.dom.value = d.innerHTML;
17888                 this.owner.fireEvent('push', this, v);
17889             }
17890         }
17891     },
17892
17893     // private
17894     deferFocus : function(){
17895         this.focus.defer(10, this);
17896     },
17897
17898     // doc'ed in Field
17899     focus : function(){
17900         if(this.win && !this.sourceEditMode){
17901             this.win.focus();
17902         }else{
17903             this.el.focus();
17904         }
17905     },
17906     
17907     assignDocWin: function()
17908     {
17909         var iframe = this.iframe;
17910         
17911          if(Roo.isIE){
17912             this.doc = iframe.contentWindow.document;
17913             this.win = iframe.contentWindow;
17914         } else {
17915 //            if (!Roo.get(this.frameId)) {
17916 //                return;
17917 //            }
17918 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
17919 //            this.win = Roo.get(this.frameId).dom.contentWindow;
17920             
17921             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
17922                 return;
17923             }
17924             
17925             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
17926             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
17927         }
17928     },
17929     
17930     // private
17931     initEditor : function(){
17932         //console.log("INIT EDITOR");
17933         this.assignDocWin();
17934         
17935         
17936         
17937         this.doc.designMode="on";
17938         this.doc.open();
17939         this.doc.write(this.getDocMarkup());
17940         this.doc.close();
17941         
17942         var dbody = (this.doc.body || this.doc.documentElement);
17943         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
17944         // this copies styles from the containing element into thsi one..
17945         // not sure why we need all of this..
17946         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
17947         
17948         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
17949         //ss['background-attachment'] = 'fixed'; // w3c
17950         dbody.bgProperties = 'fixed'; // ie
17951         //Roo.DomHelper.applyStyles(dbody, ss);
17952         Roo.EventManager.on(this.doc, {
17953             //'mousedown': this.onEditorEvent,
17954             'mouseup': this.onEditorEvent,
17955             'dblclick': this.onEditorEvent,
17956             'click': this.onEditorEvent,
17957             'keyup': this.onEditorEvent,
17958             buffer:100,
17959             scope: this
17960         });
17961         if(Roo.isGecko){
17962             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
17963         }
17964         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
17965             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
17966         }
17967         this.initialized = true;
17968
17969         this.owner.fireEvent('initialize', this);
17970         this.pushValue();
17971     },
17972
17973     // private
17974     onDestroy : function(){
17975         
17976         
17977         
17978         if(this.rendered){
17979             
17980             //for (var i =0; i < this.toolbars.length;i++) {
17981             //    // fixme - ask toolbars for heights?
17982             //    this.toolbars[i].onDestroy();
17983            // }
17984             
17985             //this.wrap.dom.innerHTML = '';
17986             //this.wrap.remove();
17987         }
17988     },
17989
17990     // private
17991     onFirstFocus : function(){
17992         
17993         this.assignDocWin();
17994         
17995         
17996         this.activated = true;
17997          
17998     
17999         if(Roo.isGecko){ // prevent silly gecko errors
18000             this.win.focus();
18001             var s = this.win.getSelection();
18002             if(!s.focusNode || s.focusNode.nodeType != 3){
18003                 var r = s.getRangeAt(0);
18004                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18005                 r.collapse(true);
18006                 this.deferFocus();
18007             }
18008             try{
18009                 this.execCmd('useCSS', true);
18010                 this.execCmd('styleWithCSS', false);
18011             }catch(e){}
18012         }
18013         this.owner.fireEvent('activate', this);
18014     },
18015
18016     // private
18017     adjustFont: function(btn){
18018         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18019         //if(Roo.isSafari){ // safari
18020         //    adjust *= 2;
18021        // }
18022         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18023         if(Roo.isSafari){ // safari
18024             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18025             v =  (v < 10) ? 10 : v;
18026             v =  (v > 48) ? 48 : v;
18027             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18028             
18029         }
18030         
18031         
18032         v = Math.max(1, v+adjust);
18033         
18034         this.execCmd('FontSize', v  );
18035     },
18036
18037     onEditorEvent : function(e){
18038         this.owner.fireEvent('editorevent', this, e);
18039       //  this.updateToolbar();
18040         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18041     },
18042
18043     insertTag : function(tg)
18044     {
18045         // could be a bit smarter... -> wrap the current selected tRoo..
18046         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18047             
18048             range = this.createRange(this.getSelection());
18049             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18050             wrappingNode.appendChild(range.extractContents());
18051             range.insertNode(wrappingNode);
18052
18053             return;
18054             
18055             
18056             
18057         }
18058         this.execCmd("formatblock",   tg);
18059         
18060     },
18061     
18062     insertText : function(txt)
18063     {
18064         
18065         
18066         var range = this.createRange();
18067         range.deleteContents();
18068                //alert(Sender.getAttribute('label'));
18069                
18070         range.insertNode(this.doc.createTextNode(txt));
18071     } ,
18072     
18073      
18074
18075     /**
18076      * Executes a Midas editor command on the editor document and performs necessary focus and
18077      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18078      * @param {String} cmd The Midas command
18079      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18080      */
18081     relayCmd : function(cmd, value){
18082         this.win.focus();
18083         this.execCmd(cmd, value);
18084         this.owner.fireEvent('editorevent', this);
18085         //this.updateToolbar();
18086         this.owner.deferFocus();
18087     },
18088
18089     /**
18090      * Executes a Midas editor command directly on the editor document.
18091      * For visual commands, you should use {@link #relayCmd} instead.
18092      * <b>This should only be called after the editor is initialized.</b>
18093      * @param {String} cmd The Midas command
18094      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18095      */
18096     execCmd : function(cmd, value){
18097         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18098         this.syncValue();
18099     },
18100  
18101  
18102    
18103     /**
18104      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18105      * to insert tRoo.
18106      * @param {String} text | dom node.. 
18107      */
18108     insertAtCursor : function(text)
18109     {
18110         
18111         
18112         
18113         if(!this.activated){
18114             return;
18115         }
18116         /*
18117         if(Roo.isIE){
18118             this.win.focus();
18119             var r = this.doc.selection.createRange();
18120             if(r){
18121                 r.collapse(true);
18122                 r.pasteHTML(text);
18123                 this.syncValue();
18124                 this.deferFocus();
18125             
18126             }
18127             return;
18128         }
18129         */
18130         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18131             this.win.focus();
18132             
18133             
18134             // from jquery ui (MIT licenced)
18135             var range, node;
18136             var win = this.win;
18137             
18138             if (win.getSelection && win.getSelection().getRangeAt) {
18139                 range = win.getSelection().getRangeAt(0);
18140                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18141                 range.insertNode(node);
18142             } else if (win.document.selection && win.document.selection.createRange) {
18143                 // no firefox support
18144                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18145                 win.document.selection.createRange().pasteHTML(txt);
18146             } else {
18147                 // no firefox support
18148                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18149                 this.execCmd('InsertHTML', txt);
18150             } 
18151             
18152             this.syncValue();
18153             
18154             this.deferFocus();
18155         }
18156     },
18157  // private
18158     mozKeyPress : function(e){
18159         if(e.ctrlKey){
18160             var c = e.getCharCode(), cmd;
18161           
18162             if(c > 0){
18163                 c = String.fromCharCode(c).toLowerCase();
18164                 switch(c){
18165                     case 'b':
18166                         cmd = 'bold';
18167                         break;
18168                     case 'i':
18169                         cmd = 'italic';
18170                         break;
18171                     
18172                     case 'u':
18173                         cmd = 'underline';
18174                         break;
18175                     
18176                     case 'v':
18177                         this.cleanUpPaste.defer(100, this);
18178                         return;
18179                         
18180                 }
18181                 if(cmd){
18182                     this.win.focus();
18183                     this.execCmd(cmd);
18184                     this.deferFocus();
18185                     e.preventDefault();
18186                 }
18187                 
18188             }
18189         }
18190     },
18191
18192     // private
18193     fixKeys : function(){ // load time branching for fastest keydown performance
18194         if(Roo.isIE){
18195             return function(e){
18196                 var k = e.getKey(), r;
18197                 if(k == e.TAB){
18198                     e.stopEvent();
18199                     r = this.doc.selection.createRange();
18200                     if(r){
18201                         r.collapse(true);
18202                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18203                         this.deferFocus();
18204                     }
18205                     return;
18206                 }
18207                 
18208                 if(k == e.ENTER){
18209                     r = this.doc.selection.createRange();
18210                     if(r){
18211                         var target = r.parentElement();
18212                         if(!target || target.tagName.toLowerCase() != 'li'){
18213                             e.stopEvent();
18214                             r.pasteHTML('<br />');
18215                             r.collapse(false);
18216                             r.select();
18217                         }
18218                     }
18219                 }
18220                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18221                     this.cleanUpPaste.defer(100, this);
18222                     return;
18223                 }
18224                 
18225                 
18226             };
18227         }else if(Roo.isOpera){
18228             return function(e){
18229                 var k = e.getKey();
18230                 if(k == e.TAB){
18231                     e.stopEvent();
18232                     this.win.focus();
18233                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18234                     this.deferFocus();
18235                 }
18236                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18237                     this.cleanUpPaste.defer(100, this);
18238                     return;
18239                 }
18240                 
18241             };
18242         }else if(Roo.isSafari){
18243             return function(e){
18244                 var k = e.getKey();
18245                 
18246                 if(k == e.TAB){
18247                     e.stopEvent();
18248                     this.execCmd('InsertText','\t');
18249                     this.deferFocus();
18250                     return;
18251                 }
18252                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18253                     this.cleanUpPaste.defer(100, this);
18254                     return;
18255                 }
18256                 
18257              };
18258         }
18259     }(),
18260     
18261     getAllAncestors: function()
18262     {
18263         var p = this.getSelectedNode();
18264         var a = [];
18265         if (!p) {
18266             a.push(p); // push blank onto stack..
18267             p = this.getParentElement();
18268         }
18269         
18270         
18271         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18272             a.push(p);
18273             p = p.parentNode;
18274         }
18275         a.push(this.doc.body);
18276         return a;
18277     },
18278     lastSel : false,
18279     lastSelNode : false,
18280     
18281     
18282     getSelection : function() 
18283     {
18284         this.assignDocWin();
18285         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18286     },
18287     
18288     getSelectedNode: function() 
18289     {
18290         // this may only work on Gecko!!!
18291         
18292         // should we cache this!!!!
18293         
18294         
18295         
18296          
18297         var range = this.createRange(this.getSelection()).cloneRange();
18298         
18299         if (Roo.isIE) {
18300             var parent = range.parentElement();
18301             while (true) {
18302                 var testRange = range.duplicate();
18303                 testRange.moveToElementText(parent);
18304                 if (testRange.inRange(range)) {
18305                     break;
18306                 }
18307                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18308                     break;
18309                 }
18310                 parent = parent.parentElement;
18311             }
18312             return parent;
18313         }
18314         
18315         // is ancestor a text element.
18316         var ac =  range.commonAncestorContainer;
18317         if (ac.nodeType == 3) {
18318             ac = ac.parentNode;
18319         }
18320         
18321         var ar = ac.childNodes;
18322          
18323         var nodes = [];
18324         var other_nodes = [];
18325         var has_other_nodes = false;
18326         for (var i=0;i<ar.length;i++) {
18327             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18328                 continue;
18329             }
18330             // fullly contained node.
18331             
18332             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18333                 nodes.push(ar[i]);
18334                 continue;
18335             }
18336             
18337             // probably selected..
18338             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18339                 other_nodes.push(ar[i]);
18340                 continue;
18341             }
18342             // outer..
18343             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18344                 continue;
18345             }
18346             
18347             
18348             has_other_nodes = true;
18349         }
18350         if (!nodes.length && other_nodes.length) {
18351             nodes= other_nodes;
18352         }
18353         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18354             return false;
18355         }
18356         
18357         return nodes[0];
18358     },
18359     createRange: function(sel)
18360     {
18361         // this has strange effects when using with 
18362         // top toolbar - not sure if it's a great idea.
18363         //this.editor.contentWindow.focus();
18364         if (typeof sel != "undefined") {
18365             try {
18366                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18367             } catch(e) {
18368                 return this.doc.createRange();
18369             }
18370         } else {
18371             return this.doc.createRange();
18372         }
18373     },
18374     getParentElement: function()
18375     {
18376         
18377         this.assignDocWin();
18378         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18379         
18380         var range = this.createRange(sel);
18381          
18382         try {
18383             var p = range.commonAncestorContainer;
18384             while (p.nodeType == 3) { // text node
18385                 p = p.parentNode;
18386             }
18387             return p;
18388         } catch (e) {
18389             return null;
18390         }
18391     
18392     },
18393     /***
18394      *
18395      * Range intersection.. the hard stuff...
18396      *  '-1' = before
18397      *  '0' = hits..
18398      *  '1' = after.
18399      *         [ -- selected range --- ]
18400      *   [fail]                        [fail]
18401      *
18402      *    basically..
18403      *      if end is before start or  hits it. fail.
18404      *      if start is after end or hits it fail.
18405      *
18406      *   if either hits (but other is outside. - then it's not 
18407      *   
18408      *    
18409      **/
18410     
18411     
18412     // @see http://www.thismuchiknow.co.uk/?p=64.
18413     rangeIntersectsNode : function(range, node)
18414     {
18415         var nodeRange = node.ownerDocument.createRange();
18416         try {
18417             nodeRange.selectNode(node);
18418         } catch (e) {
18419             nodeRange.selectNodeContents(node);
18420         }
18421     
18422         var rangeStartRange = range.cloneRange();
18423         rangeStartRange.collapse(true);
18424     
18425         var rangeEndRange = range.cloneRange();
18426         rangeEndRange.collapse(false);
18427     
18428         var nodeStartRange = nodeRange.cloneRange();
18429         nodeStartRange.collapse(true);
18430     
18431         var nodeEndRange = nodeRange.cloneRange();
18432         nodeEndRange.collapse(false);
18433     
18434         return rangeStartRange.compareBoundaryPoints(
18435                  Range.START_TO_START, nodeEndRange) == -1 &&
18436                rangeEndRange.compareBoundaryPoints(
18437                  Range.START_TO_START, nodeStartRange) == 1;
18438         
18439          
18440     },
18441     rangeCompareNode : function(range, node)
18442     {
18443         var nodeRange = node.ownerDocument.createRange();
18444         try {
18445             nodeRange.selectNode(node);
18446         } catch (e) {
18447             nodeRange.selectNodeContents(node);
18448         }
18449         
18450         
18451         range.collapse(true);
18452     
18453         nodeRange.collapse(true);
18454      
18455         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18456         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18457          
18458         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18459         
18460         var nodeIsBefore   =  ss == 1;
18461         var nodeIsAfter    = ee == -1;
18462         
18463         if (nodeIsBefore && nodeIsAfter)
18464             return 0; // outer
18465         if (!nodeIsBefore && nodeIsAfter)
18466             return 1; //right trailed.
18467         
18468         if (nodeIsBefore && !nodeIsAfter)
18469             return 2;  // left trailed.
18470         // fully contined.
18471         return 3;
18472     },
18473
18474     // private? - in a new class?
18475     cleanUpPaste :  function()
18476     {
18477         // cleans up the whole document..
18478         Roo.log('cleanuppaste');
18479         
18480         this.cleanUpChildren(this.doc.body);
18481         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18482         if (clean != this.doc.body.innerHTML) {
18483             this.doc.body.innerHTML = clean;
18484         }
18485         
18486     },
18487     
18488     cleanWordChars : function(input) {// change the chars to hex code
18489         var he = Roo.HtmlEditorCore;
18490         
18491         var output = input;
18492         Roo.each(he.swapCodes, function(sw) { 
18493             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18494             
18495             output = output.replace(swapper, sw[1]);
18496         });
18497         
18498         return output;
18499     },
18500     
18501     
18502     cleanUpChildren : function (n)
18503     {
18504         if (!n.childNodes.length) {
18505             return;
18506         }
18507         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18508            this.cleanUpChild(n.childNodes[i]);
18509         }
18510     },
18511     
18512     
18513         
18514     
18515     cleanUpChild : function (node)
18516     {
18517         var ed = this;
18518         //console.log(node);
18519         if (node.nodeName == "#text") {
18520             // clean up silly Windows -- stuff?
18521             return; 
18522         }
18523         if (node.nodeName == "#comment") {
18524             node.parentNode.removeChild(node);
18525             // clean up silly Windows -- stuff?
18526             return; 
18527         }
18528         var lcname = node.tagName.toLowerCase();
18529         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18530         // whitelist of tags..
18531         
18532         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18533             // remove node.
18534             node.parentNode.removeChild(node);
18535             return;
18536             
18537         }
18538         
18539         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18540         
18541         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18542         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18543         
18544         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18545         //    remove_keep_children = true;
18546         //}
18547         
18548         if (remove_keep_children) {
18549             this.cleanUpChildren(node);
18550             // inserts everything just before this node...
18551             while (node.childNodes.length) {
18552                 var cn = node.childNodes[0];
18553                 node.removeChild(cn);
18554                 node.parentNode.insertBefore(cn, node);
18555             }
18556             node.parentNode.removeChild(node);
18557             return;
18558         }
18559         
18560         if (!node.attributes || !node.attributes.length) {
18561             this.cleanUpChildren(node);
18562             return;
18563         }
18564         
18565         function cleanAttr(n,v)
18566         {
18567             
18568             if (v.match(/^\./) || v.match(/^\//)) {
18569                 return;
18570             }
18571             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18572                 return;
18573             }
18574             if (v.match(/^#/)) {
18575                 return;
18576             }
18577 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18578             node.removeAttribute(n);
18579             
18580         }
18581         
18582         var cwhite = this.cwhite;
18583         var cblack = this.cblack;
18584             
18585         function cleanStyle(n,v)
18586         {
18587             if (v.match(/expression/)) { //XSS?? should we even bother..
18588                 node.removeAttribute(n);
18589                 return;
18590             }
18591             
18592             var parts = v.split(/;/);
18593             var clean = [];
18594             
18595             Roo.each(parts, function(p) {
18596                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
18597                 if (!p.length) {
18598                     return true;
18599                 }
18600                 var l = p.split(':').shift().replace(/\s+/g,'');
18601                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
18602                 
18603                 if ( cwhite.length && cblack.indexOf(l) > -1) {
18604 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18605                     //node.removeAttribute(n);
18606                     return true;
18607                 }
18608                 //Roo.log()
18609                 // only allow 'c whitelisted system attributes'
18610                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
18611 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18612                     //node.removeAttribute(n);
18613                     return true;
18614                 }
18615                 
18616                 
18617                  
18618                 
18619                 clean.push(p);
18620                 return true;
18621             });
18622             if (clean.length) { 
18623                 node.setAttribute(n, clean.join(';'));
18624             } else {
18625                 node.removeAttribute(n);
18626             }
18627             
18628         }
18629         
18630         
18631         for (var i = node.attributes.length-1; i > -1 ; i--) {
18632             var a = node.attributes[i];
18633             //console.log(a);
18634             
18635             if (a.name.toLowerCase().substr(0,2)=='on')  {
18636                 node.removeAttribute(a.name);
18637                 continue;
18638             }
18639             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
18640                 node.removeAttribute(a.name);
18641                 continue;
18642             }
18643             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
18644                 cleanAttr(a.name,a.value); // fixme..
18645                 continue;
18646             }
18647             if (a.name == 'style') {
18648                 cleanStyle(a.name,a.value);
18649                 continue;
18650             }
18651             /// clean up MS crap..
18652             // tecnically this should be a list of valid class'es..
18653             
18654             
18655             if (a.name == 'class') {
18656                 if (a.value.match(/^Mso/)) {
18657                     node.className = '';
18658                 }
18659                 
18660                 if (a.value.match(/body/)) {
18661                     node.className = '';
18662                 }
18663                 continue;
18664             }
18665             
18666             // style cleanup!?
18667             // class cleanup?
18668             
18669         }
18670         
18671         
18672         this.cleanUpChildren(node);
18673         
18674         
18675     },
18676     /**
18677      * Clean up MS wordisms...
18678      */
18679     cleanWord : function(node)
18680     {
18681         var _t = this;
18682         var cleanWordChildren = function()
18683         {
18684             if (!node.childNodes.length) {
18685                 return;
18686             }
18687             for (var i = node.childNodes.length-1; i > -1 ; i--) {
18688                _t.cleanWord(node.childNodes[i]);
18689             }
18690         }
18691         
18692         
18693         if (!node) {
18694             this.cleanWord(this.doc.body);
18695             return;
18696         }
18697         if (node.nodeName == "#text") {
18698             // clean up silly Windows -- stuff?
18699             return; 
18700         }
18701         if (node.nodeName == "#comment") {
18702             node.parentNode.removeChild(node);
18703             // clean up silly Windows -- stuff?
18704             return; 
18705         }
18706         
18707         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
18708             node.parentNode.removeChild(node);
18709             return;
18710         }
18711         
18712         // remove - but keep children..
18713         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
18714             while (node.childNodes.length) {
18715                 var cn = node.childNodes[0];
18716                 node.removeChild(cn);
18717                 node.parentNode.insertBefore(cn, node);
18718             }
18719             node.parentNode.removeChild(node);
18720             cleanWordChildren();
18721             return;
18722         }
18723         // clean styles
18724         if (node.className.length) {
18725             
18726             var cn = node.className.split(/\W+/);
18727             var cna = [];
18728             Roo.each(cn, function(cls) {
18729                 if (cls.match(/Mso[a-zA-Z]+/)) {
18730                     return;
18731                 }
18732                 cna.push(cls);
18733             });
18734             node.className = cna.length ? cna.join(' ') : '';
18735             if (!cna.length) {
18736                 node.removeAttribute("class");
18737             }
18738         }
18739         
18740         if (node.hasAttribute("lang")) {
18741             node.removeAttribute("lang");
18742         }
18743         
18744         if (node.hasAttribute("style")) {
18745             
18746             var styles = node.getAttribute("style").split(";");
18747             var nstyle = [];
18748             Roo.each(styles, function(s) {
18749                 if (!s.match(/:/)) {
18750                     return;
18751                 }
18752                 var kv = s.split(":");
18753                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
18754                     return;
18755                 }
18756                 // what ever is left... we allow.
18757                 nstyle.push(s);
18758             });
18759             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
18760             if (!nstyle.length) {
18761                 node.removeAttribute('style');
18762             }
18763         }
18764         
18765         cleanWordChildren();
18766         
18767         
18768     },
18769     domToHTML : function(currentElement, depth, nopadtext) {
18770         
18771         depth = depth || 0;
18772         nopadtext = nopadtext || false;
18773     
18774         if (!currentElement) {
18775             return this.domToHTML(this.doc.body);
18776         }
18777         
18778         //Roo.log(currentElement);
18779         var j;
18780         var allText = false;
18781         var nodeName = currentElement.nodeName;
18782         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
18783         
18784         if  (nodeName == '#text') {
18785             
18786             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
18787         }
18788         
18789         
18790         var ret = '';
18791         if (nodeName != 'BODY') {
18792              
18793             var i = 0;
18794             // Prints the node tagName, such as <A>, <IMG>, etc
18795             if (tagName) {
18796                 var attr = [];
18797                 for(i = 0; i < currentElement.attributes.length;i++) {
18798                     // quoting?
18799                     var aname = currentElement.attributes.item(i).name;
18800                     if (!currentElement.attributes.item(i).value.length) {
18801                         continue;
18802                     }
18803                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
18804                 }
18805                 
18806                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
18807             } 
18808             else {
18809                 
18810                 // eack
18811             }
18812         } else {
18813             tagName = false;
18814         }
18815         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
18816             return ret;
18817         }
18818         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
18819             nopadtext = true;
18820         }
18821         
18822         
18823         // Traverse the tree
18824         i = 0;
18825         var currentElementChild = currentElement.childNodes.item(i);
18826         var allText = true;
18827         var innerHTML  = '';
18828         lastnode = '';
18829         while (currentElementChild) {
18830             // Formatting code (indent the tree so it looks nice on the screen)
18831             var nopad = nopadtext;
18832             if (lastnode == 'SPAN') {
18833                 nopad  = true;
18834             }
18835             // text
18836             if  (currentElementChild.nodeName == '#text') {
18837                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
18838                 toadd = nopadtext ? toadd : toadd.trim();
18839                 if (!nopad && toadd.length > 80) {
18840                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
18841                 }
18842                 innerHTML  += toadd;
18843                 
18844                 i++;
18845                 currentElementChild = currentElement.childNodes.item(i);
18846                 lastNode = '';
18847                 continue;
18848             }
18849             allText = false;
18850             
18851             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
18852                 
18853             // Recursively traverse the tree structure of the child node
18854             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
18855             lastnode = currentElementChild.nodeName;
18856             i++;
18857             currentElementChild=currentElement.childNodes.item(i);
18858         }
18859         
18860         ret += innerHTML;
18861         
18862         if (!allText) {
18863                 // The remaining code is mostly for formatting the tree
18864             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
18865         }
18866         
18867         
18868         if (tagName) {
18869             ret+= "</"+tagName+">";
18870         }
18871         return ret;
18872         
18873     },
18874         
18875     applyBlacklists : function()
18876     {
18877         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
18878         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
18879         
18880         this.white = [];
18881         this.black = [];
18882         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
18883             if (b.indexOf(tag) > -1) {
18884                 return;
18885             }
18886             this.white.push(tag);
18887             
18888         }, this);
18889         
18890         Roo.each(w, function(tag) {
18891             if (b.indexOf(tag) > -1) {
18892                 return;
18893             }
18894             if (this.white.indexOf(tag) > -1) {
18895                 return;
18896             }
18897             this.white.push(tag);
18898             
18899         }, this);
18900         
18901         
18902         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
18903             if (w.indexOf(tag) > -1) {
18904                 return;
18905             }
18906             this.black.push(tag);
18907             
18908         }, this);
18909         
18910         Roo.each(b, function(tag) {
18911             if (w.indexOf(tag) > -1) {
18912                 return;
18913             }
18914             if (this.black.indexOf(tag) > -1) {
18915                 return;
18916             }
18917             this.black.push(tag);
18918             
18919         }, this);
18920         
18921         
18922         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
18923         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
18924         
18925         this.cwhite = [];
18926         this.cblack = [];
18927         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
18928             if (b.indexOf(tag) > -1) {
18929                 return;
18930             }
18931             this.cwhite.push(tag);
18932             
18933         }, this);
18934         
18935         Roo.each(w, function(tag) {
18936             if (b.indexOf(tag) > -1) {
18937                 return;
18938             }
18939             if (this.cwhite.indexOf(tag) > -1) {
18940                 return;
18941             }
18942             this.cwhite.push(tag);
18943             
18944         }, this);
18945         
18946         
18947         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
18948             if (w.indexOf(tag) > -1) {
18949                 return;
18950             }
18951             this.cblack.push(tag);
18952             
18953         }, this);
18954         
18955         Roo.each(b, function(tag) {
18956             if (w.indexOf(tag) > -1) {
18957                 return;
18958             }
18959             if (this.cblack.indexOf(tag) > -1) {
18960                 return;
18961             }
18962             this.cblack.push(tag);
18963             
18964         }, this);
18965     },
18966     
18967     setStylesheets : function(stylesheets)
18968     {
18969         if(typeof(stylesheets) == 'string'){
18970             Roo.get(this.iframe.contentDocument.head).createChild({
18971                 tag : 'link',
18972                 rel : 'stylesheet',
18973                 type : 'text/css',
18974                 href : stylesheets
18975             });
18976             
18977             return;
18978         }
18979         var _this = this;
18980      
18981         Roo.each(stylesheets, function(s) {
18982             if(!s.length){
18983                 return;
18984             }
18985             
18986             Roo.get(_this.iframe.contentDocument.head).createChild({
18987                 tag : 'link',
18988                 rel : 'stylesheet',
18989                 type : 'text/css',
18990                 href : s
18991             });
18992         });
18993
18994         
18995     },
18996     
18997     removeStylesheets : function()
18998     {
18999         var _this = this;
19000         
19001         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19002             s.remove();
19003         });
19004     }
19005     
19006     // hide stuff that is not compatible
19007     /**
19008      * @event blur
19009      * @hide
19010      */
19011     /**
19012      * @event change
19013      * @hide
19014      */
19015     /**
19016      * @event focus
19017      * @hide
19018      */
19019     /**
19020      * @event specialkey
19021      * @hide
19022      */
19023     /**
19024      * @cfg {String} fieldClass @hide
19025      */
19026     /**
19027      * @cfg {String} focusClass @hide
19028      */
19029     /**
19030      * @cfg {String} autoCreate @hide
19031      */
19032     /**
19033      * @cfg {String} inputType @hide
19034      */
19035     /**
19036      * @cfg {String} invalidClass @hide
19037      */
19038     /**
19039      * @cfg {String} invalidText @hide
19040      */
19041     /**
19042      * @cfg {String} msgFx @hide
19043      */
19044     /**
19045      * @cfg {String} validateOnBlur @hide
19046      */
19047 });
19048
19049 Roo.HtmlEditorCore.white = [
19050         'area', 'br', 'img', 'input', 'hr', 'wbr',
19051         
19052        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19053        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19054        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19055        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19056        'table',   'ul',         'xmp', 
19057        
19058        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19059       'thead',   'tr', 
19060      
19061       'dir', 'menu', 'ol', 'ul', 'dl',
19062        
19063       'embed',  'object'
19064 ];
19065
19066
19067 Roo.HtmlEditorCore.black = [
19068     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19069         'applet', // 
19070         'base',   'basefont', 'bgsound', 'blink',  'body', 
19071         'frame',  'frameset', 'head',    'html',   'ilayer', 
19072         'iframe', 'layer',  'link',     'meta',    'object',   
19073         'script', 'style' ,'title',  'xml' // clean later..
19074 ];
19075 Roo.HtmlEditorCore.clean = [
19076     'script', 'style', 'title', 'xml'
19077 ];
19078 Roo.HtmlEditorCore.remove = [
19079     'font'
19080 ];
19081 // attributes..
19082
19083 Roo.HtmlEditorCore.ablack = [
19084     'on'
19085 ];
19086     
19087 Roo.HtmlEditorCore.aclean = [ 
19088     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19089 ];
19090
19091 // protocols..
19092 Roo.HtmlEditorCore.pwhite= [
19093         'http',  'https',  'mailto'
19094 ];
19095
19096 // white listed style attributes.
19097 Roo.HtmlEditorCore.cwhite= [
19098       //  'text-align', /// default is to allow most things..
19099       
19100          
19101 //        'font-size'//??
19102 ];
19103
19104 // black listed style attributes.
19105 Roo.HtmlEditorCore.cblack= [
19106       //  'font-size' -- this can be set by the project 
19107 ];
19108
19109
19110 Roo.HtmlEditorCore.swapCodes   =[ 
19111     [    8211, "--" ], 
19112     [    8212, "--" ], 
19113     [    8216,  "'" ],  
19114     [    8217, "'" ],  
19115     [    8220, '"' ],  
19116     [    8221, '"' ],  
19117     [    8226, "*" ],  
19118     [    8230, "..." ]
19119 ]; 
19120
19121     /*
19122  * - LGPL
19123  *
19124  * HtmlEditor
19125  * 
19126  */
19127
19128 /**
19129  * @class Roo.bootstrap.HtmlEditor
19130  * @extends Roo.bootstrap.TextArea
19131  * Bootstrap HtmlEditor class
19132
19133  * @constructor
19134  * Create a new HtmlEditor
19135  * @param {Object} config The config object
19136  */
19137
19138 Roo.bootstrap.HtmlEditor = function(config){
19139     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19140     if (!this.toolbars) {
19141         this.toolbars = [];
19142     }
19143     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19144     this.addEvents({
19145             /**
19146              * @event initialize
19147              * Fires when the editor is fully initialized (including the iframe)
19148              * @param {HtmlEditor} this
19149              */
19150             initialize: true,
19151             /**
19152              * @event activate
19153              * Fires when the editor is first receives the focus. Any insertion must wait
19154              * until after this event.
19155              * @param {HtmlEditor} this
19156              */
19157             activate: true,
19158              /**
19159              * @event beforesync
19160              * Fires before the textarea is updated with content from the editor iframe. Return false
19161              * to cancel the sync.
19162              * @param {HtmlEditor} this
19163              * @param {String} html
19164              */
19165             beforesync: true,
19166              /**
19167              * @event beforepush
19168              * Fires before the iframe editor is updated with content from the textarea. Return false
19169              * to cancel the push.
19170              * @param {HtmlEditor} this
19171              * @param {String} html
19172              */
19173             beforepush: true,
19174              /**
19175              * @event sync
19176              * Fires when the textarea is updated with content from the editor iframe.
19177              * @param {HtmlEditor} this
19178              * @param {String} html
19179              */
19180             sync: true,
19181              /**
19182              * @event push
19183              * Fires when the iframe editor is updated with content from the textarea.
19184              * @param {HtmlEditor} this
19185              * @param {String} html
19186              */
19187             push: true,
19188              /**
19189              * @event editmodechange
19190              * Fires when the editor switches edit modes
19191              * @param {HtmlEditor} this
19192              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19193              */
19194             editmodechange: true,
19195             /**
19196              * @event editorevent
19197              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19198              * @param {HtmlEditor} this
19199              */
19200             editorevent: true,
19201             /**
19202              * @event firstfocus
19203              * Fires when on first focus - needed by toolbars..
19204              * @param {HtmlEditor} this
19205              */
19206             firstfocus: true,
19207             /**
19208              * @event autosave
19209              * Auto save the htmlEditor value as a file into Events
19210              * @param {HtmlEditor} this
19211              */
19212             autosave: true,
19213             /**
19214              * @event savedpreview
19215              * preview the saved version of htmlEditor
19216              * @param {HtmlEditor} this
19217              */
19218             savedpreview: true
19219         });
19220 };
19221
19222
19223 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19224     
19225     
19226       /**
19227      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19228      */
19229     toolbars : false,
19230    
19231      /**
19232      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19233      *                        Roo.resizable.
19234      */
19235     resizable : false,
19236      /**
19237      * @cfg {Number} height (in pixels)
19238      */   
19239     height: 300,
19240    /**
19241      * @cfg {Number} width (in pixels)
19242      */   
19243     width: false,
19244     
19245     /**
19246      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19247      * 
19248      */
19249     stylesheets: false,
19250     
19251     // id of frame..
19252     frameId: false,
19253     
19254     // private properties
19255     validationEvent : false,
19256     deferHeight: true,
19257     initialized : false,
19258     activated : false,
19259     
19260     onFocus : Roo.emptyFn,
19261     iframePad:3,
19262     hideMode:'offsets',
19263     
19264     
19265     tbContainer : false,
19266     
19267     toolbarContainer :function() {
19268         return this.wrap.select('.x-html-editor-tb',true).first();
19269     },
19270
19271     /**
19272      * Protected method that will not generally be called directly. It
19273      * is called when the editor creates its toolbar. Override this method if you need to
19274      * add custom toolbar buttons.
19275      * @param {HtmlEditor} editor
19276      */
19277     createToolbar : function(){
19278         
19279         Roo.log("create toolbars");
19280         
19281         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19282         this.toolbars[0].render(this.toolbarContainer());
19283         
19284         return;
19285         
19286 //        if (!editor.toolbars || !editor.toolbars.length) {
19287 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19288 //        }
19289 //        
19290 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19291 //            editor.toolbars[i] = Roo.factory(
19292 //                    typeof(editor.toolbars[i]) == 'string' ?
19293 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19294 //                Roo.bootstrap.HtmlEditor);
19295 //            editor.toolbars[i].init(editor);
19296 //        }
19297     },
19298
19299      
19300     // private
19301     onRender : function(ct, position)
19302     {
19303        // Roo.log("Call onRender: " + this.xtype);
19304         var _t = this;
19305         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19306       
19307         this.wrap = this.inputEl().wrap({
19308             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19309         });
19310         
19311         this.editorcore.onRender(ct, position);
19312          
19313         if (this.resizable) {
19314             this.resizeEl = new Roo.Resizable(this.wrap, {
19315                 pinned : true,
19316                 wrap: true,
19317                 dynamic : true,
19318                 minHeight : this.height,
19319                 height: this.height,
19320                 handles : this.resizable,
19321                 width: this.width,
19322                 listeners : {
19323                     resize : function(r, w, h) {
19324                         _t.onResize(w,h); // -something
19325                     }
19326                 }
19327             });
19328             
19329         }
19330         this.createToolbar(this);
19331        
19332         
19333         if(!this.width && this.resizable){
19334             this.setSize(this.wrap.getSize());
19335         }
19336         if (this.resizeEl) {
19337             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19338             // should trigger onReize..
19339         }
19340         
19341     },
19342
19343     // private
19344     onResize : function(w, h)
19345     {
19346         Roo.log('resize: ' +w + ',' + h );
19347         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19348         var ew = false;
19349         var eh = false;
19350         
19351         if(this.inputEl() ){
19352             if(typeof w == 'number'){
19353                 var aw = w - this.wrap.getFrameWidth('lr');
19354                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19355                 ew = aw;
19356             }
19357             if(typeof h == 'number'){
19358                  var tbh = -11;  // fixme it needs to tool bar size!
19359                 for (var i =0; i < this.toolbars.length;i++) {
19360                     // fixme - ask toolbars for heights?
19361                     tbh += this.toolbars[i].el.getHeight();
19362                     //if (this.toolbars[i].footer) {
19363                     //    tbh += this.toolbars[i].footer.el.getHeight();
19364                     //}
19365                 }
19366               
19367                 
19368                 
19369                 
19370                 
19371                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19372                 ah -= 5; // knock a few pixes off for look..
19373                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19374                 var eh = ah;
19375             }
19376         }
19377         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19378         this.editorcore.onResize(ew,eh);
19379         
19380     },
19381
19382     /**
19383      * Toggles the editor between standard and source edit mode.
19384      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19385      */
19386     toggleSourceEdit : function(sourceEditMode)
19387     {
19388         this.editorcore.toggleSourceEdit(sourceEditMode);
19389         
19390         if(this.editorcore.sourceEditMode){
19391             Roo.log('editor - showing textarea');
19392             
19393 //            Roo.log('in');
19394 //            Roo.log(this.syncValue());
19395             this.syncValue();
19396             this.inputEl().removeClass(['hide', 'x-hidden']);
19397             this.inputEl().dom.removeAttribute('tabIndex');
19398             this.inputEl().focus();
19399         }else{
19400             Roo.log('editor - hiding textarea');
19401 //            Roo.log('out')
19402 //            Roo.log(this.pushValue()); 
19403             this.pushValue();
19404             
19405             this.inputEl().addClass(['hide', 'x-hidden']);
19406             this.inputEl().dom.setAttribute('tabIndex', -1);
19407             //this.deferFocus();
19408         }
19409          
19410         if(this.resizable){
19411             this.setSize(this.wrap.getSize());
19412         }
19413         
19414         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19415     },
19416  
19417     // private (for BoxComponent)
19418     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19419
19420     // private (for BoxComponent)
19421     getResizeEl : function(){
19422         return this.wrap;
19423     },
19424
19425     // private (for BoxComponent)
19426     getPositionEl : function(){
19427         return this.wrap;
19428     },
19429
19430     // private
19431     initEvents : function(){
19432         this.originalValue = this.getValue();
19433     },
19434
19435 //    /**
19436 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19437 //     * @method
19438 //     */
19439 //    markInvalid : Roo.emptyFn,
19440 //    /**
19441 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19442 //     * @method
19443 //     */
19444 //    clearInvalid : Roo.emptyFn,
19445
19446     setValue : function(v){
19447         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19448         this.editorcore.pushValue();
19449     },
19450
19451      
19452     // private
19453     deferFocus : function(){
19454         this.focus.defer(10, this);
19455     },
19456
19457     // doc'ed in Field
19458     focus : function(){
19459         this.editorcore.focus();
19460         
19461     },
19462       
19463
19464     // private
19465     onDestroy : function(){
19466         
19467         
19468         
19469         if(this.rendered){
19470             
19471             for (var i =0; i < this.toolbars.length;i++) {
19472                 // fixme - ask toolbars for heights?
19473                 this.toolbars[i].onDestroy();
19474             }
19475             
19476             this.wrap.dom.innerHTML = '';
19477             this.wrap.remove();
19478         }
19479     },
19480
19481     // private
19482     onFirstFocus : function(){
19483         //Roo.log("onFirstFocus");
19484         this.editorcore.onFirstFocus();
19485          for (var i =0; i < this.toolbars.length;i++) {
19486             this.toolbars[i].onFirstFocus();
19487         }
19488         
19489     },
19490     
19491     // private
19492     syncValue : function()
19493     {   
19494         this.editorcore.syncValue();
19495     },
19496     
19497     pushValue : function()
19498     {   
19499         this.editorcore.pushValue();
19500     }
19501      
19502     
19503     // hide stuff that is not compatible
19504     /**
19505      * @event blur
19506      * @hide
19507      */
19508     /**
19509      * @event change
19510      * @hide
19511      */
19512     /**
19513      * @event focus
19514      * @hide
19515      */
19516     /**
19517      * @event specialkey
19518      * @hide
19519      */
19520     /**
19521      * @cfg {String} fieldClass @hide
19522      */
19523     /**
19524      * @cfg {String} focusClass @hide
19525      */
19526     /**
19527      * @cfg {String} autoCreate @hide
19528      */
19529     /**
19530      * @cfg {String} inputType @hide
19531      */
19532     /**
19533      * @cfg {String} invalidClass @hide
19534      */
19535     /**
19536      * @cfg {String} invalidText @hide
19537      */
19538     /**
19539      * @cfg {String} msgFx @hide
19540      */
19541     /**
19542      * @cfg {String} validateOnBlur @hide
19543      */
19544 });
19545  
19546     
19547    
19548    
19549    
19550       
19551 Roo.namespace('Roo.bootstrap.htmleditor');
19552 /**
19553  * @class Roo.bootstrap.HtmlEditorToolbar1
19554  * Basic Toolbar
19555  * 
19556  * Usage:
19557  *
19558  new Roo.bootstrap.HtmlEditor({
19559     ....
19560     toolbars : [
19561         new Roo.bootstrap.HtmlEditorToolbar1({
19562             disable : { fonts: 1 , format: 1, ..., ... , ...],
19563             btns : [ .... ]
19564         })
19565     }
19566      
19567  * 
19568  * @cfg {Object} disable List of elements to disable..
19569  * @cfg {Array} btns List of additional buttons.
19570  * 
19571  * 
19572  * NEEDS Extra CSS? 
19573  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19574  */
19575  
19576 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19577 {
19578     
19579     Roo.apply(this, config);
19580     
19581     // default disabled, based on 'good practice'..
19582     this.disable = this.disable || {};
19583     Roo.applyIf(this.disable, {
19584         fontSize : true,
19585         colors : true,
19586         specialElements : true
19587     });
19588     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
19589     
19590     this.editor = config.editor;
19591     this.editorcore = config.editor.editorcore;
19592     
19593     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
19594     
19595     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
19596     // dont call parent... till later.
19597 }
19598 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
19599      
19600     bar : true,
19601     
19602     editor : false,
19603     editorcore : false,
19604     
19605     
19606     formats : [
19607         "p" ,  
19608         "h1","h2","h3","h4","h5","h6", 
19609         "pre", "code", 
19610         "abbr", "acronym", "address", "cite", "samp", "var",
19611         'div','span'
19612     ],
19613     
19614     onRender : function(ct, position)
19615     {
19616        // Roo.log("Call onRender: " + this.xtype);
19617         
19618        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
19619        Roo.log(this.el);
19620        this.el.dom.style.marginBottom = '0';
19621        var _this = this;
19622        var editorcore = this.editorcore;
19623        var editor= this.editor;
19624        
19625        var children = [];
19626        var btn = function(id,cmd , toggle, handler){
19627        
19628             var  event = toggle ? 'toggle' : 'click';
19629        
19630             var a = {
19631                 size : 'sm',
19632                 xtype: 'Button',
19633                 xns: Roo.bootstrap,
19634                 glyphicon : id,
19635                 cmd : id || cmd,
19636                 enableToggle:toggle !== false,
19637                 //html : 'submit'
19638                 pressed : toggle ? false : null,
19639                 listeners : {}
19640             }
19641             a.listeners[toggle ? 'toggle' : 'click'] = function() {
19642                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
19643             }
19644             children.push(a);
19645             return a;
19646        }
19647         
19648         var style = {
19649                 xtype: 'Button',
19650                 size : 'sm',
19651                 xns: Roo.bootstrap,
19652                 glyphicon : 'font',
19653                 //html : 'submit'
19654                 menu : {
19655                     xtype: 'Menu',
19656                     xns: Roo.bootstrap,
19657                     items:  []
19658                 }
19659         };
19660         Roo.each(this.formats, function(f) {
19661             style.menu.items.push({
19662                 xtype :'MenuItem',
19663                 xns: Roo.bootstrap,
19664                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
19665                 tagname : f,
19666                 listeners : {
19667                     click : function()
19668                     {
19669                         editorcore.insertTag(this.tagname);
19670                         editor.focus();
19671                     }
19672                 }
19673                 
19674             });
19675         });
19676          children.push(style);   
19677             
19678             
19679         btn('bold',false,true);
19680         btn('italic',false,true);
19681         btn('align-left', 'justifyleft',true);
19682         btn('align-center', 'justifycenter',true);
19683         btn('align-right' , 'justifyright',true);
19684         btn('link', false, false, function(btn) {
19685             //Roo.log("create link?");
19686             var url = prompt(this.createLinkText, this.defaultLinkValue);
19687             if(url && url != 'http:/'+'/'){
19688                 this.editorcore.relayCmd('createlink', url);
19689             }
19690         }),
19691         btn('list','insertunorderedlist',true);
19692         btn('pencil', false,true, function(btn){
19693                 Roo.log(this);
19694                 
19695                 this.toggleSourceEdit(btn.pressed);
19696         });
19697         /*
19698         var cog = {
19699                 xtype: 'Button',
19700                 size : 'sm',
19701                 xns: Roo.bootstrap,
19702                 glyphicon : 'cog',
19703                 //html : 'submit'
19704                 menu : {
19705                     xtype: 'Menu',
19706                     xns: Roo.bootstrap,
19707                     items:  []
19708                 }
19709         };
19710         
19711         cog.menu.items.push({
19712             xtype :'MenuItem',
19713             xns: Roo.bootstrap,
19714             html : Clean styles,
19715             tagname : f,
19716             listeners : {
19717                 click : function()
19718                 {
19719                     editorcore.insertTag(this.tagname);
19720                     editor.focus();
19721                 }
19722             }
19723             
19724         });
19725        */
19726         
19727          
19728        this.xtype = 'NavSimplebar';
19729         
19730         for(var i=0;i< children.length;i++) {
19731             
19732             this.buttons.add(this.addxtypeChild(children[i]));
19733             
19734         }
19735         
19736         editor.on('editorevent', this.updateToolbar, this);
19737     },
19738     onBtnClick : function(id)
19739     {
19740        this.editorcore.relayCmd(id);
19741        this.editorcore.focus();
19742     },
19743     
19744     /**
19745      * Protected method that will not generally be called directly. It triggers
19746      * a toolbar update by reading the markup state of the current selection in the editor.
19747      */
19748     updateToolbar: function(){
19749
19750         if(!this.editorcore.activated){
19751             this.editor.onFirstFocus(); // is this neeed?
19752             return;
19753         }
19754
19755         var btns = this.buttons; 
19756         var doc = this.editorcore.doc;
19757         btns.get('bold').setActive(doc.queryCommandState('bold'));
19758         btns.get('italic').setActive(doc.queryCommandState('italic'));
19759         //btns.get('underline').setActive(doc.queryCommandState('underline'));
19760         
19761         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
19762         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
19763         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
19764         
19765         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
19766         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
19767          /*
19768         
19769         var ans = this.editorcore.getAllAncestors();
19770         if (this.formatCombo) {
19771             
19772             
19773             var store = this.formatCombo.store;
19774             this.formatCombo.setValue("");
19775             for (var i =0; i < ans.length;i++) {
19776                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
19777                     // select it..
19778                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
19779                     break;
19780                 }
19781             }
19782         }
19783         
19784         
19785         
19786         // hides menus... - so this cant be on a menu...
19787         Roo.bootstrap.MenuMgr.hideAll();
19788         */
19789         Roo.bootstrap.MenuMgr.hideAll();
19790         //this.editorsyncValue();
19791     },
19792     onFirstFocus: function() {
19793         this.buttons.each(function(item){
19794            item.enable();
19795         });
19796     },
19797     toggleSourceEdit : function(sourceEditMode){
19798         
19799           
19800         if(sourceEditMode){
19801             Roo.log("disabling buttons");
19802            this.buttons.each( function(item){
19803                 if(item.cmd != 'pencil'){
19804                     item.disable();
19805                 }
19806             });
19807           
19808         }else{
19809             Roo.log("enabling buttons");
19810             if(this.editorcore.initialized){
19811                 this.buttons.each( function(item){
19812                     item.enable();
19813                 });
19814             }
19815             
19816         }
19817         Roo.log("calling toggole on editor");
19818         // tell the editor that it's been pressed..
19819         this.editor.toggleSourceEdit(sourceEditMode);
19820        
19821     }
19822 });
19823
19824
19825
19826
19827
19828 /**
19829  * @class Roo.bootstrap.Table.AbstractSelectionModel
19830  * @extends Roo.util.Observable
19831  * Abstract base class for grid SelectionModels.  It provides the interface that should be
19832  * implemented by descendant classes.  This class should not be directly instantiated.
19833  * @constructor
19834  */
19835 Roo.bootstrap.Table.AbstractSelectionModel = function(){
19836     this.locked = false;
19837     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
19838 };
19839
19840
19841 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
19842     /** @ignore Called by the grid automatically. Do not call directly. */
19843     init : function(grid){
19844         this.grid = grid;
19845         this.initEvents();
19846     },
19847
19848     /**
19849      * Locks the selections.
19850      */
19851     lock : function(){
19852         this.locked = true;
19853     },
19854
19855     /**
19856      * Unlocks the selections.
19857      */
19858     unlock : function(){
19859         this.locked = false;
19860     },
19861
19862     /**
19863      * Returns true if the selections are locked.
19864      * @return {Boolean}
19865      */
19866     isLocked : function(){
19867         return this.locked;
19868     }
19869 });
19870 /**
19871  * @extends Roo.bootstrap.Table.AbstractSelectionModel
19872  * @class Roo.bootstrap.Table.RowSelectionModel
19873  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
19874  * It supports multiple selections and keyboard selection/navigation. 
19875  * @constructor
19876  * @param {Object} config
19877  */
19878
19879 Roo.bootstrap.Table.RowSelectionModel = function(config){
19880     Roo.apply(this, config);
19881     this.selections = new Roo.util.MixedCollection(false, function(o){
19882         return o.id;
19883     });
19884
19885     this.last = false;
19886     this.lastActive = false;
19887
19888     this.addEvents({
19889         /**
19890              * @event selectionchange
19891              * Fires when the selection changes
19892              * @param {SelectionModel} this
19893              */
19894             "selectionchange" : true,
19895         /**
19896              * @event afterselectionchange
19897              * Fires after the selection changes (eg. by key press or clicking)
19898              * @param {SelectionModel} this
19899              */
19900             "afterselectionchange" : true,
19901         /**
19902              * @event beforerowselect
19903              * Fires when a row is selected being selected, return false to cancel.
19904              * @param {SelectionModel} this
19905              * @param {Number} rowIndex The selected index
19906              * @param {Boolean} keepExisting False if other selections will be cleared
19907              */
19908             "beforerowselect" : true,
19909         /**
19910              * @event rowselect
19911              * Fires when a row is selected.
19912              * @param {SelectionModel} this
19913              * @param {Number} rowIndex The selected index
19914              * @param {Roo.data.Record} r The record
19915              */
19916             "rowselect" : true,
19917         /**
19918              * @event rowdeselect
19919              * Fires when a row is deselected.
19920              * @param {SelectionModel} this
19921              * @param {Number} rowIndex The selected index
19922              */
19923         "rowdeselect" : true
19924     });
19925     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
19926     this.locked = false;
19927 };
19928
19929 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
19930     /**
19931      * @cfg {Boolean} singleSelect
19932      * True to allow selection of only one row at a time (defaults to false)
19933      */
19934     singleSelect : false,
19935
19936     // private
19937     initEvents : function(){
19938
19939         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
19940             this.grid.on("mousedown", this.handleMouseDown, this);
19941         }else{ // allow click to work like normal
19942             this.grid.on("rowclick", this.handleDragableRowClick, this);
19943         }
19944
19945         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
19946             "up" : function(e){
19947                 if(!e.shiftKey){
19948                     this.selectPrevious(e.shiftKey);
19949                 }else if(this.last !== false && this.lastActive !== false){
19950                     var last = this.last;
19951                     this.selectRange(this.last,  this.lastActive-1);
19952                     this.grid.getView().focusRow(this.lastActive);
19953                     if(last !== false){
19954                         this.last = last;
19955                     }
19956                 }else{
19957                     this.selectFirstRow();
19958                 }
19959                 this.fireEvent("afterselectionchange", this);
19960             },
19961             "down" : function(e){
19962                 if(!e.shiftKey){
19963                     this.selectNext(e.shiftKey);
19964                 }else if(this.last !== false && this.lastActive !== false){
19965                     var last = this.last;
19966                     this.selectRange(this.last,  this.lastActive+1);
19967                     this.grid.getView().focusRow(this.lastActive);
19968                     if(last !== false){
19969                         this.last = last;
19970                     }
19971                 }else{
19972                     this.selectFirstRow();
19973                 }
19974                 this.fireEvent("afterselectionchange", this);
19975             },
19976             scope: this
19977         });
19978
19979         var view = this.grid.view;
19980         view.on("refresh", this.onRefresh, this);
19981         view.on("rowupdated", this.onRowUpdated, this);
19982         view.on("rowremoved", this.onRemove, this);
19983     },
19984
19985     // private
19986     onRefresh : function(){
19987         var ds = this.grid.dataSource, i, v = this.grid.view;
19988         var s = this.selections;
19989         s.each(function(r){
19990             if((i = ds.indexOfId(r.id)) != -1){
19991                 v.onRowSelect(i);
19992             }else{
19993                 s.remove(r);
19994             }
19995         });
19996     },
19997
19998     // private
19999     onRemove : function(v, index, r){
20000         this.selections.remove(r);
20001     },
20002
20003     // private
20004     onRowUpdated : function(v, index, r){
20005         if(this.isSelected(r)){
20006             v.onRowSelect(index);
20007         }
20008     },
20009
20010     /**
20011      * Select records.
20012      * @param {Array} records The records to select
20013      * @param {Boolean} keepExisting (optional) True to keep existing selections
20014      */
20015     selectRecords : function(records, keepExisting){
20016         if(!keepExisting){
20017             this.clearSelections();
20018         }
20019         var ds = this.grid.dataSource;
20020         for(var i = 0, len = records.length; i < len; i++){
20021             this.selectRow(ds.indexOf(records[i]), true);
20022         }
20023     },
20024
20025     /**
20026      * Gets the number of selected rows.
20027      * @return {Number}
20028      */
20029     getCount : function(){
20030         return this.selections.length;
20031     },
20032
20033     /**
20034      * Selects the first row in the grid.
20035      */
20036     selectFirstRow : function(){
20037         this.selectRow(0);
20038     },
20039
20040     /**
20041      * Select the last row.
20042      * @param {Boolean} keepExisting (optional) True to keep existing selections
20043      */
20044     selectLastRow : function(keepExisting){
20045         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20046     },
20047
20048     /**
20049      * Selects the row immediately following the last selected row.
20050      * @param {Boolean} keepExisting (optional) True to keep existing selections
20051      */
20052     selectNext : function(keepExisting){
20053         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20054             this.selectRow(this.last+1, keepExisting);
20055             this.grid.getView().focusRow(this.last);
20056         }
20057     },
20058
20059     /**
20060      * Selects the row that precedes the last selected row.
20061      * @param {Boolean} keepExisting (optional) True to keep existing selections
20062      */
20063     selectPrevious : function(keepExisting){
20064         if(this.last){
20065             this.selectRow(this.last-1, keepExisting);
20066             this.grid.getView().focusRow(this.last);
20067         }
20068     },
20069
20070     /**
20071      * Returns the selected records
20072      * @return {Array} Array of selected records
20073      */
20074     getSelections : function(){
20075         return [].concat(this.selections.items);
20076     },
20077
20078     /**
20079      * Returns the first selected record.
20080      * @return {Record}
20081      */
20082     getSelected : function(){
20083         return this.selections.itemAt(0);
20084     },
20085
20086
20087     /**
20088      * Clears all selections.
20089      */
20090     clearSelections : function(fast){
20091         if(this.locked) return;
20092         if(fast !== true){
20093             var ds = this.grid.dataSource;
20094             var s = this.selections;
20095             s.each(function(r){
20096                 this.deselectRow(ds.indexOfId(r.id));
20097             }, this);
20098             s.clear();
20099         }else{
20100             this.selections.clear();
20101         }
20102         this.last = false;
20103     },
20104
20105
20106     /**
20107      * Selects all rows.
20108      */
20109     selectAll : function(){
20110         if(this.locked) return;
20111         this.selections.clear();
20112         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20113             this.selectRow(i, true);
20114         }
20115     },
20116
20117     /**
20118      * Returns True if there is a selection.
20119      * @return {Boolean}
20120      */
20121     hasSelection : function(){
20122         return this.selections.length > 0;
20123     },
20124
20125     /**
20126      * Returns True if the specified row is selected.
20127      * @param {Number/Record} record The record or index of the record to check
20128      * @return {Boolean}
20129      */
20130     isSelected : function(index){
20131         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20132         return (r && this.selections.key(r.id) ? true : false);
20133     },
20134
20135     /**
20136      * Returns True if the specified record id is selected.
20137      * @param {String} id The id of record to check
20138      * @return {Boolean}
20139      */
20140     isIdSelected : function(id){
20141         return (this.selections.key(id) ? true : false);
20142     },
20143
20144     // private
20145     handleMouseDown : function(e, t){
20146         var view = this.grid.getView(), rowIndex;
20147         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20148             return;
20149         };
20150         if(e.shiftKey && this.last !== false){
20151             var last = this.last;
20152             this.selectRange(last, rowIndex, e.ctrlKey);
20153             this.last = last; // reset the last
20154             view.focusRow(rowIndex);
20155         }else{
20156             var isSelected = this.isSelected(rowIndex);
20157             if(e.button !== 0 && isSelected){
20158                 view.focusRow(rowIndex);
20159             }else if(e.ctrlKey && isSelected){
20160                 this.deselectRow(rowIndex);
20161             }else if(!isSelected){
20162                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20163                 view.focusRow(rowIndex);
20164             }
20165         }
20166         this.fireEvent("afterselectionchange", this);
20167     },
20168     // private
20169     handleDragableRowClick :  function(grid, rowIndex, e) 
20170     {
20171         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20172             this.selectRow(rowIndex, false);
20173             grid.view.focusRow(rowIndex);
20174              this.fireEvent("afterselectionchange", this);
20175         }
20176     },
20177     
20178     /**
20179      * Selects multiple rows.
20180      * @param {Array} rows Array of the indexes of the row to select
20181      * @param {Boolean} keepExisting (optional) True to keep existing selections
20182      */
20183     selectRows : function(rows, keepExisting){
20184         if(!keepExisting){
20185             this.clearSelections();
20186         }
20187         for(var i = 0, len = rows.length; i < len; i++){
20188             this.selectRow(rows[i], true);
20189         }
20190     },
20191
20192     /**
20193      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20194      * @param {Number} startRow The index of the first row in the range
20195      * @param {Number} endRow The index of the last row in the range
20196      * @param {Boolean} keepExisting (optional) True to retain existing selections
20197      */
20198     selectRange : function(startRow, endRow, keepExisting){
20199         if(this.locked) return;
20200         if(!keepExisting){
20201             this.clearSelections();
20202         }
20203         if(startRow <= endRow){
20204             for(var i = startRow; i <= endRow; i++){
20205                 this.selectRow(i, true);
20206             }
20207         }else{
20208             for(var i = startRow; i >= endRow; i--){
20209                 this.selectRow(i, true);
20210             }
20211         }
20212     },
20213
20214     /**
20215      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20216      * @param {Number} startRow The index of the first row in the range
20217      * @param {Number} endRow The index of the last row in the range
20218      */
20219     deselectRange : function(startRow, endRow, preventViewNotify){
20220         if(this.locked) return;
20221         for(var i = startRow; i <= endRow; i++){
20222             this.deselectRow(i, preventViewNotify);
20223         }
20224     },
20225
20226     /**
20227      * Selects a row.
20228      * @param {Number} row The index of the row to select
20229      * @param {Boolean} keepExisting (optional) True to keep existing selections
20230      */
20231     selectRow : function(index, keepExisting, preventViewNotify){
20232         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20233         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20234             if(!keepExisting || this.singleSelect){
20235                 this.clearSelections();
20236             }
20237             var r = this.grid.dataSource.getAt(index);
20238             this.selections.add(r);
20239             this.last = this.lastActive = index;
20240             if(!preventViewNotify){
20241                 this.grid.getView().onRowSelect(index);
20242             }
20243             this.fireEvent("rowselect", this, index, r);
20244             this.fireEvent("selectionchange", this);
20245         }
20246     },
20247
20248     /**
20249      * Deselects a row.
20250      * @param {Number} row The index of the row to deselect
20251      */
20252     deselectRow : function(index, preventViewNotify){
20253         if(this.locked) return;
20254         if(this.last == index){
20255             this.last = false;
20256         }
20257         if(this.lastActive == index){
20258             this.lastActive = false;
20259         }
20260         var r = this.grid.dataSource.getAt(index);
20261         this.selections.remove(r);
20262         if(!preventViewNotify){
20263             this.grid.getView().onRowDeselect(index);
20264         }
20265         this.fireEvent("rowdeselect", this, index);
20266         this.fireEvent("selectionchange", this);
20267     },
20268
20269     // private
20270     restoreLast : function(){
20271         if(this._last){
20272             this.last = this._last;
20273         }
20274     },
20275
20276     // private
20277     acceptsNav : function(row, col, cm){
20278         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20279     },
20280
20281     // private
20282     onEditorKey : function(field, e){
20283         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20284         if(k == e.TAB){
20285             e.stopEvent();
20286             ed.completeEdit();
20287             if(e.shiftKey){
20288                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20289             }else{
20290                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20291             }
20292         }else if(k == e.ENTER && !e.ctrlKey){
20293             e.stopEvent();
20294             ed.completeEdit();
20295             if(e.shiftKey){
20296                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20297             }else{
20298                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20299             }
20300         }else if(k == e.ESC){
20301             ed.cancelEdit();
20302         }
20303         if(newCell){
20304             g.startEditing(newCell[0], newCell[1]);
20305         }
20306     }
20307 });/*
20308  * Based on:
20309  * Ext JS Library 1.1.1
20310  * Copyright(c) 2006-2007, Ext JS, LLC.
20311  *
20312  * Originally Released Under LGPL - original licence link has changed is not relivant.
20313  *
20314  * Fork - LGPL
20315  * <script type="text/javascript">
20316  */
20317  
20318 /**
20319  * @class Roo.bootstrap.PagingToolbar
20320  * @extends Roo.Row
20321  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20322  * @constructor
20323  * Create a new PagingToolbar
20324  * @param {Object} config The config object
20325  */
20326 Roo.bootstrap.PagingToolbar = function(config)
20327 {
20328     // old args format still supported... - xtype is prefered..
20329         // created from xtype...
20330     var ds = config.dataSource;
20331     this.toolbarItems = [];
20332     if (config.items) {
20333         this.toolbarItems = config.items;
20334 //        config.items = [];
20335     }
20336     
20337     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20338     this.ds = ds;
20339     this.cursor = 0;
20340     if (ds) { 
20341         this.bind(ds);
20342     }
20343     
20344     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20345     
20346 };
20347
20348 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20349     /**
20350      * @cfg {Roo.data.Store} dataSource
20351      * The underlying data store providing the paged data
20352      */
20353     /**
20354      * @cfg {String/HTMLElement/Element} container
20355      * container The id or element that will contain the toolbar
20356      */
20357     /**
20358      * @cfg {Boolean} displayInfo
20359      * True to display the displayMsg (defaults to false)
20360      */
20361     /**
20362      * @cfg {Number} pageSize
20363      * The number of records to display per page (defaults to 20)
20364      */
20365     pageSize: 20,
20366     /**
20367      * @cfg {String} displayMsg
20368      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20369      */
20370     displayMsg : 'Displaying {0} - {1} of {2}',
20371     /**
20372      * @cfg {String} emptyMsg
20373      * The message to display when no records are found (defaults to "No data to display")
20374      */
20375     emptyMsg : 'No data to display',
20376     /**
20377      * Customizable piece of the default paging text (defaults to "Page")
20378      * @type String
20379      */
20380     beforePageText : "Page",
20381     /**
20382      * Customizable piece of the default paging text (defaults to "of %0")
20383      * @type String
20384      */
20385     afterPageText : "of {0}",
20386     /**
20387      * Customizable piece of the default paging text (defaults to "First Page")
20388      * @type String
20389      */
20390     firstText : "First Page",
20391     /**
20392      * Customizable piece of the default paging text (defaults to "Previous Page")
20393      * @type String
20394      */
20395     prevText : "Previous Page",
20396     /**
20397      * Customizable piece of the default paging text (defaults to "Next Page")
20398      * @type String
20399      */
20400     nextText : "Next Page",
20401     /**
20402      * Customizable piece of the default paging text (defaults to "Last Page")
20403      * @type String
20404      */
20405     lastText : "Last Page",
20406     /**
20407      * Customizable piece of the default paging text (defaults to "Refresh")
20408      * @type String
20409      */
20410     refreshText : "Refresh",
20411
20412     buttons : false,
20413     // private
20414     onRender : function(ct, position) 
20415     {
20416         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20417         this.navgroup.parentId = this.id;
20418         this.navgroup.onRender(this.el, null);
20419         // add the buttons to the navgroup
20420         
20421         if(this.displayInfo){
20422             Roo.log(this.el.select('ul.navbar-nav',true).first());
20423             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20424             this.displayEl = this.el.select('.x-paging-info', true).first();
20425 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20426 //            this.displayEl = navel.el.select('span',true).first();
20427         }
20428         
20429         var _this = this;
20430         
20431         if(this.buttons){
20432             Roo.each(_this.buttons, function(e){
20433                Roo.factory(e).onRender(_this.el, null);
20434             });
20435         }
20436             
20437         Roo.each(_this.toolbarItems, function(e) {
20438             _this.navgroup.addItem(e);
20439         });
20440         
20441         
20442         this.first = this.navgroup.addItem({
20443             tooltip: this.firstText,
20444             cls: "prev",
20445             icon : 'fa fa-backward',
20446             disabled: true,
20447             preventDefault: true,
20448             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20449         });
20450         
20451         this.prev =  this.navgroup.addItem({
20452             tooltip: this.prevText,
20453             cls: "prev",
20454             icon : 'fa fa-step-backward',
20455             disabled: true,
20456             preventDefault: true,
20457             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20458         });
20459     //this.addSeparator();
20460         
20461         
20462         var field = this.navgroup.addItem( {
20463             tagtype : 'span',
20464             cls : 'x-paging-position',
20465             
20466             html : this.beforePageText  +
20467                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20468                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20469          } ); //?? escaped?
20470         
20471         this.field = field.el.select('input', true).first();
20472         this.field.on("keydown", this.onPagingKeydown, this);
20473         this.field.on("focus", function(){this.dom.select();});
20474     
20475     
20476         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20477         //this.field.setHeight(18);
20478         //this.addSeparator();
20479         this.next = this.navgroup.addItem({
20480             tooltip: this.nextText,
20481             cls: "next",
20482             html : ' <i class="fa fa-step-forward">',
20483             disabled: true,
20484             preventDefault: true,
20485             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20486         });
20487         this.last = this.navgroup.addItem({
20488             tooltip: this.lastText,
20489             icon : 'fa fa-forward',
20490             cls: "next",
20491             disabled: true,
20492             preventDefault: true,
20493             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20494         });
20495     //this.addSeparator();
20496         this.loading = this.navgroup.addItem({
20497             tooltip: this.refreshText,
20498             icon: 'fa fa-refresh',
20499             preventDefault: true,
20500             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20501         });
20502
20503     },
20504
20505     // private
20506     updateInfo : function(){
20507         if(this.displayEl){
20508             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20509             var msg = count == 0 ?
20510                 this.emptyMsg :
20511                 String.format(
20512                     this.displayMsg,
20513                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20514                 );
20515             this.displayEl.update(msg);
20516         }
20517     },
20518
20519     // private
20520     onLoad : function(ds, r, o){
20521        this.cursor = o.params ? o.params.start : 0;
20522        var d = this.getPageData(),
20523             ap = d.activePage,
20524             ps = d.pages;
20525         
20526        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20527        this.field.dom.value = ap;
20528        this.first.setDisabled(ap == 1);
20529        this.prev.setDisabled(ap == 1);
20530        this.next.setDisabled(ap == ps);
20531        this.last.setDisabled(ap == ps);
20532        this.loading.enable();
20533        this.updateInfo();
20534     },
20535
20536     // private
20537     getPageData : function(){
20538         var total = this.ds.getTotalCount();
20539         return {
20540             total : total,
20541             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20542             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20543         };
20544     },
20545
20546     // private
20547     onLoadError : function(){
20548         this.loading.enable();
20549     },
20550
20551     // private
20552     onPagingKeydown : function(e){
20553         var k = e.getKey();
20554         var d = this.getPageData();
20555         if(k == e.RETURN){
20556             var v = this.field.dom.value, pageNum;
20557             if(!v || isNaN(pageNum = parseInt(v, 10))){
20558                 this.field.dom.value = d.activePage;
20559                 return;
20560             }
20561             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20562             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20563             e.stopEvent();
20564         }
20565         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))
20566         {
20567           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20568           this.field.dom.value = pageNum;
20569           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20570           e.stopEvent();
20571         }
20572         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20573         {
20574           var v = this.field.dom.value, pageNum; 
20575           var increment = (e.shiftKey) ? 10 : 1;
20576           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20577             increment *= -1;
20578           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20579             this.field.dom.value = d.activePage;
20580             return;
20581           }
20582           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
20583           {
20584             this.field.dom.value = parseInt(v, 10) + increment;
20585             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
20586             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20587           }
20588           e.stopEvent();
20589         }
20590     },
20591
20592     // private
20593     beforeLoad : function(){
20594         if(this.loading){
20595             this.loading.disable();
20596         }
20597     },
20598
20599     // private
20600     onClick : function(which){
20601         
20602         var ds = this.ds;
20603         if (!ds) {
20604             return;
20605         }
20606         
20607         switch(which){
20608             case "first":
20609                 ds.load({params:{start: 0, limit: this.pageSize}});
20610             break;
20611             case "prev":
20612                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
20613             break;
20614             case "next":
20615                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
20616             break;
20617             case "last":
20618                 var total = ds.getTotalCount();
20619                 var extra = total % this.pageSize;
20620                 var lastStart = extra ? (total - extra) : total-this.pageSize;
20621                 ds.load({params:{start: lastStart, limit: this.pageSize}});
20622             break;
20623             case "refresh":
20624                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
20625             break;
20626         }
20627     },
20628
20629     /**
20630      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
20631      * @param {Roo.data.Store} store The data store to unbind
20632      */
20633     unbind : function(ds){
20634         ds.un("beforeload", this.beforeLoad, this);
20635         ds.un("load", this.onLoad, this);
20636         ds.un("loadexception", this.onLoadError, this);
20637         ds.un("remove", this.updateInfo, this);
20638         ds.un("add", this.updateInfo, this);
20639         this.ds = undefined;
20640     },
20641
20642     /**
20643      * Binds the paging toolbar to the specified {@link Roo.data.Store}
20644      * @param {Roo.data.Store} store The data store to bind
20645      */
20646     bind : function(ds){
20647         ds.on("beforeload", this.beforeLoad, this);
20648         ds.on("load", this.onLoad, this);
20649         ds.on("loadexception", this.onLoadError, this);
20650         ds.on("remove", this.updateInfo, this);
20651         ds.on("add", this.updateInfo, this);
20652         this.ds = ds;
20653     }
20654 });/*
20655  * - LGPL
20656  *
20657  * element
20658  * 
20659  */
20660
20661 /**
20662  * @class Roo.bootstrap.MessageBar
20663  * @extends Roo.bootstrap.Component
20664  * Bootstrap MessageBar class
20665  * @cfg {String} html contents of the MessageBar
20666  * @cfg {String} weight (info | success | warning | danger) default info
20667  * @cfg {String} beforeClass insert the bar before the given class
20668  * @cfg {Boolean} closable (true | false) default false
20669  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
20670  * 
20671  * @constructor
20672  * Create a new Element
20673  * @param {Object} config The config object
20674  */
20675
20676 Roo.bootstrap.MessageBar = function(config){
20677     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
20678 };
20679
20680 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
20681     
20682     html: '',
20683     weight: 'info',
20684     closable: false,
20685     fixed: false,
20686     beforeClass: 'bootstrap-sticky-wrap',
20687     
20688     getAutoCreate : function(){
20689         
20690         var cfg = {
20691             tag: 'div',
20692             cls: 'alert alert-dismissable alert-' + this.weight,
20693             cn: [
20694                 {
20695                     tag: 'span',
20696                     cls: 'message',
20697                     html: this.html || ''
20698                 }
20699             ]
20700         }
20701         
20702         if(this.fixed){
20703             cfg.cls += ' alert-messages-fixed';
20704         }
20705         
20706         if(this.closable){
20707             cfg.cn.push({
20708                 tag: 'button',
20709                 cls: 'close',
20710                 html: 'x'
20711             });
20712         }
20713         
20714         return cfg;
20715     },
20716     
20717     onRender : function(ct, position)
20718     {
20719         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20720         
20721         if(!this.el){
20722             var cfg = Roo.apply({},  this.getAutoCreate());
20723             cfg.id = Roo.id();
20724             
20725             if (this.cls) {
20726                 cfg.cls += ' ' + this.cls;
20727             }
20728             if (this.style) {
20729                 cfg.style = this.style;
20730             }
20731             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
20732             
20733             this.el.setVisibilityMode(Roo.Element.DISPLAY);
20734         }
20735         
20736         this.el.select('>button.close').on('click', this.hide, this);
20737         
20738     },
20739     
20740     show : function()
20741     {
20742         if (!this.rendered) {
20743             this.render();
20744         }
20745         
20746         this.el.show();
20747         
20748         this.fireEvent('show', this);
20749         
20750     },
20751     
20752     hide : function()
20753     {
20754         if (!this.rendered) {
20755             this.render();
20756         }
20757         
20758         this.el.hide();
20759         
20760         this.fireEvent('hide', this);
20761     },
20762     
20763     update : function()
20764     {
20765 //        var e = this.el.dom.firstChild;
20766 //        
20767 //        if(this.closable){
20768 //            e = e.nextSibling;
20769 //        }
20770 //        
20771 //        e.data = this.html || '';
20772
20773         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
20774     }
20775    
20776 });
20777
20778  
20779
20780      /*
20781  * - LGPL
20782  *
20783  * Graph
20784  * 
20785  */
20786
20787
20788 /**
20789  * @class Roo.bootstrap.Graph
20790  * @extends Roo.bootstrap.Component
20791  * Bootstrap Graph class
20792 > Prameters
20793  -sm {number} sm 4
20794  -md {number} md 5
20795  @cfg {String} graphtype  bar | vbar | pie
20796  @cfg {number} g_x coodinator | centre x (pie)
20797  @cfg {number} g_y coodinator | centre y (pie)
20798  @cfg {number} g_r radius (pie)
20799  @cfg {number} g_height height of the chart (respected by all elements in the set)
20800  @cfg {number} g_width width of the chart (respected by all elements in the set)
20801  @cfg {Object} title The title of the chart
20802     
20803  -{Array}  values
20804  -opts (object) options for the chart 
20805      o {
20806      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
20807      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
20808      o vgutter (number)
20809      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.
20810      o stacked (boolean) whether or not to tread values as in a stacked bar chart
20811      o to
20812      o stretch (boolean)
20813      o }
20814  -opts (object) options for the pie
20815      o{
20816      o cut
20817      o startAngle (number)
20818      o endAngle (number)
20819      } 
20820  *
20821  * @constructor
20822  * Create a new Input
20823  * @param {Object} config The config object
20824  */
20825
20826 Roo.bootstrap.Graph = function(config){
20827     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
20828     
20829     this.addEvents({
20830         // img events
20831         /**
20832          * @event click
20833          * The img click event for the img.
20834          * @param {Roo.EventObject} e
20835          */
20836         "click" : true
20837     });
20838 };
20839
20840 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
20841     
20842     sm: 4,
20843     md: 5,
20844     graphtype: 'bar',
20845     g_height: 250,
20846     g_width: 400,
20847     g_x: 50,
20848     g_y: 50,
20849     g_r: 30,
20850     opts:{
20851         //g_colors: this.colors,
20852         g_type: 'soft',
20853         g_gutter: '20%'
20854
20855     },
20856     title : false,
20857
20858     getAutoCreate : function(){
20859         
20860         var cfg = {
20861             tag: 'div',
20862             html : null
20863         }
20864         
20865         
20866         return  cfg;
20867     },
20868
20869     onRender : function(ct,position){
20870         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
20871         this.raphael = Raphael(this.el.dom);
20872         
20873                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
20874                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
20875                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
20876                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
20877                 /*
20878                 r.text(160, 10, "Single Series Chart").attr(txtattr);
20879                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
20880                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
20881                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
20882                 
20883                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
20884                 r.barchart(330, 10, 300, 220, data1);
20885                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
20886                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
20887                 */
20888                 
20889                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
20890                 // r.barchart(30, 30, 560, 250,  xdata, {
20891                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
20892                 //     axis : "0 0 1 1",
20893                 //     axisxlabels :  xdata
20894                 //     //yvalues : cols,
20895                    
20896                 // });
20897 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
20898 //        
20899 //        this.load(null,xdata,{
20900 //                axis : "0 0 1 1",
20901 //                axisxlabels :  xdata
20902 //                });
20903
20904     },
20905
20906     load : function(graphtype,xdata,opts){
20907         this.raphael.clear();
20908         if(!graphtype) {
20909             graphtype = this.graphtype;
20910         }
20911         if(!opts){
20912             opts = this.opts;
20913         }
20914         var r = this.raphael,
20915             fin = function () {
20916                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
20917             },
20918             fout = function () {
20919                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
20920             },
20921             pfin = function() {
20922                 this.sector.stop();
20923                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
20924
20925                 if (this.label) {
20926                     this.label[0].stop();
20927                     this.label[0].attr({ r: 7.5 });
20928                     this.label[1].attr({ "font-weight": 800 });
20929                 }
20930             },
20931             pfout = function() {
20932                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
20933
20934                 if (this.label) {
20935                     this.label[0].animate({ r: 5 }, 500, "bounce");
20936                     this.label[1].attr({ "font-weight": 400 });
20937                 }
20938             };
20939
20940         switch(graphtype){
20941             case 'bar':
20942                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
20943                 break;
20944             case 'hbar':
20945                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
20946                 break;
20947             case 'pie':
20948 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
20949 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
20950 //            
20951                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
20952                 
20953                 break;
20954
20955         }
20956         
20957         if(this.title){
20958             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
20959         }
20960         
20961     },
20962     
20963     setTitle: function(o)
20964     {
20965         this.title = o;
20966     },
20967     
20968     initEvents: function() {
20969         
20970         if(!this.href){
20971             this.el.on('click', this.onClick, this);
20972         }
20973     },
20974     
20975     onClick : function(e)
20976     {
20977         Roo.log('img onclick');
20978         this.fireEvent('click', this, e);
20979     }
20980    
20981 });
20982
20983  
20984 /*
20985  * - LGPL
20986  *
20987  * numberBox
20988  * 
20989  */
20990 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20991
20992 /**
20993  * @class Roo.bootstrap.dash.NumberBox
20994  * @extends Roo.bootstrap.Component
20995  * Bootstrap NumberBox class
20996  * @cfg {String} headline Box headline
20997  * @cfg {String} content Box content
20998  * @cfg {String} icon Box icon
20999  * @cfg {String} footer Footer text
21000  * @cfg {String} fhref Footer href
21001  * 
21002  * @constructor
21003  * Create a new NumberBox
21004  * @param {Object} config The config object
21005  */
21006
21007
21008 Roo.bootstrap.dash.NumberBox = function(config){
21009     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21010     
21011 };
21012
21013 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21014     
21015     headline : '',
21016     content : '',
21017     icon : '',
21018     footer : '',
21019     fhref : '',
21020     ficon : '',
21021     
21022     getAutoCreate : function(){
21023         
21024         var cfg = {
21025             tag : 'div',
21026             cls : 'small-box ',
21027             cn : [
21028                 {
21029                     tag : 'div',
21030                     cls : 'inner',
21031                     cn :[
21032                         {
21033                             tag : 'h3',
21034                             cls : 'roo-headline',
21035                             html : this.headline
21036                         },
21037                         {
21038                             tag : 'p',
21039                             cls : 'roo-content',
21040                             html : this.content
21041                         }
21042                     ]
21043                 }
21044             ]
21045         }
21046         
21047         if(this.icon){
21048             cfg.cn.push({
21049                 tag : 'div',
21050                 cls : 'icon',
21051                 cn :[
21052                     {
21053                         tag : 'i',
21054                         cls : 'ion ' + this.icon
21055                     }
21056                 ]
21057             });
21058         }
21059         
21060         if(this.footer){
21061             var footer = {
21062                 tag : 'a',
21063                 cls : 'small-box-footer',
21064                 href : this.fhref || '#',
21065                 html : this.footer
21066             };
21067             
21068             cfg.cn.push(footer);
21069             
21070         }
21071         
21072         return  cfg;
21073     },
21074
21075     onRender : function(ct,position){
21076         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21077
21078
21079        
21080                 
21081     },
21082
21083     setHeadline: function (value)
21084     {
21085         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21086     },
21087     
21088     setFooter: function (value, href)
21089     {
21090         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21091         
21092         if(href){
21093             this.el.select('a.small-box-footer',true).first().attr('href', href);
21094         }
21095         
21096     },
21097
21098     setContent: function (value)
21099     {
21100         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21101     },
21102
21103     initEvents: function() 
21104     {   
21105         
21106     }
21107     
21108 });
21109
21110  
21111 /*
21112  * - LGPL
21113  *
21114  * TabBox
21115  * 
21116  */
21117 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21118
21119 /**
21120  * @class Roo.bootstrap.dash.TabBox
21121  * @extends Roo.bootstrap.Component
21122  * Bootstrap TabBox class
21123  * @cfg {String} title Title of the TabBox
21124  * @cfg {String} icon Icon of the TabBox
21125  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21126  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21127  * 
21128  * @constructor
21129  * Create a new TabBox
21130  * @param {Object} config The config object
21131  */
21132
21133
21134 Roo.bootstrap.dash.TabBox = function(config){
21135     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21136     this.addEvents({
21137         // raw events
21138         /**
21139          * @event addpane
21140          * When a pane is added
21141          * @param {Roo.bootstrap.dash.TabPane} pane
21142          */
21143         "addpane" : true,
21144         /**
21145          * @event activatepane
21146          * When a pane is activated
21147          * @param {Roo.bootstrap.dash.TabPane} pane
21148          */
21149         "activatepane" : true
21150         
21151          
21152     });
21153     
21154     this.panes = [];
21155 };
21156
21157 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21158
21159     title : '',
21160     icon : false,
21161     showtabs : true,
21162     tabScrollable : false,
21163     
21164     getChildContainer : function()
21165     {
21166         return this.el.select('.tab-content', true).first();
21167     },
21168     
21169     getAutoCreate : function(){
21170         
21171         var header = {
21172             tag: 'li',
21173             cls: 'pull-left header',
21174             html: this.title,
21175             cn : []
21176         };
21177         
21178         if(this.icon){
21179             header.cn.push({
21180                 tag: 'i',
21181                 cls: 'fa ' + this.icon
21182             });
21183         }
21184         
21185         var h = {
21186             tag: 'ul',
21187             cls: 'nav nav-tabs pull-right',
21188             cn: [
21189                 header
21190             ]
21191         };
21192         
21193         if(this.tabScrollable){
21194             h = {
21195                 tag: 'div',
21196                 cls: 'tab-header',
21197                 cn: [
21198                     {
21199                         tag: 'ul',
21200                         cls: 'nav nav-tabs pull-right',
21201                         cn: [
21202                             header
21203                         ]
21204                     }
21205                 ]
21206             }
21207         }
21208         
21209         var cfg = {
21210             tag: 'div',
21211             cls: 'nav-tabs-custom',
21212             cn: [
21213                 h,
21214                 {
21215                     tag: 'div',
21216                     cls: 'tab-content no-padding',
21217                     cn: []
21218                 }
21219             ]
21220         }
21221
21222         return  cfg;
21223     },
21224     initEvents : function()
21225     {
21226         //Roo.log('add add pane handler');
21227         this.on('addpane', this.onAddPane, this);
21228     },
21229      /**
21230      * Updates the box title
21231      * @param {String} html to set the title to.
21232      */
21233     setTitle : function(value)
21234     {
21235         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21236     },
21237     onAddPane : function(pane)
21238     {
21239         this.panes.push(pane);
21240         //Roo.log('addpane');
21241         //Roo.log(pane);
21242         // tabs are rendere left to right..
21243         if(!this.showtabs){
21244             return;
21245         }
21246         
21247         var ctr = this.el.select('.nav-tabs', true).first();
21248          
21249          
21250         var existing = ctr.select('.nav-tab',true);
21251         var qty = existing.getCount();;
21252         
21253         
21254         var tab = ctr.createChild({
21255             tag : 'li',
21256             cls : 'nav-tab' + (qty ? '' : ' active'),
21257             cn : [
21258                 {
21259                     tag : 'a',
21260                     href:'#',
21261                     html : pane.title
21262                 }
21263             ]
21264         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21265         pane.tab = tab;
21266         
21267         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21268         if (!qty) {
21269             pane.el.addClass('active');
21270         }
21271         
21272                 
21273     },
21274     onTabClick : function(ev,un,ob,pane)
21275     {
21276         //Roo.log('tab - prev default');
21277         ev.preventDefault();
21278         
21279         
21280         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21281         pane.tab.addClass('active');
21282         //Roo.log(pane.title);
21283         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21284         // technically we should have a deactivate event.. but maybe add later.
21285         // and it should not de-activate the selected tab...
21286         this.fireEvent('activatepane', pane);
21287         pane.el.addClass('active');
21288         pane.fireEvent('activate');
21289         
21290         
21291     },
21292     
21293     getActivePane : function()
21294     {
21295         var r = false;
21296         Roo.each(this.panes, function(p) {
21297             if(p.el.hasClass('active')){
21298                 r = p;
21299                 return false;
21300             }
21301             
21302             return;
21303         });
21304         
21305         return r;
21306     }
21307     
21308     
21309 });
21310
21311  
21312 /*
21313  * - LGPL
21314  *
21315  * Tab pane
21316  * 
21317  */
21318 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21319 /**
21320  * @class Roo.bootstrap.TabPane
21321  * @extends Roo.bootstrap.Component
21322  * Bootstrap TabPane class
21323  * @cfg {Boolean} active (false | true) Default false
21324  * @cfg {String} title title of panel
21325
21326  * 
21327  * @constructor
21328  * Create a new TabPane
21329  * @param {Object} config The config object
21330  */
21331
21332 Roo.bootstrap.dash.TabPane = function(config){
21333     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21334     
21335     this.addEvents({
21336         // raw events
21337         /**
21338          * @event activate
21339          * When a pane is activated
21340          * @param {Roo.bootstrap.dash.TabPane} pane
21341          */
21342         "activate" : true
21343          
21344     });
21345 };
21346
21347 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21348     
21349     active : false,
21350     title : '',
21351     
21352     // the tabBox that this is attached to.
21353     tab : false,
21354      
21355     getAutoCreate : function() 
21356     {
21357         var cfg = {
21358             tag: 'div',
21359             cls: 'tab-pane'
21360         }
21361         
21362         if(this.active){
21363             cfg.cls += ' active';
21364         }
21365         
21366         return cfg;
21367     },
21368     initEvents  : function()
21369     {
21370         //Roo.log('trigger add pane handler');
21371         this.parent().fireEvent('addpane', this)
21372     },
21373     
21374      /**
21375      * Updates the tab title 
21376      * @param {String} html to set the title to.
21377      */
21378     setTitle: function(str)
21379     {
21380         if (!this.tab) {
21381             return;
21382         }
21383         this.title = str;
21384         this.tab.select('a', true).first().dom.innerHTML = str;
21385         
21386     }
21387     
21388     
21389     
21390 });
21391
21392  
21393
21394
21395  /*
21396  * - LGPL
21397  *
21398  * menu
21399  * 
21400  */
21401 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21402
21403 /**
21404  * @class Roo.bootstrap.menu.Menu
21405  * @extends Roo.bootstrap.Component
21406  * Bootstrap Menu class - container for Menu
21407  * @cfg {String} html Text of the menu
21408  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21409  * @cfg {String} icon Font awesome icon
21410  * @cfg {String} pos Menu align to (top | bottom) default bottom
21411  * 
21412  * 
21413  * @constructor
21414  * Create a new Menu
21415  * @param {Object} config The config object
21416  */
21417
21418
21419 Roo.bootstrap.menu.Menu = function(config){
21420     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21421     
21422     this.addEvents({
21423         /**
21424          * @event beforeshow
21425          * Fires before this menu is displayed
21426          * @param {Roo.bootstrap.menu.Menu} this
21427          */
21428         beforeshow : true,
21429         /**
21430          * @event beforehide
21431          * Fires before this menu is hidden
21432          * @param {Roo.bootstrap.menu.Menu} this
21433          */
21434         beforehide : true,
21435         /**
21436          * @event show
21437          * Fires after this menu is displayed
21438          * @param {Roo.bootstrap.menu.Menu} this
21439          */
21440         show : true,
21441         /**
21442          * @event hide
21443          * Fires after this menu is hidden
21444          * @param {Roo.bootstrap.menu.Menu} this
21445          */
21446         hide : true,
21447         /**
21448          * @event click
21449          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21450          * @param {Roo.bootstrap.menu.Menu} this
21451          * @param {Roo.EventObject} e
21452          */
21453         click : true
21454     });
21455     
21456 };
21457
21458 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21459     
21460     submenu : false,
21461     html : '',
21462     weight : 'default',
21463     icon : false,
21464     pos : 'bottom',
21465     
21466     
21467     getChildContainer : function() {
21468         if(this.isSubMenu){
21469             return this.el;
21470         }
21471         
21472         return this.el.select('ul.dropdown-menu', true).first();  
21473     },
21474     
21475     getAutoCreate : function()
21476     {
21477         var text = [
21478             {
21479                 tag : 'span',
21480                 cls : 'roo-menu-text',
21481                 html : this.html
21482             }
21483         ];
21484         
21485         if(this.icon){
21486             text.unshift({
21487                 tag : 'i',
21488                 cls : 'fa ' + this.icon
21489             })
21490         }
21491         
21492         
21493         var cfg = {
21494             tag : 'div',
21495             cls : 'btn-group',
21496             cn : [
21497                 {
21498                     tag : 'button',
21499                     cls : 'dropdown-button btn btn-' + this.weight,
21500                     cn : text
21501                 },
21502                 {
21503                     tag : 'button',
21504                     cls : 'dropdown-toggle btn btn-' + this.weight,
21505                     cn : [
21506                         {
21507                             tag : 'span',
21508                             cls : 'caret'
21509                         }
21510                     ]
21511                 },
21512                 {
21513                     tag : 'ul',
21514                     cls : 'dropdown-menu'
21515                 }
21516             ]
21517             
21518         };
21519         
21520         if(this.pos == 'top'){
21521             cfg.cls += ' dropup';
21522         }
21523         
21524         if(this.isSubMenu){
21525             cfg = {
21526                 tag : 'ul',
21527                 cls : 'dropdown-menu'
21528             }
21529         }
21530         
21531         return cfg;
21532     },
21533     
21534     onRender : function(ct, position)
21535     {
21536         this.isSubMenu = ct.hasClass('dropdown-submenu');
21537         
21538         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21539     },
21540     
21541     initEvents : function() 
21542     {
21543         if(this.isSubMenu){
21544             return;
21545         }
21546         
21547         this.hidden = true;
21548         
21549         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21550         this.triggerEl.on('click', this.onTriggerPress, this);
21551         
21552         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21553         this.buttonEl.on('click', this.onClick, this);
21554         
21555     },
21556     
21557     list : function()
21558     {
21559         if(this.isSubMenu){
21560             return this.el;
21561         }
21562         
21563         return this.el.select('ul.dropdown-menu', true).first();
21564     },
21565     
21566     onClick : function(e)
21567     {
21568         this.fireEvent("click", this, e);
21569     },
21570     
21571     onTriggerPress  : function(e)
21572     {   
21573         if (this.isVisible()) {
21574             this.hide();
21575         } else {
21576             this.show();
21577         }
21578     },
21579     
21580     isVisible : function(){
21581         return !this.hidden;
21582     },
21583     
21584     show : function()
21585     {
21586         this.fireEvent("beforeshow", this);
21587         
21588         this.hidden = false;
21589         this.el.addClass('open');
21590         
21591         Roo.get(document).on("mouseup", this.onMouseUp, this);
21592         
21593         this.fireEvent("show", this);
21594         
21595         
21596     },
21597     
21598     hide : function()
21599     {
21600         this.fireEvent("beforehide", this);
21601         
21602         this.hidden = true;
21603         this.el.removeClass('open');
21604         
21605         Roo.get(document).un("mouseup", this.onMouseUp);
21606         
21607         this.fireEvent("hide", this);
21608     },
21609     
21610     onMouseUp : function()
21611     {
21612         this.hide();
21613     }
21614     
21615 });
21616
21617  
21618  /*
21619  * - LGPL
21620  *
21621  * menu item
21622  * 
21623  */
21624 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21625
21626 /**
21627  * @class Roo.bootstrap.menu.Item
21628  * @extends Roo.bootstrap.Component
21629  * Bootstrap MenuItem class
21630  * @cfg {Boolean} submenu (true | false) default false
21631  * @cfg {String} html text of the item
21632  * @cfg {String} href the link
21633  * @cfg {Boolean} disable (true | false) default false
21634  * @cfg {Boolean} preventDefault (true | false) default true
21635  * @cfg {String} icon Font awesome icon
21636  * @cfg {String} pos Submenu align to (left | right) default right 
21637  * 
21638  * 
21639  * @constructor
21640  * Create a new Item
21641  * @param {Object} config The config object
21642  */
21643
21644
21645 Roo.bootstrap.menu.Item = function(config){
21646     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
21647     this.addEvents({
21648         /**
21649          * @event mouseover
21650          * Fires when the mouse is hovering over this menu
21651          * @param {Roo.bootstrap.menu.Item} this
21652          * @param {Roo.EventObject} e
21653          */
21654         mouseover : true,
21655         /**
21656          * @event mouseout
21657          * Fires when the mouse exits this menu
21658          * @param {Roo.bootstrap.menu.Item} this
21659          * @param {Roo.EventObject} e
21660          */
21661         mouseout : true,
21662         // raw events
21663         /**
21664          * @event click
21665          * The raw click event for the entire grid.
21666          * @param {Roo.EventObject} e
21667          */
21668         click : true
21669     });
21670 };
21671
21672 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
21673     
21674     submenu : false,
21675     href : '',
21676     html : '',
21677     preventDefault: true,
21678     disable : false,
21679     icon : false,
21680     pos : 'right',
21681     
21682     getAutoCreate : function()
21683     {
21684         var text = [
21685             {
21686                 tag : 'span',
21687                 cls : 'roo-menu-item-text',
21688                 html : this.html
21689             }
21690         ];
21691         
21692         if(this.icon){
21693             text.unshift({
21694                 tag : 'i',
21695                 cls : 'fa ' + this.icon
21696             })
21697         }
21698         
21699         var cfg = {
21700             tag : 'li',
21701             cn : [
21702                 {
21703                     tag : 'a',
21704                     href : this.href || '#',
21705                     cn : text
21706                 }
21707             ]
21708         };
21709         
21710         if(this.disable){
21711             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
21712         }
21713         
21714         if(this.submenu){
21715             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
21716             
21717             if(this.pos == 'left'){
21718                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
21719             }
21720         }
21721         
21722         return cfg;
21723     },
21724     
21725     initEvents : function() 
21726     {
21727         this.el.on('mouseover', this.onMouseOver, this);
21728         this.el.on('mouseout', this.onMouseOut, this);
21729         
21730         this.el.select('a', true).first().on('click', this.onClick, this);
21731         
21732     },
21733     
21734     onClick : function(e)
21735     {
21736         if(this.preventDefault){
21737             e.preventDefault();
21738         }
21739         
21740         this.fireEvent("click", this, e);
21741     },
21742     
21743     onMouseOver : function(e)
21744     {
21745         if(this.submenu && this.pos == 'left'){
21746             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
21747         }
21748         
21749         this.fireEvent("mouseover", this, e);
21750     },
21751     
21752     onMouseOut : function(e)
21753     {
21754         this.fireEvent("mouseout", this, e);
21755     }
21756 });
21757
21758  
21759
21760  /*
21761  * - LGPL
21762  *
21763  * menu separator
21764  * 
21765  */
21766 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21767
21768 /**
21769  * @class Roo.bootstrap.menu.Separator
21770  * @extends Roo.bootstrap.Component
21771  * Bootstrap Separator class
21772  * 
21773  * @constructor
21774  * Create a new Separator
21775  * @param {Object} config The config object
21776  */
21777
21778
21779 Roo.bootstrap.menu.Separator = function(config){
21780     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
21781 };
21782
21783 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
21784     
21785     getAutoCreate : function(){
21786         var cfg = {
21787             tag : 'li',
21788             cls: 'divider'
21789         };
21790         
21791         return cfg;
21792     }
21793    
21794 });
21795
21796  
21797
21798  /*
21799  * - LGPL
21800  *
21801  * Tooltip
21802  * 
21803  */
21804
21805 /**
21806  * @class Roo.bootstrap.Tooltip
21807  * Bootstrap Tooltip class
21808  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
21809  * to determine which dom element triggers the tooltip.
21810  * 
21811  * It needs to add support for additional attributes like tooltip-position
21812  * 
21813  * @constructor
21814  * Create a new Toolti
21815  * @param {Object} config The config object
21816  */
21817
21818 Roo.bootstrap.Tooltip = function(config){
21819     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
21820 };
21821
21822 Roo.apply(Roo.bootstrap.Tooltip, {
21823     /**
21824      * @function init initialize tooltip monitoring.
21825      * @static
21826      */
21827     currentEl : false,
21828     currentTip : false,
21829     currentRegion : false,
21830     
21831     //  init : delay?
21832     
21833     init : function()
21834     {
21835         Roo.get(document).on('mouseover', this.enter ,this);
21836         Roo.get(document).on('mouseout', this.leave, this);
21837          
21838         
21839         this.currentTip = new Roo.bootstrap.Tooltip();
21840     },
21841     
21842     enter : function(ev)
21843     {
21844         var dom = ev.getTarget();
21845         
21846         //Roo.log(['enter',dom]);
21847         var el = Roo.fly(dom);
21848         if (this.currentEl) {
21849             //Roo.log(dom);
21850             //Roo.log(this.currentEl);
21851             //Roo.log(this.currentEl.contains(dom));
21852             if (this.currentEl == el) {
21853                 return;
21854             }
21855             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
21856                 return;
21857             }
21858
21859         }
21860         
21861         
21862         
21863         if (this.currentTip.el) {
21864             this.currentTip.el.hide(); // force hiding...
21865         }    
21866         //Roo.log(ev);
21867         var bindEl = el;
21868         
21869         // you can not look for children, as if el is the body.. then everythign is the child..
21870         if (!el.attr('tooltip')) { //
21871             if (!el.select("[tooltip]").elements.length) {
21872                 return;
21873             }
21874             // is the mouse over this child...?
21875             bindEl = el.select("[tooltip]").first();
21876             var xy = ev.getXY();
21877             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
21878                 //Roo.log("not in region.");
21879                 return;
21880             }
21881             //Roo.log("child element over..");
21882             
21883         }
21884         this.currentEl = bindEl;
21885         this.currentTip.bind(bindEl);
21886         this.currentRegion = Roo.lib.Region.getRegion(dom);
21887         this.currentTip.enter();
21888         
21889     },
21890     leave : function(ev)
21891     {
21892         var dom = ev.getTarget();
21893         //Roo.log(['leave',dom]);
21894         if (!this.currentEl) {
21895             return;
21896         }
21897         
21898         
21899         if (dom != this.currentEl.dom) {
21900             return;
21901         }
21902         var xy = ev.getXY();
21903         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
21904             return;
21905         }
21906         // only activate leave if mouse cursor is outside... bounding box..
21907         
21908         
21909         
21910         
21911         if (this.currentTip) {
21912             this.currentTip.leave();
21913         }
21914         //Roo.log('clear currentEl');
21915         this.currentEl = false;
21916         
21917         
21918     },
21919     alignment : {
21920         'left' : ['r-l', [-2,0], 'right'],
21921         'right' : ['l-r', [2,0], 'left'],
21922         'bottom' : ['t-b', [0,2], 'top'],
21923         'top' : [ 'b-t', [0,-2], 'bottom']
21924     }
21925     
21926 });
21927
21928
21929 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
21930     
21931     
21932     bindEl : false,
21933     
21934     delay : null, // can be { show : 300 , hide: 500}
21935     
21936     timeout : null,
21937     
21938     hoverState : null, //???
21939     
21940     placement : 'bottom', 
21941     
21942     getAutoCreate : function(){
21943     
21944         var cfg = {
21945            cls : 'tooltip',
21946            role : 'tooltip',
21947            cn : [
21948                 {
21949                     cls : 'tooltip-arrow'
21950                 },
21951                 {
21952                     cls : 'tooltip-inner'
21953                 }
21954            ]
21955         };
21956         
21957         return cfg;
21958     },
21959     bind : function(el)
21960     {
21961         this.bindEl = el;
21962     },
21963       
21964     
21965     enter : function () {
21966        
21967         if (this.timeout != null) {
21968             clearTimeout(this.timeout);
21969         }
21970         
21971         this.hoverState = 'in';
21972          //Roo.log("enter - show");
21973         if (!this.delay || !this.delay.show) {
21974             this.show();
21975             return;
21976         }
21977         var _t = this;
21978         this.timeout = setTimeout(function () {
21979             if (_t.hoverState == 'in') {
21980                 _t.show();
21981             }
21982         }, this.delay.show);
21983     },
21984     leave : function()
21985     {
21986         clearTimeout(this.timeout);
21987     
21988         this.hoverState = 'out';
21989          if (!this.delay || !this.delay.hide) {
21990             this.hide();
21991             return;
21992         }
21993        
21994         var _t = this;
21995         this.timeout = setTimeout(function () {
21996             //Roo.log("leave - timeout");
21997             
21998             if (_t.hoverState == 'out') {
21999                 _t.hide();
22000                 Roo.bootstrap.Tooltip.currentEl = false;
22001             }
22002         }, delay);
22003     },
22004     
22005     show : function ()
22006     {
22007         if (!this.el) {
22008             this.render(document.body);
22009         }
22010         // set content.
22011         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22012         
22013         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22014         
22015         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22016         
22017         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22018         
22019         var placement = typeof this.placement == 'function' ?
22020             this.placement.call(this, this.el, on_el) :
22021             this.placement;
22022             
22023         var autoToken = /\s?auto?\s?/i;
22024         var autoPlace = autoToken.test(placement);
22025         if (autoPlace) {
22026             placement = placement.replace(autoToken, '') || 'top';
22027         }
22028         
22029         //this.el.detach()
22030         //this.el.setXY([0,0]);
22031         this.el.show();
22032         //this.el.dom.style.display='block';
22033         this.el.addClass(placement);
22034         
22035         //this.el.appendTo(on_el);
22036         
22037         var p = this.getPosition();
22038         var box = this.el.getBox();
22039         
22040         if (autoPlace) {
22041             // fixme..
22042         }
22043         var align = Roo.bootstrap.Tooltip.alignment[placement];
22044         this.el.alignTo(this.bindEl, align[0],align[1]);
22045         //var arrow = this.el.select('.arrow',true).first();
22046         //arrow.set(align[2], 
22047         
22048         this.el.addClass('in fade');
22049         this.hoverState = null;
22050         
22051         if (this.el.hasClass('fade')) {
22052             // fade it?
22053         }
22054         
22055     },
22056     hide : function()
22057     {
22058          
22059         if (!this.el) {
22060             return;
22061         }
22062         //this.el.setXY([0,0]);
22063         this.el.removeClass('in');
22064         //this.el.hide();
22065         
22066     }
22067     
22068 });
22069  
22070
22071  /*
22072  * - LGPL
22073  *
22074  * Location Picker
22075  * 
22076  */
22077
22078 /**
22079  * @class Roo.bootstrap.LocationPicker
22080  * @extends Roo.bootstrap.Component
22081  * Bootstrap LocationPicker class
22082  * @cfg {Number} latitude Position when init default 0
22083  * @cfg {Number} longitude Position when init default 0
22084  * @cfg {Number} zoom default 15
22085  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22086  * @cfg {Boolean} mapTypeControl default false
22087  * @cfg {Boolean} disableDoubleClickZoom default false
22088  * @cfg {Boolean} scrollwheel default true
22089  * @cfg {Boolean} streetViewControl default false
22090  * @cfg {Number} radius default 0
22091  * @cfg {String} locationName
22092  * @cfg {Boolean} draggable default true
22093  * @cfg {Boolean} enableAutocomplete default false
22094  * @cfg {Boolean} enableReverseGeocode default true
22095  * @cfg {String} markerTitle
22096  * 
22097  * @constructor
22098  * Create a new LocationPicker
22099  * @param {Object} config The config object
22100  */
22101
22102
22103 Roo.bootstrap.LocationPicker = function(config){
22104     
22105     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22106     
22107     this.addEvents({
22108         /**
22109          * @event initial
22110          * Fires when the picker initialized.
22111          * @param {Roo.bootstrap.LocationPicker} this
22112          * @param {Google Location} location
22113          */
22114         initial : true,
22115         /**
22116          * @event positionchanged
22117          * Fires when the picker position changed.
22118          * @param {Roo.bootstrap.LocationPicker} this
22119          * @param {Google Location} location
22120          */
22121         positionchanged : true,
22122         /**
22123          * @event resize
22124          * Fires when the map resize.
22125          * @param {Roo.bootstrap.LocationPicker} this
22126          */
22127         resize : true,
22128         /**
22129          * @event show
22130          * Fires when the map show.
22131          * @param {Roo.bootstrap.LocationPicker} this
22132          */
22133         show : true,
22134         /**
22135          * @event hide
22136          * Fires when the map hide.
22137          * @param {Roo.bootstrap.LocationPicker} this
22138          */
22139         hide : true,
22140         /**
22141          * @event mapClick
22142          * Fires when click the map.
22143          * @param {Roo.bootstrap.LocationPicker} this
22144          * @param {Map event} e
22145          */
22146         mapClick : true,
22147         /**
22148          * @event mapRightClick
22149          * Fires when right click the map.
22150          * @param {Roo.bootstrap.LocationPicker} this
22151          * @param {Map event} e
22152          */
22153         mapRightClick : true,
22154         /**
22155          * @event markerClick
22156          * Fires when click the marker.
22157          * @param {Roo.bootstrap.LocationPicker} this
22158          * @param {Map event} e
22159          */
22160         markerClick : true,
22161         /**
22162          * @event markerRightClick
22163          * Fires when right click the marker.
22164          * @param {Roo.bootstrap.LocationPicker} this
22165          * @param {Map event} e
22166          */
22167         markerRightClick : true,
22168         /**
22169          * @event OverlayViewDraw
22170          * Fires when OverlayView Draw
22171          * @param {Roo.bootstrap.LocationPicker} this
22172          */
22173         OverlayViewDraw : true,
22174         /**
22175          * @event OverlayViewOnAdd
22176          * Fires when OverlayView Draw
22177          * @param {Roo.bootstrap.LocationPicker} this
22178          */
22179         OverlayViewOnAdd : true,
22180         /**
22181          * @event OverlayViewOnRemove
22182          * Fires when OverlayView Draw
22183          * @param {Roo.bootstrap.LocationPicker} this
22184          */
22185         OverlayViewOnRemove : true,
22186         /**
22187          * @event OverlayViewShow
22188          * Fires when OverlayView Draw
22189          * @param {Roo.bootstrap.LocationPicker} this
22190          * @param {Pixel} cpx
22191          */
22192         OverlayViewShow : true,
22193         /**
22194          * @event OverlayViewHide
22195          * Fires when OverlayView Draw
22196          * @param {Roo.bootstrap.LocationPicker} this
22197          */
22198         OverlayViewHide : true
22199     });
22200         
22201 };
22202
22203 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22204     
22205     gMapContext: false,
22206     
22207     latitude: 0,
22208     longitude: 0,
22209     zoom: 15,
22210     mapTypeId: false,
22211     mapTypeControl: false,
22212     disableDoubleClickZoom: false,
22213     scrollwheel: true,
22214     streetViewControl: false,
22215     radius: 0,
22216     locationName: '',
22217     draggable: true,
22218     enableAutocomplete: false,
22219     enableReverseGeocode: true,
22220     markerTitle: '',
22221     
22222     getAutoCreate: function()
22223     {
22224
22225         var cfg = {
22226             tag: 'div',
22227             cls: 'roo-location-picker'
22228         };
22229         
22230         return cfg
22231     },
22232     
22233     initEvents: function(ct, position)
22234     {       
22235         if(!this.el.getWidth() || this.isApplied()){
22236             return;
22237         }
22238         
22239         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22240         
22241         this.initial();
22242     },
22243     
22244     initial: function()
22245     {
22246         if(!this.mapTypeId){
22247             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22248         }
22249         
22250         this.gMapContext = this.GMapContext();
22251         
22252         this.initOverlayView();
22253         
22254         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22255         
22256         var _this = this;
22257                 
22258         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22259             _this.setPosition(_this.gMapContext.marker.position);
22260         });
22261         
22262         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22263             _this.fireEvent('mapClick', this, event);
22264             
22265         });
22266
22267         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22268             _this.fireEvent('mapRightClick', this, event);
22269             
22270         });
22271         
22272         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22273             _this.fireEvent('markerClick', this, event);
22274             
22275         });
22276
22277         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22278             _this.fireEvent('markerRightClick', this, event);
22279             
22280         });
22281         
22282         this.setPosition(this.gMapContext.location);
22283         
22284         this.fireEvent('initial', this, this.gMapContext.location);
22285     },
22286     
22287     initOverlayView: function()
22288     {
22289         var _this = this;
22290         
22291         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22292             
22293             draw: function()
22294             {
22295                 _this.fireEvent('OverlayViewDraw', _this);
22296             },
22297             
22298             onAdd: function()
22299             {
22300                 _this.fireEvent('OverlayViewOnAdd', _this);
22301             },
22302             
22303             onRemove: function()
22304             {
22305                 _this.fireEvent('OverlayViewOnRemove', _this);
22306             },
22307             
22308             show: function(cpx)
22309             {
22310                 _this.fireEvent('OverlayViewShow', _this, cpx);
22311             },
22312             
22313             hide: function()
22314             {
22315                 _this.fireEvent('OverlayViewHide', _this);
22316             }
22317             
22318         });
22319     },
22320     
22321     fromLatLngToContainerPixel: function(event)
22322     {
22323         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22324     },
22325     
22326     isApplied: function() 
22327     {
22328         return this.getGmapContext() == false ? false : true;
22329     },
22330     
22331     getGmapContext: function() 
22332     {
22333         return this.gMapContext
22334     },
22335     
22336     GMapContext: function() 
22337     {
22338         var position = new google.maps.LatLng(this.latitude, this.longitude);
22339         
22340         var _map = new google.maps.Map(this.el.dom, {
22341             center: position,
22342             zoom: this.zoom,
22343             mapTypeId: this.mapTypeId,
22344             mapTypeControl: this.mapTypeControl,
22345             disableDoubleClickZoom: this.disableDoubleClickZoom,
22346             scrollwheel: this.scrollwheel,
22347             streetViewControl: this.streetViewControl,
22348             locationName: this.locationName,
22349             draggable: this.draggable,
22350             enableAutocomplete: this.enableAutocomplete,
22351             enableReverseGeocode: this.enableReverseGeocode
22352         });
22353         
22354         var _marker = new google.maps.Marker({
22355             position: position,
22356             map: _map,
22357             title: this.markerTitle,
22358             draggable: this.draggable
22359         });
22360         
22361         return {
22362             map: _map,
22363             marker: _marker,
22364             circle: null,
22365             location: position,
22366             radius: this.radius,
22367             locationName: this.locationName,
22368             addressComponents: {
22369                 formatted_address: null,
22370                 addressLine1: null,
22371                 addressLine2: null,
22372                 streetName: null,
22373                 streetNumber: null,
22374                 city: null,
22375                 district: null,
22376                 state: null,
22377                 stateOrProvince: null
22378             },
22379             settings: this,
22380             domContainer: this.el.dom,
22381             geodecoder: new google.maps.Geocoder()
22382         };
22383     },
22384     
22385     drawCircle: function(center, radius, options) 
22386     {
22387         if (this.gMapContext.circle != null) {
22388             this.gMapContext.circle.setMap(null);
22389         }
22390         if (radius > 0) {
22391             radius *= 1;
22392             options = Roo.apply({}, options, {
22393                 strokeColor: "#0000FF",
22394                 strokeOpacity: .35,
22395                 strokeWeight: 2,
22396                 fillColor: "#0000FF",
22397                 fillOpacity: .2
22398             });
22399             
22400             options.map = this.gMapContext.map;
22401             options.radius = radius;
22402             options.center = center;
22403             this.gMapContext.circle = new google.maps.Circle(options);
22404             return this.gMapContext.circle;
22405         }
22406         
22407         return null;
22408     },
22409     
22410     setPosition: function(location) 
22411     {
22412         this.gMapContext.location = location;
22413         this.gMapContext.marker.setPosition(location);
22414         this.gMapContext.map.panTo(location);
22415         this.drawCircle(location, this.gMapContext.radius, {});
22416         
22417         var _this = this;
22418         
22419         if (this.gMapContext.settings.enableReverseGeocode) {
22420             this.gMapContext.geodecoder.geocode({
22421                 latLng: this.gMapContext.location
22422             }, function(results, status) {
22423                 
22424                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22425                     _this.gMapContext.locationName = results[0].formatted_address;
22426                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22427                     
22428                     _this.fireEvent('positionchanged', this, location);
22429                 }
22430             });
22431             
22432             return;
22433         }
22434         
22435         this.fireEvent('positionchanged', this, location);
22436     },
22437     
22438     resize: function()
22439     {
22440         google.maps.event.trigger(this.gMapContext.map, "resize");
22441         
22442         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22443         
22444         this.fireEvent('resize', this);
22445     },
22446     
22447     setPositionByLatLng: function(latitude, longitude)
22448     {
22449         this.setPosition(new google.maps.LatLng(latitude, longitude));
22450     },
22451     
22452     getCurrentPosition: function() 
22453     {
22454         return {
22455             latitude: this.gMapContext.location.lat(),
22456             longitude: this.gMapContext.location.lng()
22457         };
22458     },
22459     
22460     getAddressName: function() 
22461     {
22462         return this.gMapContext.locationName;
22463     },
22464     
22465     getAddressComponents: function() 
22466     {
22467         return this.gMapContext.addressComponents;
22468     },
22469     
22470     address_component_from_google_geocode: function(address_components) 
22471     {
22472         var result = {};
22473         
22474         for (var i = 0; i < address_components.length; i++) {
22475             var component = address_components[i];
22476             if (component.types.indexOf("postal_code") >= 0) {
22477                 result.postalCode = component.short_name;
22478             } else if (component.types.indexOf("street_number") >= 0) {
22479                 result.streetNumber = component.short_name;
22480             } else if (component.types.indexOf("route") >= 0) {
22481                 result.streetName = component.short_name;
22482             } else if (component.types.indexOf("neighborhood") >= 0) {
22483                 result.city = component.short_name;
22484             } else if (component.types.indexOf("locality") >= 0) {
22485                 result.city = component.short_name;
22486             } else if (component.types.indexOf("sublocality") >= 0) {
22487                 result.district = component.short_name;
22488             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22489                 result.stateOrProvince = component.short_name;
22490             } else if (component.types.indexOf("country") >= 0) {
22491                 result.country = component.short_name;
22492             }
22493         }
22494         
22495         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22496         result.addressLine2 = "";
22497         return result;
22498     },
22499     
22500     setZoomLevel: function(zoom)
22501     {
22502         this.gMapContext.map.setZoom(zoom);
22503     },
22504     
22505     show: function()
22506     {
22507         if(!this.el){
22508             return;
22509         }
22510         
22511         this.el.show();
22512         
22513         this.resize();
22514         
22515         this.fireEvent('show', this);
22516     },
22517     
22518     hide: function()
22519     {
22520         if(!this.el){
22521             return;
22522         }
22523         
22524         this.el.hide();
22525         
22526         this.fireEvent('hide', this);
22527     }
22528     
22529 });
22530
22531 Roo.apply(Roo.bootstrap.LocationPicker, {
22532     
22533     OverlayView : function(map, options)
22534     {
22535         options = options || {};
22536         
22537         this.setMap(map);
22538     }
22539     
22540     
22541 });/*
22542  * - LGPL
22543  *
22544  * Alert
22545  * 
22546  */
22547
22548 /**
22549  * @class Roo.bootstrap.Alert
22550  * @extends Roo.bootstrap.Component
22551  * Bootstrap Alert class
22552  * @cfg {String} title The title of alert
22553  * @cfg {String} html The content of alert
22554  * @cfg {String} weight (  success | info | warning | danger )
22555  * @cfg {String} faicon font-awesomeicon
22556  * 
22557  * @constructor
22558  * Create a new alert
22559  * @param {Object} config The config object
22560  */
22561
22562
22563 Roo.bootstrap.Alert = function(config){
22564     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22565     
22566 };
22567
22568 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22569     
22570     title: '',
22571     html: '',
22572     weight: false,
22573     faicon: false,
22574     
22575     getAutoCreate : function()
22576     {
22577         
22578         var cfg = {
22579             tag : 'div',
22580             cls : 'alert',
22581             cn : [
22582                 {
22583                     tag : 'i',
22584                     cls : 'roo-alert-icon'
22585                     
22586                 },
22587                 {
22588                     tag : 'b',
22589                     cls : 'roo-alert-title',
22590                     html : this.title
22591                 },
22592                 {
22593                     tag : 'span',
22594                     cls : 'roo-alert-text',
22595                     html : this.html
22596                 }
22597             ]
22598         };
22599         
22600         if(this.faicon){
22601             cfg.cn[0].cls += ' fa ' + this.faicon;
22602         }
22603         
22604         if(this.weight){
22605             cfg.cls += ' alert-' + this.weight;
22606         }
22607         
22608         return cfg;
22609     },
22610     
22611     initEvents: function() 
22612     {
22613         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22614     },
22615     
22616     setTitle : function(str)
22617     {
22618         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
22619     },
22620     
22621     setText : function(str)
22622     {
22623         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
22624     },
22625     
22626     setWeight : function(weight)
22627     {
22628         if(this.weight){
22629             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
22630         }
22631         
22632         this.weight = weight;
22633         
22634         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
22635     },
22636     
22637     setIcon : function(icon)
22638     {
22639         if(this.faicon){
22640             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
22641         }
22642         
22643         this.faicon = icon
22644         
22645         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
22646     },
22647     
22648     hide: function() 
22649     {
22650         this.el.hide();   
22651     },
22652     
22653     show: function() 
22654     {  
22655         this.el.show();   
22656     }
22657     
22658 });
22659
22660