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  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228             skip_children = false;
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233                 if (!cn.el) {
234                     skip_children = true;
235                 }
236                 
237              } else {
238                  
239                 // actually if flexy:foreach is found, we really want to create 
240                 // multiple copies here...
241                 //Roo.log('render');
242                 //Roo.log(this[cntr]());
243                 cn.render(this[cntr](true));
244              }
245             // then add the element..
246         }
247         
248         
249         // handle the kids..
250         
251         var nitems = [];
252         /*
253         if (typeof (tree.menu) != 'undefined') {
254             tree.menu.parentType = cn.xtype;
255             tree.menu.triggerEl = cn.el;
256             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
257             
258         }
259         */
260         if (!tree.items || !tree.items.length) {
261             cn.items = nitems;
262             return cn;
263         }
264         var items = tree.items;
265         delete tree.items;
266         
267         //Roo.log(items.length);
268             // add the items..
269         if (!skip_children) {    
270             for(var i =0;i < items.length;i++) {
271                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
272             }
273         }
274         
275         cn.items = nitems;
276         
277         return cn;
278     }
279     
280     
281     
282     
283 });
284
285  /*
286  * - LGPL
287  *
288  * Body
289  * 
290  */
291
292 /**
293  * @class Roo.bootstrap.Body
294  * @extends Roo.bootstrap.Component
295  * Bootstrap Body class
296  * 
297  * @constructor
298  * Create a new body
299  * @param {Object} config The config object
300  */
301
302 Roo.bootstrap.Body = function(config){
303     Roo.bootstrap.Body.superclass.constructor.call(this, config);
304     this.el = Roo.get(document.body);
305     if (this.cls && this.cls.length) {
306         Roo.get(document.body).addClass(this.cls);
307     }
308 };
309
310 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
311       
312         autoCreate : {
313         cls: 'container'
314     },
315     onRender : function(ct, position)
316     {
317        /* Roo.log("Roo.bootstrap.Body - onRender");
318         if (this.cls && this.cls.length) {
319             Roo.get(document.body).addClass(this.cls);
320         }
321         // style??? xttr???
322         */
323     }
324     
325     
326  
327    
328 });
329
330  /*
331  * - LGPL
332  *
333  * button group
334  * 
335  */
336
337
338 /**
339  * @class Roo.bootstrap.ButtonGroup
340  * @extends Roo.bootstrap.Component
341  * Bootstrap ButtonGroup class
342  * @cfg {String} size lg | sm | xs (default empty normal)
343  * @cfg {String} align vertical | justified  (default none)
344  * @cfg {String} direction up | down (default down)
345  * @cfg {Boolean} toolbar false | true
346  * @cfg {Boolean} btn true | false
347  * 
348  * 
349  * @constructor
350  * Create a new Input
351  * @param {Object} config The config object
352  */
353
354 Roo.bootstrap.ButtonGroup = function(config){
355     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
356 };
357
358 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
359     
360     size: '',
361     align: '',
362     direction: '',
363     toolbar: false,
364     btn: true,
365
366     getAutoCreate : function(){
367         var cfg = {
368             cls: 'btn-group',
369             html : null
370         }
371         
372         cfg.html = this.html || cfg.html;
373         
374         if (this.toolbar) {
375             cfg = {
376                 cls: 'btn-toolbar',
377                 html: null
378             }
379             
380             return cfg;
381         }
382         
383         if (['vertical','justified'].indexOf(this.align)!==-1) {
384             cfg.cls = 'btn-group-' + this.align;
385             
386             if (this.align == 'justified') {
387                 console.log(this.items);
388             }
389         }
390         
391         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
392             cfg.cls += ' btn-group-' + this.size;
393         }
394         
395         if (this.direction == 'up') {
396             cfg.cls += ' dropup' ;
397         }
398         
399         return cfg;
400     }
401    
402 });
403
404  /*
405  * - LGPL
406  *
407  * button
408  * 
409  */
410
411 /**
412  * @class Roo.bootstrap.Button
413  * @extends Roo.bootstrap.Component
414  * Bootstrap Button class
415  * @cfg {String} html The button content
416  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
417  * @cfg {String} size empty | lg | sm | xs
418  * @cfg {String} tag empty | a | input | submit
419  * @cfg {String} href empty or href
420  * @cfg {Boolean} disabled false | true
421  * @cfg {Boolean} isClose false | true
422  * @cfg {String} glyphicon empty | 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
423  * @cfg {String} badge text for badge
424  * @cfg {String} theme default (or empty) | glow
425  * @cfg {Boolean} inverse false | true
426  * @cfg {Boolean} toggle false | true
427  * @cfg {String} ontext text for on toggle state
428  * @cfg {String} offtext text for off toggle state
429  * @cfg {Boolean} defaulton true | false
430  * @cfg {Boolean} preventDefault (true | false) default true
431  * @cfg {Boolean} removeClass true | false remove the standard class..
432  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
433  * 
434  * @constructor
435  * Create a new button
436  * @param {Object} config The config object
437  */
438
439
440 Roo.bootstrap.Button = function(config){
441     Roo.bootstrap.Button.superclass.constructor.call(this, config);
442     this.addEvents({
443         // raw events
444         /**
445          * @event click
446          * When a butotn is pressed
447          * @param {Roo.EventObject} e
448          */
449         "click" : true,
450          /**
451          * @event toggle
452          * After the button has been toggles
453          * @param {Roo.EventObject} e
454          * @param {boolean} pressed (also available as button.pressed)
455          */
456         "toggle" : true
457     });
458 };
459
460 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
461     html: false,
462     active: false,
463     weight: '',
464     size: '',
465     tag: 'button',
466     href: '',
467     disabled: false,
468     isClose: false,
469     glyphicon: '',
470     badge: '',
471     theme: 'default',
472     inverse: false,
473     
474     toggle: false,
475     ontext: 'ON',
476     offtext: 'OFF',
477     defaulton: true,
478     preventDefault: true,
479     removeClass: false,
480     name: false,
481     target: false,
482     
483     
484     pressed : null,
485      
486     
487     getAutoCreate : function(){
488         
489         var cfg = {
490             tag : 'button',
491             cls : 'roo-button',
492             html: ''
493         };
494         
495         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
496             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
497             this.tag = 'button';
498         } else {
499             cfg.tag = this.tag;
500         }
501         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
502         
503         if (this.toggle == true) {
504             cfg={
505                 tag: 'div',
506                 cls: 'slider-frame roo-button',
507                 cn: [
508                     {
509                         tag: 'span',
510                         'data-on-text':'ON',
511                         'data-off-text':'OFF',
512                         cls: 'slider-button',
513                         html: this.offtext
514                     }
515                 ]
516             };
517             
518             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
519                 cfg.cls += ' '+this.weight;
520             }
521             
522             return cfg;
523         }
524         
525         if (this.isClose) {
526             cfg.cls += ' close';
527             
528             cfg["aria-hidden"] = true;
529             
530             cfg.html = "&times;";
531             
532             return cfg;
533         }
534         
535          
536         if (this.theme==='default') {
537             cfg.cls = 'btn roo-button';
538             
539             //if (this.parentType != 'Navbar') {
540             this.weight = this.weight.length ?  this.weight : 'default';
541             //}
542             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
543                 
544                 cfg.cls += ' btn-' + this.weight;
545             }
546         } else if (this.theme==='glow') {
547             
548             cfg.tag = 'a';
549             cfg.cls = 'btn-glow roo-button';
550             
551             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
552                 
553                 cfg.cls += ' ' + this.weight;
554             }
555         }
556    
557         
558         if (this.inverse) {
559             this.cls += ' inverse';
560         }
561         
562         
563         if (this.active) {
564             cfg.cls += ' active';
565         }
566         
567         if (this.disabled) {
568             cfg.disabled = 'disabled';
569         }
570         
571         if (this.items) {
572             Roo.log('changing to ul' );
573             cfg.tag = 'ul';
574             this.glyphicon = 'caret';
575         }
576         
577         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
578          
579         //gsRoo.log(this.parentType);
580         if (this.parentType === 'Navbar' && !this.parent().bar) {
581             Roo.log('changing to li?');
582             
583             cfg.tag = 'li';
584             
585             cfg.cls = '';
586             cfg.cn =  [{
587                 tag : 'a',
588                 cls : 'roo-button',
589                 html : this.html,
590                 href : this.href || '#'
591             }];
592             if (this.menu) {
593                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
594                 cfg.cls += ' dropdown';
595             }   
596             
597             delete cfg.html;
598             
599         }
600         
601        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
602         
603         if (this.glyphicon) {
604             cfg.html = ' ' + cfg.html;
605             
606             cfg.cn = [
607                 {
608                     tag: 'span',
609                     cls: 'glyphicon glyphicon-' + this.glyphicon
610                 }
611             ];
612         }
613         
614         if (this.badge) {
615             cfg.html += ' ';
616             
617             cfg.tag = 'a';
618             
619 //            cfg.cls='btn roo-button';
620             
621             cfg.href=this.href;
622             
623             var value = cfg.html;
624             
625             if(this.glyphicon){
626                 value = {
627                             tag: 'span',
628                             cls: 'glyphicon glyphicon-' + this.glyphicon,
629                             html: this.html
630                         };
631                 
632             }
633             
634             cfg.cn = [
635                 value,
636                 {
637                     tag: 'span',
638                     cls: 'badge',
639                     html: this.badge
640                 }
641             ];
642             
643             cfg.html='';
644         }
645         
646         if (this.menu) {
647             cfg.cls += ' dropdown';
648             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
649         }
650         
651         if (cfg.tag !== 'a' && this.href !== '') {
652             throw "Tag must be a to set href.";
653         } else if (this.href.length > 0) {
654             cfg.href = this.href;
655         }
656         
657         if(this.removeClass){
658             cfg.cls = '';
659         }
660         
661         if(this.target){
662             cfg.target = this.target;
663         }
664         
665         return cfg;
666     },
667     initEvents: function() {
668        // Roo.log('init events?');
669 //        Roo.log(this.el.dom);
670         // add the menu...
671         
672         if (typeof (this.menu) != 'undefined') {
673             this.menu.parentType = this.xtype;
674             this.menu.triggerEl = this.el;
675             this.addxtype(Roo.apply({}, this.menu));
676         }
677
678
679        if (this.el.hasClass('roo-button')) {
680             this.el.on('click', this.onClick, this);
681        } else {
682             this.el.select('.roo-button').on('click', this.onClick, this);
683        }
684        
685        if(this.removeClass){
686            this.el.on('click', this.onClick, this);
687        }
688        
689        this.el.enableDisplayMode();
690         
691     },
692     onClick : function(e)
693     {
694         if (this.disabled) {
695             return;
696         }
697         
698         Roo.log('button on click ');
699         if(this.preventDefault){
700             e.preventDefault();
701         }
702         if (this.pressed === true || this.pressed === false) {
703             this.pressed = !this.pressed;
704             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
705             this.fireEvent('toggle', this, e, this.pressed);
706         }
707         
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     /**
713      * Enables this button
714      */
715     enable : function()
716     {
717         this.disabled = false;
718         this.el.removeClass('disabled');
719     },
720     
721     /**
722      * Disable this button
723      */
724     disable : function()
725     {
726         this.disabled = true;
727         this.el.addClass('disabled');
728     },
729      /**
730      * sets the active state on/off, 
731      * @param {Boolean} state (optional) Force a particular state
732      */
733     setActive : function(v) {
734         
735         this.el[v ? 'addClass' : 'removeClass']('active');
736     },
737      /**
738      * toggles the current active state 
739      */
740     toggleActive : function()
741     {
742        var active = this.el.hasClass('active');
743        this.setActive(!active);
744        
745         
746     },
747     setText : function(str)
748     {
749         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
750     },
751     getText : function()
752     {
753         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
754     },
755     hide: function() {
756        
757      
758         this.el.hide();   
759     },
760     show: function() {
761        
762         this.el.show();   
763     }
764     
765     
766 });
767
768  /*
769  * - LGPL
770  *
771  * column
772  * 
773  */
774
775 /**
776  * @class Roo.bootstrap.Column
777  * @extends Roo.bootstrap.Component
778  * Bootstrap Column class
779  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
780  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
781  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
782  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
783  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
784  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
785  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
786  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
787  *
788  * 
789  * @cfg {Boolean} hidden (true|false) hide the element
790  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
791  * @cfg {String} fa (ban|check|...) font awesome icon
792  * @cfg {Number} fasize (1|2|....) font awsome size
793
794  * @cfg {String} icon (info-sign|check|...) glyphicon name
795
796  * @cfg {String} html content of column.
797  * 
798  * @constructor
799  * Create a new Column
800  * @param {Object} config The config object
801  */
802
803 Roo.bootstrap.Column = function(config){
804     Roo.bootstrap.Column.superclass.constructor.call(this, config);
805 };
806
807 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
808     
809     xs: false,
810     sm: false,
811     md: false,
812     lg: false,
813     xsoff: false,
814     smoff: false,
815     mdoff: false,
816     lgoff: false,
817     html: '',
818     offset: 0,
819     alert: false,
820     fa: false,
821     icon : false,
822     hidden : false,
823     fasize : 1,
824     
825     getAutoCreate : function(){
826         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
827         
828         cfg = {
829             tag: 'div',
830             cls: 'column'
831         };
832         
833         var settings=this;
834         ['xs','sm','md','lg'].map(function(size){
835             //Roo.log( size + ':' + settings[size]);
836             
837             if (settings[size+'off'] !== false) {
838                 cfg.cls += ' col-' + settings[size+'off'] + '-offset';
839             }
840             
841             if (settings[size] === false) {
842                 return;
843             }
844             Roo.log(settings[size]);
845             if (!settings[size]) { // 0 = hidden
846                 cfg.cls += ' hidden-' + size;
847                 return;
848             }
849             cfg.cls += ' col-' + size + '-' + settings[size];
850             
851         });
852         
853         if (this.hidden) {
854             cfg.cls += ' hidden';
855         }
856         
857         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
858             cfg.cls +=' alert alert-' + this.alert;
859         }
860         
861         
862         if (this.html.length) {
863             cfg.html = this.html;
864         }
865         if (this.fa) {
866             var fasize = '';
867             if (this.fasize > 1) {
868                 fasize = ' fa-' + this.fasize + 'x';
869             }
870             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
871             
872             
873         }
874         if (this.icon) {
875             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
876         }
877         
878         return cfg;
879     }
880    
881 });
882
883  
884
885  /*
886  * - LGPL
887  *
888  * page container.
889  * 
890  */
891
892
893 /**
894  * @class Roo.bootstrap.Container
895  * @extends Roo.bootstrap.Component
896  * Bootstrap Container class
897  * @cfg {Boolean} jumbotron is it a jumbotron element
898  * @cfg {String} html content of element
899  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
900  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
901  * @cfg {String} header content of header (for panel)
902  * @cfg {String} footer content of footer (for panel)
903  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
904  * @cfg {String} tag (header|aside|section) type of HTML tag.
905  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
906  * @cfg {String} fa (ban|check|...) font awesome icon
907  * @cfg {String} icon (info-sign|check|...) glyphicon name
908  * @cfg {Boolean} hidden (true|false) hide the element
909
910  *     
911  * @constructor
912  * Create a new Container
913  * @param {Object} config The config object
914  */
915
916 Roo.bootstrap.Container = function(config){
917     Roo.bootstrap.Container.superclass.constructor.call(this, config);
918 };
919
920 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
921     
922     jumbotron : false,
923     well: '',
924     panel : '',
925     header: '',
926     footer : '',
927     sticky: '',
928     tag : false,
929     alert : false,
930     fa: false,
931     icon : false,
932   
933      
934     getChildContainer : function() {
935         
936         if(!this.el){
937             return false;
938         }
939         
940         if (this.panel.length) {
941             return this.el.select('.panel-body',true).first();
942         }
943         
944         return this.el;
945     },
946     
947     
948     getAutoCreate : function(){
949         
950         var cfg = {
951             tag : this.tag || 'div',
952             html : '',
953             cls : ''
954         };
955         if (this.jumbotron) {
956             cfg.cls = 'jumbotron';
957         }
958         
959         
960         
961         // - this is applied by the parent..
962         //if (this.cls) {
963         //    cfg.cls = this.cls + '';
964         //}
965         
966         if (this.sticky.length) {
967             
968             var bd = Roo.get(document.body);
969             if (!bd.hasClass('bootstrap-sticky')) {
970                 bd.addClass('bootstrap-sticky');
971                 Roo.select('html',true).setStyle('height', '100%');
972             }
973              
974             cfg.cls += 'bootstrap-sticky-' + this.sticky;
975         }
976         
977         
978         if (this.well.length) {
979             switch (this.well) {
980                 case 'lg':
981                 case 'sm':
982                     cfg.cls +=' well well-' +this.well;
983                     break;
984                 default:
985                     cfg.cls +=' well';
986                     break;
987             }
988         }
989         
990         if (this.hidden) {
991             cfg.cls += ' hidden';
992         }
993         
994         
995         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
996             cfg.cls +=' alert alert-' + this.alert;
997         }
998         
999         var body = cfg;
1000         
1001         if (this.panel.length) {
1002             cfg.cls += ' panel panel-' + this.panel;
1003             cfg.cn = [];
1004             if (this.header.length) {
1005                 cfg.cn.push({
1006                     
1007                     cls : 'panel-heading',
1008                     cn : [{
1009                         tag: 'h3',
1010                         cls : 'panel-title',
1011                         html : this.header
1012                     }]
1013                     
1014                 });
1015             }
1016             body = false;
1017             cfg.cn.push({
1018                 cls : 'panel-body',
1019                 html : this.html
1020             });
1021             
1022             
1023             if (this.footer.length) {
1024                 cfg.cn.push({
1025                     cls : 'panel-footer',
1026                     html : this.footer
1027                     
1028                 });
1029             }
1030             
1031         }
1032         
1033         if (body) {
1034             body.html = this.html || cfg.html;
1035             // prefix with the icons..
1036             if (this.fa) {
1037                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1038             }
1039             if (this.icon) {
1040                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1041             }
1042             
1043             
1044         }
1045         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1046             cfg.cls =  'container';
1047         }
1048         
1049         return cfg;
1050     },
1051     
1052     titleEl : function()
1053     {
1054         if(!this.el || !this.panel.length || !this.header.length){
1055             return;
1056         }
1057         
1058         return this.el.select('.panel-title',true).first();
1059     },
1060     
1061     setTitle : function(v)
1062     {
1063         var titleEl = this.titleEl();
1064         
1065         if(!titleEl){
1066             return;
1067         }
1068         
1069         titleEl.dom.innerHTML = v;
1070     },
1071     
1072     getTitle : function()
1073     {
1074         
1075         var titleEl = this.titleEl();
1076         
1077         if(!titleEl){
1078             return '';
1079         }
1080         
1081         return titleEl.dom.innerHTML;
1082     }
1083    
1084 });
1085
1086  /*
1087  * - LGPL
1088  *
1089  * image
1090  * 
1091  */
1092
1093
1094 /**
1095  * @class Roo.bootstrap.Img
1096  * @extends Roo.bootstrap.Component
1097  * Bootstrap Img class
1098  * @cfg {Boolean} imgResponsive false | true
1099  * @cfg {String} border rounded | circle | thumbnail
1100  * @cfg {String} src image source
1101  * @cfg {String} alt image alternative text
1102  * @cfg {String} href a tag href
1103  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1104  * 
1105  * @constructor
1106  * Create a new Input
1107  * @param {Object} config The config object
1108  */
1109
1110 Roo.bootstrap.Img = function(config){
1111     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1112     
1113     this.addEvents({
1114         // img events
1115         /**
1116          * @event click
1117          * The img click event for the img.
1118          * @param {Roo.EventObject} e
1119          */
1120         "click" : true
1121     });
1122 };
1123
1124 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1125     
1126     imgResponsive: true,
1127     border: '',
1128     src: '',
1129     href: false,
1130     target: false,
1131
1132     getAutoCreate : function(){
1133         
1134         var cfg = {
1135             tag: 'img',
1136             cls: (this.imgResponsive) ? 'img-responsive' : '',
1137             html : null
1138         }
1139         
1140         cfg.html = this.html || cfg.html;
1141         
1142         cfg.src = this.src || cfg.src;
1143         
1144         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1145             cfg.cls += ' img-' + this.border;
1146         }
1147         
1148         if(this.alt){
1149             cfg.alt = this.alt;
1150         }
1151         
1152         if(this.href){
1153             var a = {
1154                 tag: 'a',
1155                 href: this.href,
1156                 cn: [
1157                     cfg
1158                 ]
1159             }
1160             
1161             if(this.target){
1162                 a.target = this.target;
1163             }
1164             
1165         }
1166         
1167         
1168         return (this.href) ? a : cfg;
1169     },
1170     
1171     initEvents: function() {
1172         
1173         if(!this.href){
1174             this.el.on('click', this.onClick, this);
1175         }
1176     },
1177     
1178     onClick : function(e)
1179     {
1180         Roo.log('img onclick');
1181         this.fireEvent('click', this, e);
1182     }
1183    
1184 });
1185
1186  /*
1187  * - LGPL
1188  *
1189  * image
1190  * 
1191  */
1192
1193
1194 /**
1195  * @class Roo.bootstrap.Link
1196  * @extends Roo.bootstrap.Component
1197  * Bootstrap Link Class
1198  * @cfg {String} alt image alternative text
1199  * @cfg {String} href a tag href
1200  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1201  * @cfg {String} html the content of the link.
1202  * @cfg {String} anchor name for the anchor link
1203
1204  * @cfg {Boolean} preventDefault (true | false) default false
1205
1206  * 
1207  * @constructor
1208  * Create a new Input
1209  * @param {Object} config The config object
1210  */
1211
1212 Roo.bootstrap.Link = function(config){
1213     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1214     
1215     this.addEvents({
1216         // img events
1217         /**
1218          * @event click
1219          * The img click event for the img.
1220          * @param {Roo.EventObject} e
1221          */
1222         "click" : true
1223     });
1224 };
1225
1226 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1227     
1228     href: false,
1229     target: false,
1230     preventDefault: false,
1231     anchor : false,
1232     alt : false,
1233
1234     getAutoCreate : function()
1235     {
1236         
1237         var cfg = {
1238             tag: 'a'
1239         };
1240         // anchor's do not require html/href...
1241         if (this.anchor === false) {
1242             cfg.html = this.html || 'html-missing';
1243             cfg.href = this.href || '#';
1244         } else {
1245             cfg.name = this.anchor;
1246             if (this.html !== false) {
1247                 cfg.html = this.html;
1248             }
1249             if (this.href !== false) {
1250                 cfg.href = this.href;
1251             }
1252         }
1253         
1254         if(this.alt !== false){
1255             cfg.alt = this.alt;
1256         }
1257         
1258         
1259         if(this.target !== false) {
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     
1266     initEvents: function() {
1267         
1268         if(!this.href || this.preventDefault){
1269             this.el.on('click', this.onClick, this);
1270         }
1271     },
1272     
1273     onClick : function(e)
1274     {
1275         if(this.preventDefault){
1276             e.preventDefault();
1277         }
1278         //Roo.log('img onclick');
1279         this.fireEvent('click', this, e);
1280     }
1281    
1282 });
1283
1284  /*
1285  * - LGPL
1286  *
1287  * header
1288  * 
1289  */
1290
1291 /**
1292  * @class Roo.bootstrap.Header
1293  * @extends Roo.bootstrap.Component
1294  * Bootstrap Header class
1295  * @cfg {String} html content of header
1296  * @cfg {Number} level (1|2|3|4|5|6) default 1
1297  * 
1298  * @constructor
1299  * Create a new Header
1300  * @param {Object} config The config object
1301  */
1302
1303
1304 Roo.bootstrap.Header  = function(config){
1305     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1306 };
1307
1308 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1309     
1310     //href : false,
1311     html : false,
1312     level : 1,
1313     
1314     
1315     
1316     getAutoCreate : function(){
1317         
1318         var cfg = {
1319             tag: 'h' + (1 *this.level),
1320             html: this.html || 'fill in html'
1321         } ;
1322         
1323         return cfg;
1324     }
1325    
1326 });
1327
1328  
1329
1330  /*
1331  * Based on:
1332  * Ext JS Library 1.1.1
1333  * Copyright(c) 2006-2007, Ext JS, LLC.
1334  *
1335  * Originally Released Under LGPL - original licence link has changed is not relivant.
1336  *
1337  * Fork - LGPL
1338  * <script type="text/javascript">
1339  */
1340  
1341 /**
1342  * @class Roo.bootstrap.MenuMgr
1343  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1344  * @singleton
1345  */
1346 Roo.bootstrap.MenuMgr = function(){
1347    var menus, active, groups = {}, attached = false, lastShow = new Date();
1348
1349    // private - called when first menu is created
1350    function init(){
1351        menus = {};
1352        active = new Roo.util.MixedCollection();
1353        Roo.get(document).addKeyListener(27, function(){
1354            if(active.length > 0){
1355                hideAll();
1356            }
1357        });
1358    }
1359
1360    // private
1361    function hideAll(){
1362        if(active && active.length > 0){
1363            var c = active.clone();
1364            c.each(function(m){
1365                m.hide();
1366            });
1367        }
1368    }
1369
1370    // private
1371    function onHide(m){
1372        active.remove(m);
1373        if(active.length < 1){
1374            Roo.get(document).un("mouseup", onMouseDown);
1375             
1376            attached = false;
1377        }
1378    }
1379
1380    // private
1381    function onShow(m){
1382        var last = active.last();
1383        lastShow = new Date();
1384        active.add(m);
1385        if(!attached){
1386           Roo.get(document).on("mouseup", onMouseDown);
1387            
1388            attached = true;
1389        }
1390        if(m.parentMenu){
1391           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1392           m.parentMenu.activeChild = m;
1393        }else if(last && last.isVisible()){
1394           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1395        }
1396    }
1397
1398    // private
1399    function onBeforeHide(m){
1400        if(m.activeChild){
1401            m.activeChild.hide();
1402        }
1403        if(m.autoHideTimer){
1404            clearTimeout(m.autoHideTimer);
1405            delete m.autoHideTimer;
1406        }
1407    }
1408
1409    // private
1410    function onBeforeShow(m){
1411        var pm = m.parentMenu;
1412        if(!pm && !m.allowOtherMenus){
1413            hideAll();
1414        }else if(pm && pm.activeChild && active != m){
1415            pm.activeChild.hide();
1416        }
1417    }
1418
1419    // private
1420    function onMouseDown(e){
1421         Roo.log("on MouseDown");
1422         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1423            hideAll();
1424         }
1425         
1426         
1427    }
1428
1429    // private
1430    function onBeforeCheck(mi, state){
1431        if(state){
1432            var g = groups[mi.group];
1433            for(var i = 0, l = g.length; i < l; i++){
1434                if(g[i] != mi){
1435                    g[i].setChecked(false);
1436                }
1437            }
1438        }
1439    }
1440
1441    return {
1442
1443        /**
1444         * Hides all menus that are currently visible
1445         */
1446        hideAll : function(){
1447             hideAll();  
1448        },
1449
1450        // private
1451        register : function(menu){
1452            if(!menus){
1453                init();
1454            }
1455            menus[menu.id] = menu;
1456            menu.on("beforehide", onBeforeHide);
1457            menu.on("hide", onHide);
1458            menu.on("beforeshow", onBeforeShow);
1459            menu.on("show", onShow);
1460            var g = menu.group;
1461            if(g && menu.events["checkchange"]){
1462                if(!groups[g]){
1463                    groups[g] = [];
1464                }
1465                groups[g].push(menu);
1466                menu.on("checkchange", onCheck);
1467            }
1468        },
1469
1470         /**
1471          * Returns a {@link Roo.menu.Menu} object
1472          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1473          * be used to generate and return a new Menu instance.
1474          */
1475        get : function(menu){
1476            if(typeof menu == "string"){ // menu id
1477                return menus[menu];
1478            }else if(menu.events){  // menu instance
1479                return menu;
1480            }
1481            /*else if(typeof menu.length == 'number'){ // array of menu items?
1482                return new Roo.bootstrap.Menu({items:menu});
1483            }else{ // otherwise, must be a config
1484                return new Roo.bootstrap.Menu(menu);
1485            }
1486            */
1487            return false;
1488        },
1489
1490        // private
1491        unregister : function(menu){
1492            delete menus[menu.id];
1493            menu.un("beforehide", onBeforeHide);
1494            menu.un("hide", onHide);
1495            menu.un("beforeshow", onBeforeShow);
1496            menu.un("show", onShow);
1497            var g = menu.group;
1498            if(g && menu.events["checkchange"]){
1499                groups[g].remove(menu);
1500                menu.un("checkchange", onCheck);
1501            }
1502        },
1503
1504        // private
1505        registerCheckable : function(menuItem){
1506            var g = menuItem.group;
1507            if(g){
1508                if(!groups[g]){
1509                    groups[g] = [];
1510                }
1511                groups[g].push(menuItem);
1512                menuItem.on("beforecheckchange", onBeforeCheck);
1513            }
1514        },
1515
1516        // private
1517        unregisterCheckable : function(menuItem){
1518            var g = menuItem.group;
1519            if(g){
1520                groups[g].remove(menuItem);
1521                menuItem.un("beforecheckchange", onBeforeCheck);
1522            }
1523        }
1524    };
1525 }();/*
1526  * - LGPL
1527  *
1528  * menu
1529  * 
1530  */
1531
1532 /**
1533  * @class Roo.bootstrap.Menu
1534  * @extends Roo.bootstrap.Component
1535  * Bootstrap Menu class - container for MenuItems
1536  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1537  * 
1538  * @constructor
1539  * Create a new Menu
1540  * @param {Object} config The config object
1541  */
1542
1543
1544 Roo.bootstrap.Menu = function(config){
1545     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1546     if (this.registerMenu) {
1547         Roo.bootstrap.MenuMgr.register(this);
1548     }
1549     this.addEvents({
1550         /**
1551          * @event beforeshow
1552          * Fires before this menu is displayed
1553          * @param {Roo.menu.Menu} this
1554          */
1555         beforeshow : true,
1556         /**
1557          * @event beforehide
1558          * Fires before this menu is hidden
1559          * @param {Roo.menu.Menu} this
1560          */
1561         beforehide : true,
1562         /**
1563          * @event show
1564          * Fires after this menu is displayed
1565          * @param {Roo.menu.Menu} this
1566          */
1567         show : true,
1568         /**
1569          * @event hide
1570          * Fires after this menu is hidden
1571          * @param {Roo.menu.Menu} this
1572          */
1573         hide : true,
1574         /**
1575          * @event click
1576          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1577          * @param {Roo.menu.Menu} this
1578          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1579          * @param {Roo.EventObject} e
1580          */
1581         click : true,
1582         /**
1583          * @event mouseover
1584          * Fires when the mouse is hovering over this menu
1585          * @param {Roo.menu.Menu} this
1586          * @param {Roo.EventObject} e
1587          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1588          */
1589         mouseover : true,
1590         /**
1591          * @event mouseout
1592          * Fires when the mouse exits this menu
1593          * @param {Roo.menu.Menu} this
1594          * @param {Roo.EventObject} e
1595          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1596          */
1597         mouseout : true,
1598         /**
1599          * @event itemclick
1600          * Fires when a menu item contained in this menu is clicked
1601          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1602          * @param {Roo.EventObject} e
1603          */
1604         itemclick: true
1605     });
1606     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1607 };
1608
1609 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1610     
1611    /// html : false,
1612     //align : '',
1613     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1614     type: false,
1615     /**
1616      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1617      */
1618     registerMenu : true,
1619     
1620     menuItems :false, // stores the menu items..
1621     
1622     hidden:true,
1623     
1624     parentMenu : false,
1625     
1626     getChildContainer : function() {
1627         return this.el;  
1628     },
1629     
1630     getAutoCreate : function(){
1631          
1632         //if (['right'].indexOf(this.align)!==-1) {
1633         //    cfg.cn[1].cls += ' pull-right'
1634         //}
1635         
1636         
1637         var cfg = {
1638             tag : 'ul',
1639             cls : 'dropdown-menu' ,
1640             style : 'z-index:1000'
1641             
1642         }
1643         
1644         if (this.type === 'submenu') {
1645             cfg.cls = 'submenu active';
1646         }
1647         if (this.type === 'treeview') {
1648             cfg.cls = 'treeview-menu';
1649         }
1650         
1651         return cfg;
1652     },
1653     initEvents : function() {
1654         
1655        // Roo.log("ADD event");
1656        // Roo.log(this.triggerEl.dom);
1657         this.triggerEl.on('click', this.onTriggerPress, this);
1658         this.triggerEl.addClass('dropdown-toggle');
1659         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1660
1661         this.el.on("mouseover", this.onMouseOver, this);
1662         this.el.on("mouseout", this.onMouseOut, this);
1663         
1664         
1665     },
1666     findTargetItem : function(e){
1667         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1668         if(!t){
1669             return false;
1670         }
1671         //Roo.log(t);         Roo.log(t.id);
1672         if(t && t.id){
1673             //Roo.log(this.menuitems);
1674             return this.menuitems.get(t.id);
1675             
1676             //return this.items.get(t.menuItemId);
1677         }
1678         
1679         return false;
1680     },
1681     onClick : function(e){
1682         Roo.log("menu.onClick");
1683         var t = this.findTargetItem(e);
1684         if(!t){
1685             return;
1686         }
1687         Roo.log(e);
1688         /*
1689         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1690             if(t == this.activeItem && t.shouldDeactivate(e)){
1691                 this.activeItem.deactivate();
1692                 delete this.activeItem;
1693                 return;
1694             }
1695             if(t.canActivate){
1696                 this.setActiveItem(t, true);
1697             }
1698             return;
1699             
1700             
1701         }
1702         */
1703         Roo.log('pass click event');
1704         
1705         t.onClick(e);
1706         
1707         this.fireEvent("click", this, t, e);
1708         
1709         this.hide();
1710     },
1711      onMouseOver : function(e){
1712         var t  = this.findTargetItem(e);
1713         //Roo.log(t);
1714         //if(t){
1715         //    if(t.canActivate && !t.disabled){
1716         //        this.setActiveItem(t, true);
1717         //    }
1718         //}
1719         
1720         this.fireEvent("mouseover", this, e, t);
1721     },
1722     isVisible : function(){
1723         return !this.hidden;
1724     },
1725      onMouseOut : function(e){
1726         var t  = this.findTargetItem(e);
1727         
1728         //if(t ){
1729         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1730         //        this.activeItem.deactivate();
1731         //        delete this.activeItem;
1732         //    }
1733         //}
1734         this.fireEvent("mouseout", this, e, t);
1735     },
1736     
1737     
1738     /**
1739      * Displays this menu relative to another element
1740      * @param {String/HTMLElement/Roo.Element} element The element to align to
1741      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1742      * the element (defaults to this.defaultAlign)
1743      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1744      */
1745     show : function(el, pos, parentMenu){
1746         this.parentMenu = parentMenu;
1747         if(!this.el){
1748             this.render();
1749         }
1750         this.fireEvent("beforeshow", this);
1751         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1752     },
1753      /**
1754      * Displays this menu at a specific xy position
1755      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1756      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1757      */
1758     showAt : function(xy, parentMenu, /* private: */_e){
1759         this.parentMenu = parentMenu;
1760         if(!this.el){
1761             this.render();
1762         }
1763         if(_e !== false){
1764             this.fireEvent("beforeshow", this);
1765             
1766             //xy = this.el.adjustForConstraints(xy);
1767         }
1768         //this.el.setXY(xy);
1769         //this.el.show();
1770         this.hideMenuItems();
1771         this.hidden = false;
1772         this.triggerEl.addClass('open');
1773         this.focus();
1774         this.fireEvent("show", this);
1775     },
1776     
1777     focus : function(){
1778         return;
1779         if(!this.hidden){
1780             this.doFocus.defer(50, this);
1781         }
1782     },
1783
1784     doFocus : function(){
1785         if(!this.hidden){
1786             this.focusEl.focus();
1787         }
1788     },
1789
1790     /**
1791      * Hides this menu and optionally all parent menus
1792      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1793      */
1794     hide : function(deep){
1795         
1796         this.hideMenuItems();
1797         if(this.el && this.isVisible()){
1798             this.fireEvent("beforehide", this);
1799             if(this.activeItem){
1800                 this.activeItem.deactivate();
1801                 this.activeItem = null;
1802             }
1803             this.triggerEl.removeClass('open');;
1804             this.hidden = true;
1805             this.fireEvent("hide", this);
1806         }
1807         if(deep === true && this.parentMenu){
1808             this.parentMenu.hide(true);
1809         }
1810     },
1811     
1812     onTriggerPress  : function(e)
1813     {
1814         
1815         Roo.log('trigger press');
1816         //Roo.log(e.getTarget());
1817        // Roo.log(this.triggerEl.dom);
1818         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1819             return;
1820         }
1821         if (this.isVisible()) {
1822             Roo.log('hide');
1823             this.hide();
1824         } else {
1825             this.show(this.triggerEl, false, false);
1826         }
1827         
1828         
1829     },
1830     
1831          
1832        
1833     
1834     hideMenuItems : function()
1835     {
1836         //$(backdrop).remove()
1837         Roo.select('.open',true).each(function(aa) {
1838             
1839             aa.removeClass('open');
1840           //var parent = getParent($(this))
1841           //var relatedTarget = { relatedTarget: this }
1842           
1843            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1844           //if (e.isDefaultPrevented()) return
1845            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1846         })
1847     },
1848     addxtypeChild : function (tree, cntr) {
1849         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1850           
1851         this.menuitems.add(comp);
1852         return comp;
1853
1854     },
1855     getEl : function()
1856     {
1857         Roo.log(this.el);
1858         return this.el;
1859     }
1860 });
1861
1862  
1863  /*
1864  * - LGPL
1865  *
1866  * menu item
1867  * 
1868  */
1869
1870
1871 /**
1872  * @class Roo.bootstrap.MenuItem
1873  * @extends Roo.bootstrap.Component
1874  * Bootstrap MenuItem class
1875  * @cfg {String} html the menu label
1876  * @cfg {String} href the link
1877  * @cfg {Boolean} preventDefault (true | false) default true
1878  * 
1879  * 
1880  * @constructor
1881  * Create a new MenuItem
1882  * @param {Object} config The config object
1883  */
1884
1885
1886 Roo.bootstrap.MenuItem = function(config){
1887     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1888     this.addEvents({
1889         // raw events
1890         /**
1891          * @event click
1892          * The raw click event for the entire grid.
1893          * @param {Roo.EventObject} e
1894          */
1895         "click" : true
1896     });
1897 };
1898
1899 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1900     
1901     href : false,
1902     html : false,
1903     preventDefault: true,
1904     
1905     getAutoCreate : function(){
1906         var cfg= {
1907             tag: 'li',
1908             cls: 'dropdown-menu-item',
1909             cn: [
1910                     {
1911                         tag : 'a',
1912                         href : '#',
1913                         html : 'Link'
1914                     }
1915                 ]
1916         };
1917         if (this.parent().type == 'treeview') {
1918             cfg.cls = 'treeview-menu';
1919         }
1920         
1921         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1922         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1923         return cfg;
1924     },
1925     
1926     initEvents: function() {
1927         
1928         //this.el.select('a').on('click', this.onClick, this);
1929         
1930     },
1931     onClick : function(e)
1932     {
1933         Roo.log('item on click ');
1934         //if(this.preventDefault){
1935         //    e.preventDefault();
1936         //}
1937         //this.parent().hideMenuItems();
1938         
1939         this.fireEvent('click', this, e);
1940     },
1941     getEl : function()
1942     {
1943         return this.el;
1944     }
1945 });
1946
1947  
1948
1949  /*
1950  * - LGPL
1951  *
1952  * menu separator
1953  * 
1954  */
1955
1956
1957 /**
1958  * @class Roo.bootstrap.MenuSeparator
1959  * @extends Roo.bootstrap.Component
1960  * Bootstrap MenuSeparator class
1961  * 
1962  * @constructor
1963  * Create a new MenuItem
1964  * @param {Object} config The config object
1965  */
1966
1967
1968 Roo.bootstrap.MenuSeparator = function(config){
1969     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1970 };
1971
1972 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1973     
1974     getAutoCreate : function(){
1975         var cfg = {
1976             cls: 'divider',
1977             tag : 'li'
1978         };
1979         
1980         return cfg;
1981     }
1982    
1983 });
1984
1985  
1986
1987  
1988 /*
1989 <div class="modal fade">
1990   <div class="modal-dialog">
1991     <div class="modal-content">
1992       <div class="modal-header">
1993         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1994         <h4 class="modal-title">Modal title</h4>
1995       </div>
1996       <div class="modal-body">
1997         <p>One fine body&hellip;</p>
1998       </div>
1999       <div class="modal-footer">
2000         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2001         <button type="button" class="btn btn-primary">Save changes</button>
2002       </div>
2003     </div><!-- /.modal-content -->
2004   </div><!-- /.modal-dialog -->
2005 </div><!-- /.modal -->
2006 */
2007 /*
2008  * - LGPL
2009  *
2010  * page contgainer.
2011  * 
2012  */
2013
2014 /**
2015  * @class Roo.bootstrap.Modal
2016  * @extends Roo.bootstrap.Component
2017  * Bootstrap Modal class
2018  * @cfg {String} title Title of dialog
2019  * @cfg {Boolean} specificTitle (true|false) default false
2020  * @cfg {Array} buttons Array of buttons or standard button set..
2021  * @cfg {String} buttonPosition (left|right|center) default right
2022  * 
2023  * @constructor
2024  * Create a new Modal Dialog
2025  * @param {Object} config The config object
2026  */
2027
2028 Roo.bootstrap.Modal = function(config){
2029     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2030     this.addEvents({
2031         // raw events
2032         /**
2033          * @event btnclick
2034          * The raw btnclick event for the button
2035          * @param {Roo.EventObject} e
2036          */
2037         "btnclick" : true
2038     });
2039     this.buttons = this.buttons || [];
2040 };
2041
2042 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2043     
2044     title : 'test dialog',
2045    
2046     buttons : false,
2047     
2048     // set on load...
2049     body:  false,
2050     
2051     specificTitle: false,
2052     
2053     buttonPosition: 'right',
2054     
2055     onRender : function(ct, position)
2056     {
2057         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2058      
2059         if(!this.el){
2060             var cfg = Roo.apply({},  this.getAutoCreate());
2061             cfg.id = Roo.id();
2062             //if(!cfg.name){
2063             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2064             //}
2065             //if (!cfg.name.length) {
2066             //    delete cfg.name;
2067            // }
2068             if (this.cls) {
2069                 cfg.cls += ' ' + this.cls;
2070             }
2071             if (this.style) {
2072                 cfg.style = this.style;
2073             }
2074             this.el = Roo.get(document.body).createChild(cfg, position);
2075         }
2076         //var type = this.el.dom.type;
2077         
2078         if(this.tabIndex !== undefined){
2079             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2080         }
2081         
2082         
2083         
2084         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2085         this.maskEl.enableDisplayMode("block");
2086         this.maskEl.hide();
2087         //this.el.addClass("x-dlg-modal");
2088     
2089         if (this.buttons.length) {
2090             Roo.each(this.buttons, function(bb) {
2091                 b = Roo.apply({}, bb);
2092                 b.xns = b.xns || Roo.bootstrap;
2093                 b.xtype = b.xtype || 'Button';
2094                 if (typeof(b.listeners) == 'undefined') {
2095                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2096                 }
2097                 
2098                 var btn = Roo.factory(b);
2099                 
2100                 btn.onRender(this.el.select('.modal-footer div').first());
2101                 
2102             },this);
2103         }
2104         // render the children.
2105         var nitems = [];
2106         
2107         if(typeof(this.items) != 'undefined'){
2108             var items = this.items;
2109             delete this.items;
2110
2111             for(var i =0;i < items.length;i++) {
2112                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2113             }
2114         }
2115         
2116         this.items = nitems;
2117         
2118         this.body = this.el.select('.modal-body',true).first();
2119         this.close = this.el.select('.modal-header .close', true).first();
2120         this.footer = this.el.select('.modal-footer',true).first();
2121         this.initEvents();
2122         //this.el.addClass([this.fieldClass, this.cls]);
2123         
2124     },
2125     getAutoCreate : function(){
2126         
2127         
2128         var bdy = {
2129                 cls : 'modal-body',
2130                 html : this.html || ''
2131         };
2132         
2133         var title = {
2134             tag: 'h4',
2135             cls : 'modal-title',
2136             html : this.title
2137         };
2138         
2139         if(this.specificTitle){
2140             title = this.title;
2141         };
2142         
2143         return modal = {
2144             cls: "modal fade",
2145             style : 'display: none',
2146             cn : [
2147                 {
2148                     cls: "modal-dialog",
2149                     cn : [
2150                         {
2151                             cls : "modal-content",
2152                             cn : [
2153                                 {
2154                                     cls : 'modal-header',
2155                                     cn : [
2156                                         {
2157                                             tag: 'button',
2158                                             cls : 'close',
2159                                             html : '&times'
2160                                         },
2161                                         title
2162                                     ]
2163                                 },
2164                                 bdy,
2165                                 {
2166                                     cls : 'modal-footer',
2167                                     cn : [
2168                                         {
2169                                             tag: 'div',
2170                                             cls: 'btn-' + this.buttonPosition
2171                                         }
2172                                     ]
2173                                     
2174                                 }
2175                                 
2176                                 
2177                             ]
2178                             
2179                         }
2180                     ]
2181                         
2182                 }
2183             ]
2184             
2185             
2186         };
2187           
2188     },
2189     getChildContainer : function() {
2190          
2191          return this.el.select('.modal-body',true).first();
2192         
2193     },
2194     getButtonContainer : function() {
2195          return this.el.select('.modal-footer div',true).first();
2196         
2197     },
2198     initEvents : function()
2199     {
2200         this.el.select('.modal-header .close').on('click', this.hide, this);
2201 //        
2202 //        this.addxtype(this);
2203     },
2204     show : function() {
2205         
2206         if (!this.rendered) {
2207             this.render();
2208         }
2209        
2210         this.el.addClass('on');
2211         this.el.removeClass('fade');
2212         this.el.setStyle('display', 'block');
2213         Roo.get(document.body).addClass("x-body-masked");
2214         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2215         this.maskEl.show();
2216         this.el.setStyle('zIndex', '10001');
2217         this.fireEvent('show', this);
2218         
2219         
2220     },
2221     hide : function()
2222     {
2223         Roo.log('Modal hide?!');
2224         this.maskEl.hide();
2225         Roo.get(document.body).removeClass("x-body-masked");
2226         this.el.removeClass('on');
2227         this.el.addClass('fade');
2228         this.el.setStyle('display', 'none');
2229         this.fireEvent('hide', this);
2230     },
2231     
2232     addButton : function(str, cb)
2233     {
2234          
2235         
2236         var b = Roo.apply({}, { html : str } );
2237         b.xns = b.xns || Roo.bootstrap;
2238         b.xtype = b.xtype || 'Button';
2239         if (typeof(b.listeners) == 'undefined') {
2240             b.listeners = { click : cb.createDelegate(this)  };
2241         }
2242         
2243         var btn = Roo.factory(b);
2244            
2245         btn.onRender(this.el.select('.modal-footer div').first());
2246         
2247         return btn;   
2248        
2249     },
2250     
2251     setDefaultButton : function(btn)
2252     {
2253         //this.el.select('.modal-footer').()
2254     },
2255     resizeTo: function(w,h)
2256     {
2257         // skip..
2258     },
2259     setContentSize  : function(w, h)
2260     {
2261         
2262     },
2263     onButtonClick: function(btn,e)
2264     {
2265         //Roo.log([a,b,c]);
2266         this.fireEvent('btnclick', btn.name, e);
2267     },
2268     setTitle: function(str) {
2269         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2270         
2271     }
2272 });
2273
2274
2275 Roo.apply(Roo.bootstrap.Modal,  {
2276     /**
2277          * Button config that displays a single OK button
2278          * @type Object
2279          */
2280         OK :  [{
2281             name : 'ok',
2282             weight : 'primary',
2283             html : 'OK'
2284         }], 
2285         /**
2286          * Button config that displays Yes and No buttons
2287          * @type Object
2288          */
2289         YESNO : [
2290             {
2291                 name  : 'no',
2292                 html : 'No'
2293             },
2294             {
2295                 name  :'yes',
2296                 weight : 'primary',
2297                 html : 'Yes'
2298             }
2299         ],
2300         
2301         /**
2302          * Button config that displays OK and Cancel buttons
2303          * @type Object
2304          */
2305         OKCANCEL : [
2306             {
2307                name : 'cancel',
2308                 html : 'Cancel'
2309             },
2310             {
2311                 name : 'ok',
2312                 weight : 'primary',
2313                 html : 'OK'
2314             }
2315         ],
2316         /**
2317          * Button config that displays Yes, No and Cancel buttons
2318          * @type Object
2319          */
2320         YESNOCANCEL : [
2321             {
2322                 name : 'yes',
2323                 weight : 'primary',
2324                 html : 'Yes'
2325             },
2326             {
2327                 name : 'no',
2328                 html : 'No'
2329             },
2330             {
2331                 name : 'cancel',
2332                 html : 'Cancel'
2333             }
2334         ]
2335 });
2336  /*
2337  * - LGPL
2338  *
2339  * messagebox - can be used as a replace
2340  * 
2341  */
2342 /**
2343  * @class Roo.MessageBox
2344  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2345  * Example usage:
2346  *<pre><code>
2347 // Basic alert:
2348 Roo.Msg.alert('Status', 'Changes saved successfully.');
2349
2350 // Prompt for user data:
2351 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2352     if (btn == 'ok'){
2353         // process text value...
2354     }
2355 });
2356
2357 // Show a dialog using config options:
2358 Roo.Msg.show({
2359    title:'Save Changes?',
2360    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2361    buttons: Roo.Msg.YESNOCANCEL,
2362    fn: processResult,
2363    animEl: 'elId'
2364 });
2365 </code></pre>
2366  * @singleton
2367  */
2368 Roo.bootstrap.MessageBox = function(){
2369     var dlg, opt, mask, waitTimer;
2370     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2371     var buttons, activeTextEl, bwidth;
2372
2373     
2374     // private
2375     var handleButton = function(button){
2376         dlg.hide();
2377         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2378     };
2379
2380     // private
2381     var handleHide = function(){
2382         if(opt && opt.cls){
2383             dlg.el.removeClass(opt.cls);
2384         }
2385         //if(waitTimer){
2386         //    Roo.TaskMgr.stop(waitTimer);
2387         //    waitTimer = null;
2388         //}
2389     };
2390
2391     // private
2392     var updateButtons = function(b){
2393         var width = 0;
2394         if(!b){
2395             buttons["ok"].hide();
2396             buttons["cancel"].hide();
2397             buttons["yes"].hide();
2398             buttons["no"].hide();
2399             //dlg.footer.dom.style.display = 'none';
2400             return width;
2401         }
2402         dlg.footer.dom.style.display = '';
2403         for(var k in buttons){
2404             if(typeof buttons[k] != "function"){
2405                 if(b[k]){
2406                     buttons[k].show();
2407                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2408                     width += buttons[k].el.getWidth()+15;
2409                 }else{
2410                     buttons[k].hide();
2411                 }
2412             }
2413         }
2414         return width;
2415     };
2416
2417     // private
2418     var handleEsc = function(d, k, e){
2419         if(opt && opt.closable !== false){
2420             dlg.hide();
2421         }
2422         if(e){
2423             e.stopEvent();
2424         }
2425     };
2426
2427     return {
2428         /**
2429          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2430          * @return {Roo.BasicDialog} The BasicDialog element
2431          */
2432         getDialog : function(){
2433            if(!dlg){
2434                 dlg = new Roo.bootstrap.Modal( {
2435                     //draggable: true,
2436                     //resizable:false,
2437                     //constraintoviewport:false,
2438                     //fixedcenter:true,
2439                     //collapsible : false,
2440                     //shim:true,
2441                     //modal: true,
2442                   //  width:400,
2443                   //  height:100,
2444                     //buttonAlign:"center",
2445                     closeClick : function(){
2446                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2447                             handleButton("no");
2448                         }else{
2449                             handleButton("cancel");
2450                         }
2451                     }
2452                 });
2453                 dlg.render();
2454                 dlg.on("hide", handleHide);
2455                 mask = dlg.mask;
2456                 //dlg.addKeyListener(27, handleEsc);
2457                 buttons = {};
2458                 this.buttons = buttons;
2459                 var bt = this.buttonText;
2460                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2461                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2462                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2463                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2464                 Roo.log(buttons)
2465                 bodyEl = dlg.body.createChild({
2466
2467                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2468                         '<textarea class="roo-mb-textarea"></textarea>' +
2469                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2470                 });
2471                 msgEl = bodyEl.dom.firstChild;
2472                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2473                 textboxEl.enableDisplayMode();
2474                 textboxEl.addKeyListener([10,13], function(){
2475                     if(dlg.isVisible() && opt && opt.buttons){
2476                         if(opt.buttons.ok){
2477                             handleButton("ok");
2478                         }else if(opt.buttons.yes){
2479                             handleButton("yes");
2480                         }
2481                     }
2482                 });
2483                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2484                 textareaEl.enableDisplayMode();
2485                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2486                 progressEl.enableDisplayMode();
2487                 var pf = progressEl.dom.firstChild;
2488                 if (pf) {
2489                     pp = Roo.get(pf.firstChild);
2490                     pp.setHeight(pf.offsetHeight);
2491                 }
2492                 
2493             }
2494             return dlg;
2495         },
2496
2497         /**
2498          * Updates the message box body text
2499          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2500          * the XHTML-compliant non-breaking space character '&amp;#160;')
2501          * @return {Roo.MessageBox} This message box
2502          */
2503         updateText : function(text){
2504             if(!dlg.isVisible() && !opt.width){
2505                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2506             }
2507             msgEl.innerHTML = text || '&#160;';
2508       
2509             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2510             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2511             var w = Math.max(
2512                     Math.min(opt.width || cw , this.maxWidth), 
2513                     Math.max(opt.minWidth || this.minWidth, bwidth)
2514             );
2515             if(opt.prompt){
2516                 activeTextEl.setWidth(w);
2517             }
2518             if(dlg.isVisible()){
2519                 dlg.fixedcenter = false;
2520             }
2521             // to big, make it scroll. = But as usual stupid IE does not support
2522             // !important..
2523             
2524             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2525                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2526                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2527             } else {
2528                 bodyEl.dom.style.height = '';
2529                 bodyEl.dom.style.overflowY = '';
2530             }
2531             if (cw > w) {
2532                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2533             } else {
2534                 bodyEl.dom.style.overflowX = '';
2535             }
2536             
2537             dlg.setContentSize(w, bodyEl.getHeight());
2538             if(dlg.isVisible()){
2539                 dlg.fixedcenter = true;
2540             }
2541             return this;
2542         },
2543
2544         /**
2545          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2546          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2547          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2548          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2549          * @return {Roo.MessageBox} This message box
2550          */
2551         updateProgress : function(value, text){
2552             if(text){
2553                 this.updateText(text);
2554             }
2555             if (pp) { // weird bug on my firefox - for some reason this is not defined
2556                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2557             }
2558             return this;
2559         },        
2560
2561         /**
2562          * Returns true if the message box is currently displayed
2563          * @return {Boolean} True if the message box is visible, else false
2564          */
2565         isVisible : function(){
2566             return dlg && dlg.isVisible();  
2567         },
2568
2569         /**
2570          * Hides the message box if it is displayed
2571          */
2572         hide : function(){
2573             if(this.isVisible()){
2574                 dlg.hide();
2575             }  
2576         },
2577
2578         /**
2579          * Displays a new message box, or reinitializes an existing message box, based on the config options
2580          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2581          * The following config object properties are supported:
2582          * <pre>
2583 Property    Type             Description
2584 ----------  ---------------  ------------------------------------------------------------------------------------
2585 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2586                                    closes (defaults to undefined)
2587 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2588                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2589 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2590                                    progress and wait dialogs will ignore this property and always hide the
2591                                    close button as they can only be closed programmatically.
2592 cls               String           A custom CSS class to apply to the message box element
2593 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2594                                    displayed (defaults to 75)
2595 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2596                                    function will be btn (the name of the button that was clicked, if applicable,
2597                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2598                                    Progress and wait dialogs will ignore this option since they do not respond to
2599                                    user actions and can only be closed programmatically, so any required function
2600                                    should be called by the same code after it closes the dialog.
2601 icon              String           A CSS class that provides a background image to be used as an icon for
2602                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2603 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2604 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2605 modal             Boolean          False to allow user interaction with the page while the message box is
2606                                    displayed (defaults to true)
2607 msg               String           A string that will replace the existing message box body text (defaults
2608                                    to the XHTML-compliant non-breaking space character '&#160;')
2609 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2610 progress          Boolean          True to display a progress bar (defaults to false)
2611 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2612 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2613 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2614 title             String           The title text
2615 value             String           The string value to set into the active textbox element if displayed
2616 wait              Boolean          True to display a progress bar (defaults to false)
2617 width             Number           The width of the dialog in pixels
2618 </pre>
2619          *
2620          * Example usage:
2621          * <pre><code>
2622 Roo.Msg.show({
2623    title: 'Address',
2624    msg: 'Please enter your address:',
2625    width: 300,
2626    buttons: Roo.MessageBox.OKCANCEL,
2627    multiline: true,
2628    fn: saveAddress,
2629    animEl: 'addAddressBtn'
2630 });
2631 </code></pre>
2632          * @param {Object} config Configuration options
2633          * @return {Roo.MessageBox} This message box
2634          */
2635         show : function(options)
2636         {
2637             
2638             // this causes nightmares if you show one dialog after another
2639             // especially on callbacks..
2640              
2641             if(this.isVisible()){
2642                 
2643                 this.hide();
2644                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2645                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2646                 Roo.log("New Dialog Message:" +  options.msg )
2647                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2648                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2649                 
2650             }
2651             var d = this.getDialog();
2652             opt = options;
2653             d.setTitle(opt.title || "&#160;");
2654             d.close.setDisplayed(opt.closable !== false);
2655             activeTextEl = textboxEl;
2656             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2657             if(opt.prompt){
2658                 if(opt.multiline){
2659                     textboxEl.hide();
2660                     textareaEl.show();
2661                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2662                         opt.multiline : this.defaultTextHeight);
2663                     activeTextEl = textareaEl;
2664                 }else{
2665                     textboxEl.show();
2666                     textareaEl.hide();
2667                 }
2668             }else{
2669                 textboxEl.hide();
2670                 textareaEl.hide();
2671             }
2672             progressEl.setDisplayed(opt.progress === true);
2673             this.updateProgress(0);
2674             activeTextEl.dom.value = opt.value || "";
2675             if(opt.prompt){
2676                 dlg.setDefaultButton(activeTextEl);
2677             }else{
2678                 var bs = opt.buttons;
2679                 var db = null;
2680                 if(bs && bs.ok){
2681                     db = buttons["ok"];
2682                 }else if(bs && bs.yes){
2683                     db = buttons["yes"];
2684                 }
2685                 dlg.setDefaultButton(db);
2686             }
2687             bwidth = updateButtons(opt.buttons);
2688             this.updateText(opt.msg);
2689             if(opt.cls){
2690                 d.el.addClass(opt.cls);
2691             }
2692             d.proxyDrag = opt.proxyDrag === true;
2693             d.modal = opt.modal !== false;
2694             d.mask = opt.modal !== false ? mask : false;
2695             if(!d.isVisible()){
2696                 // force it to the end of the z-index stack so it gets a cursor in FF
2697                 document.body.appendChild(dlg.el.dom);
2698                 d.animateTarget = null;
2699                 d.show(options.animEl);
2700             }
2701             return this;
2702         },
2703
2704         /**
2705          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2706          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2707          * and closing the message box when the process is complete.
2708          * @param {String} title The title bar text
2709          * @param {String} msg The message box body text
2710          * @return {Roo.MessageBox} This message box
2711          */
2712         progress : function(title, msg){
2713             this.show({
2714                 title : title,
2715                 msg : msg,
2716                 buttons: false,
2717                 progress:true,
2718                 closable:false,
2719                 minWidth: this.minProgressWidth,
2720                 modal : true
2721             });
2722             return this;
2723         },
2724
2725         /**
2726          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2727          * If a callback function is passed it will be called after the user clicks the button, and the
2728          * id of the button that was clicked will be passed as the only parameter to the callback
2729          * (could also be the top-right close button).
2730          * @param {String} title The title bar text
2731          * @param {String} msg The message box body text
2732          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2733          * @param {Object} scope (optional) The scope of the callback function
2734          * @return {Roo.MessageBox} This message box
2735          */
2736         alert : function(title, msg, fn, scope){
2737             this.show({
2738                 title : title,
2739                 msg : msg,
2740                 buttons: this.OK,
2741                 fn: fn,
2742                 scope : scope,
2743                 modal : true
2744             });
2745             return this;
2746         },
2747
2748         /**
2749          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2750          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2751          * You are responsible for closing the message box when the process is complete.
2752          * @param {String} msg The message box body text
2753          * @param {String} title (optional) The title bar text
2754          * @return {Roo.MessageBox} This message box
2755          */
2756         wait : function(msg, title){
2757             this.show({
2758                 title : title,
2759                 msg : msg,
2760                 buttons: false,
2761                 closable:false,
2762                 progress:true,
2763                 modal:true,
2764                 width:300,
2765                 wait:true
2766             });
2767             waitTimer = Roo.TaskMgr.start({
2768                 run: function(i){
2769                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2770                 },
2771                 interval: 1000
2772             });
2773             return this;
2774         },
2775
2776         /**
2777          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2778          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2779          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2780          * @param {String} title The title bar text
2781          * @param {String} msg The message box body text
2782          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2783          * @param {Object} scope (optional) The scope of the callback function
2784          * @return {Roo.MessageBox} This message box
2785          */
2786         confirm : function(title, msg, fn, scope){
2787             this.show({
2788                 title : title,
2789                 msg : msg,
2790                 buttons: this.YESNO,
2791                 fn: fn,
2792                 scope : scope,
2793                 modal : true
2794             });
2795             return this;
2796         },
2797
2798         /**
2799          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2800          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2801          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2802          * (could also be the top-right close button) and the text that was entered will be passed as the two
2803          * parameters to the callback.
2804          * @param {String} title The title bar text
2805          * @param {String} msg The message box body text
2806          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2807          * @param {Object} scope (optional) The scope of the callback function
2808          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2809          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2810          * @return {Roo.MessageBox} This message box
2811          */
2812         prompt : function(title, msg, fn, scope, multiline){
2813             this.show({
2814                 title : title,
2815                 msg : msg,
2816                 buttons: this.OKCANCEL,
2817                 fn: fn,
2818                 minWidth:250,
2819                 scope : scope,
2820                 prompt:true,
2821                 multiline: multiline,
2822                 modal : true
2823             });
2824             return this;
2825         },
2826
2827         /**
2828          * Button config that displays a single OK button
2829          * @type Object
2830          */
2831         OK : {ok:true},
2832         /**
2833          * Button config that displays Yes and No buttons
2834          * @type Object
2835          */
2836         YESNO : {yes:true, no:true},
2837         /**
2838          * Button config that displays OK and Cancel buttons
2839          * @type Object
2840          */
2841         OKCANCEL : {ok:true, cancel:true},
2842         /**
2843          * Button config that displays Yes, No and Cancel buttons
2844          * @type Object
2845          */
2846         YESNOCANCEL : {yes:true, no:true, cancel:true},
2847
2848         /**
2849          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2850          * @type Number
2851          */
2852         defaultTextHeight : 75,
2853         /**
2854          * The maximum width in pixels of the message box (defaults to 600)
2855          * @type Number
2856          */
2857         maxWidth : 600,
2858         /**
2859          * The minimum width in pixels of the message box (defaults to 100)
2860          * @type Number
2861          */
2862         minWidth : 100,
2863         /**
2864          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2865          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2866          * @type Number
2867          */
2868         minProgressWidth : 250,
2869         /**
2870          * An object containing the default button text strings that can be overriden for localized language support.
2871          * Supported properties are: ok, cancel, yes and no.
2872          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2873          * @type Object
2874          */
2875         buttonText : {
2876             ok : "OK",
2877             cancel : "Cancel",
2878             yes : "Yes",
2879             no : "No"
2880         }
2881     };
2882 }();
2883
2884 /**
2885  * Shorthand for {@link Roo.MessageBox}
2886  */
2887 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2888 Roo.Msg = Roo.Msg || Roo.MessageBox;
2889 /*
2890  * - LGPL
2891  *
2892  * navbar
2893  * 
2894  */
2895
2896 /**
2897  * @class Roo.bootstrap.Navbar
2898  * @extends Roo.bootstrap.Component
2899  * Bootstrap Navbar class
2900
2901  * @constructor
2902  * Create a new Navbar
2903  * @param {Object} config The config object
2904  */
2905
2906
2907 Roo.bootstrap.Navbar = function(config){
2908     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2909     
2910 };
2911
2912 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2913     
2914     
2915    
2916     // private
2917     navItems : false,
2918     loadMask : false,
2919     
2920     
2921     getAutoCreate : function(){
2922         
2923         
2924         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2925         
2926     },
2927     
2928     initEvents :function ()
2929     {
2930         //Roo.log(this.el.select('.navbar-toggle',true));
2931         this.el.select('.navbar-toggle',true).on('click', function() {
2932            // Roo.log('click');
2933             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2934         }, this);
2935         
2936         var mark = {
2937             tag: "div",
2938             cls:"x-dlg-mask"
2939         }
2940         
2941         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2942         
2943         var size = this.el.getSize();
2944         this.maskEl.setSize(size.width, size.height);
2945         this.maskEl.enableDisplayMode("block");
2946         this.maskEl.hide();
2947         
2948         if(this.loadMask){
2949             this.maskEl.show();
2950         }
2951     },
2952     
2953     
2954     getChildContainer : function()
2955     {
2956         if (this.el.select('.collapse').getCount()) {
2957             return this.el.select('.collapse',true).first();
2958         }
2959         
2960         return this.el;
2961     },
2962     
2963     mask : function()
2964     {
2965         this.maskEl.show();
2966     },
2967     
2968     unmask : function()
2969     {
2970         this.maskEl.hide();
2971     } 
2972     
2973     
2974     
2975     
2976 });
2977
2978
2979
2980  
2981
2982  /*
2983  * - LGPL
2984  *
2985  * navbar
2986  * 
2987  */
2988
2989 /**
2990  * @class Roo.bootstrap.NavSimplebar
2991  * @extends Roo.bootstrap.Navbar
2992  * Bootstrap Sidebar class
2993  *
2994  * @cfg {Boolean} inverse is inverted color
2995  * 
2996  * @cfg {String} type (nav | pills | tabs)
2997  * @cfg {Boolean} arrangement stacked | justified
2998  * @cfg {String} align (left | right) alignment
2999  * 
3000  * @cfg {Boolean} main (true|false) main nav bar? default false
3001  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3002  * 
3003  * @cfg {String} tag (header|footer|nav|div) default is nav 
3004
3005  * 
3006  * 
3007  * 
3008  * @constructor
3009  * Create a new Sidebar
3010  * @param {Object} config The config object
3011  */
3012
3013
3014 Roo.bootstrap.NavSimplebar = function(config){
3015     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3016 };
3017
3018 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3019     
3020     inverse: false,
3021     
3022     type: false,
3023     arrangement: '',
3024     align : false,
3025     
3026     
3027     
3028     main : false,
3029     
3030     
3031     tag : false,
3032     
3033     
3034     getAutoCreate : function(){
3035         
3036         
3037         var cfg = {
3038             tag : this.tag || 'div',
3039             cls : 'navbar'
3040         };
3041           
3042         
3043         cfg.cn = [
3044             {
3045                 cls: 'nav',
3046                 tag : 'ul'
3047             }
3048         ];
3049         
3050          
3051         this.type = this.type || 'nav';
3052         if (['tabs','pills'].indexOf(this.type)!==-1) {
3053             cfg.cn[0].cls += ' nav-' + this.type
3054         
3055         
3056         } else {
3057             if (this.type!=='nav') {
3058                 Roo.log('nav type must be nav/tabs/pills')
3059             }
3060             cfg.cn[0].cls += ' navbar-nav'
3061         }
3062         
3063         
3064         
3065         
3066         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3067             cfg.cn[0].cls += ' nav-' + this.arrangement;
3068         }
3069         
3070         
3071         if (this.align === 'right') {
3072             cfg.cn[0].cls += ' navbar-right';
3073         }
3074         
3075         if (this.inverse) {
3076             cfg.cls += ' navbar-inverse';
3077             
3078         }
3079         
3080         
3081         return cfg;
3082     
3083         
3084     }
3085     
3086     
3087     
3088 });
3089
3090
3091
3092  
3093
3094  
3095        /*
3096  * - LGPL
3097  *
3098  * navbar
3099  * 
3100  */
3101
3102 /**
3103  * @class Roo.bootstrap.NavHeaderbar
3104  * @extends Roo.bootstrap.NavSimplebar
3105  * Bootstrap Sidebar class
3106  *
3107  * @cfg {String} brand what is brand
3108  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3109  * @cfg {String} brand_href href of the brand
3110  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3111  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3112  * 
3113  * @constructor
3114  * Create a new Sidebar
3115  * @param {Object} config The config object
3116  */
3117
3118
3119 Roo.bootstrap.NavHeaderbar = function(config){
3120     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3121 };
3122
3123 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3124     
3125     position: '',
3126     brand: '',
3127     brand_href: false,
3128     srButton : true,
3129     autohide : false,
3130     
3131     getAutoCreate : function(){
3132         
3133         var   cfg = {
3134             tag: this.nav || 'nav',
3135             cls: 'navbar',
3136             role: 'navigation',
3137             cn: []
3138         };
3139         
3140         if(this.srButton){
3141             cfg.cn.push({
3142                 tag: 'div',
3143                 cls: 'navbar-header',
3144                 cn: [
3145                     {
3146                         tag: 'button',
3147                         type: 'button',
3148                         cls: 'navbar-toggle',
3149                         'data-toggle': 'collapse',
3150                         cn: [
3151                             {
3152                                 tag: 'span',
3153                                 cls: 'sr-only',
3154                                 html: 'Toggle navigation'
3155                             },
3156                             {
3157                                 tag: 'span',
3158                                 cls: 'icon-bar'
3159                             },
3160                             {
3161                                 tag: 'span',
3162                                 cls: 'icon-bar'
3163                             },
3164                             {
3165                                 tag: 'span',
3166                                 cls: 'icon-bar'
3167                             }
3168                         ]
3169                     }
3170                 ]
3171             });
3172         }
3173         
3174         cfg.cn.push({
3175             tag: 'div',
3176             cls: 'collapse navbar-collapse',
3177             cn : []
3178         });
3179         
3180         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3181         
3182         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3183             cfg.cls += ' navbar-' + this.position;
3184             
3185             // tag can override this..
3186             
3187             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3188         }
3189         
3190         if (this.brand !== '') {
3191             cfg.cn[0].cn.push({
3192                 tag: 'a',
3193                 href: this.brand_href ? this.brand_href : '#',
3194                 cls: 'navbar-brand',
3195                 cn: [
3196                 this.brand
3197                 ]
3198             });
3199         }
3200         
3201         if(this.main){
3202             cfg.cls += ' main-nav';
3203         }
3204         
3205         
3206         return cfg;
3207
3208         
3209     },
3210     initEvents : function()
3211     {
3212         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3213         
3214         if (this.autohide) {
3215             
3216             var prevScroll = 0;
3217             var ft = this.el;
3218             
3219             Roo.get(document).on('scroll',function(e) {
3220                 var ns = Roo.get(document).getScroll().top;
3221                 var os = prevScroll;
3222                 prevScroll = ns;
3223                 
3224                 if(ns > os){
3225                     ft.removeClass('slideDown');
3226                     ft.addClass('slideUp');
3227                     return;
3228                 }
3229                 ft.removeClass('slideUp');
3230                 ft.addClass('slideDown');
3231                  
3232               
3233           },this);
3234         }
3235     }    
3236           
3237       
3238     
3239     
3240 });
3241
3242
3243
3244  
3245
3246  /*
3247  * - LGPL
3248  *
3249  * navbar
3250  * 
3251  */
3252
3253 /**
3254  * @class Roo.bootstrap.NavSidebar
3255  * @extends Roo.bootstrap.Navbar
3256  * Bootstrap Sidebar class
3257  * 
3258  * @constructor
3259  * Create a new Sidebar
3260  * @param {Object} config The config object
3261  */
3262
3263
3264 Roo.bootstrap.NavSidebar = function(config){
3265     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3266 };
3267
3268 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3269     
3270     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3271     
3272     getAutoCreate : function(){
3273         
3274         
3275         return  {
3276             tag: 'div',
3277             cls: 'sidebar sidebar-nav'
3278         };
3279     
3280         
3281     }
3282     
3283     
3284     
3285 });
3286
3287
3288
3289  
3290
3291  /*
3292  * - LGPL
3293  *
3294  * nav group
3295  * 
3296  */
3297
3298 /**
3299  * @class Roo.bootstrap.NavGroup
3300  * @extends Roo.bootstrap.Component
3301  * Bootstrap NavGroup class
3302  * @cfg {String} align left | right
3303  * @cfg {Boolean} inverse false | true
3304  * @cfg {String} type (nav|pills|tab) default nav
3305  * @cfg {String} navId - reference Id for navbar.
3306
3307  * 
3308  * @constructor
3309  * Create a new nav group
3310  * @param {Object} config The config object
3311  */
3312
3313 Roo.bootstrap.NavGroup = function(config){
3314     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3315     this.navItems = [];
3316    
3317     Roo.bootstrap.NavGroup.register(this);
3318      this.addEvents({
3319         /**
3320              * @event changed
3321              * Fires when the active item changes
3322              * @param {Roo.bootstrap.NavGroup} this
3323              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3324              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3325          */
3326         'changed': true
3327      });
3328     
3329 };
3330
3331 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3332     
3333     align: '',
3334     inverse: false,
3335     form: false,
3336     type: 'nav',
3337     navId : '',
3338     // private
3339     
3340     navItems : false, 
3341     
3342     getAutoCreate : function()
3343     {
3344         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3345         
3346         cfg = {
3347             tag : 'ul',
3348             cls: 'nav' 
3349         }
3350         
3351         if (['tabs','pills'].indexOf(this.type)!==-1) {
3352             cfg.cls += ' nav-' + this.type
3353         } else {
3354             if (this.type!=='nav') {
3355                 Roo.log('nav type must be nav/tabs/pills')
3356             }
3357             cfg.cls += ' navbar-nav'
3358         }
3359         
3360         if (this.parent().sidebar) {
3361             cfg = {
3362                 tag: 'ul',
3363                 cls: 'dashboard-menu sidebar-menu'
3364             }
3365             
3366             return cfg;
3367         }
3368         
3369         if (this.form === true) {
3370             cfg = {
3371                 tag: 'form',
3372                 cls: 'navbar-form'
3373             }
3374             
3375             if (this.align === 'right') {
3376                 cfg.cls += ' navbar-right';
3377             } else {
3378                 cfg.cls += ' navbar-left';
3379             }
3380         }
3381         
3382         if (this.align === 'right') {
3383             cfg.cls += ' navbar-right';
3384         }
3385         
3386         if (this.inverse) {
3387             cfg.cls += ' navbar-inverse';
3388             
3389         }
3390         
3391         
3392         return cfg;
3393     },
3394     /**
3395     * sets the active Navigation item
3396     * @param {Roo.bootstrap.NavItem} the new current navitem
3397     */
3398     setActiveItem : function(item)
3399     {
3400         var prev = false;
3401         Roo.each(this.navItems, function(v){
3402             if (v == item) {
3403                 return ;
3404             }
3405             if (v.isActive()) {
3406                 v.setActive(false, true);
3407                 prev = v;
3408                 
3409             }
3410             
3411         });
3412
3413         item.setActive(true, true);
3414         this.fireEvent('changed', this, item, prev);
3415         
3416         
3417     },
3418     /**
3419     * gets the active Navigation item
3420     * @return {Roo.bootstrap.NavItem} the current navitem
3421     */
3422     getActive : function()
3423     {
3424         
3425         var prev = false;
3426         Roo.each(this.navItems, function(v){
3427             
3428             if (v.isActive()) {
3429                 prev = v;
3430                 
3431             }
3432             
3433         });
3434         return prev;
3435     },
3436     
3437     indexOfNav : function()
3438     {
3439         
3440         var prev = false;
3441         Roo.each(this.navItems, function(v,i){
3442             
3443             if (v.isActive()) {
3444                 prev = i;
3445                 
3446             }
3447             
3448         });
3449         return prev;
3450     },
3451     /**
3452     * adds a Navigation item
3453     * @param {Roo.bootstrap.NavItem} the navitem to add
3454     */
3455     addItem : function(cfg)
3456     {
3457         var cn = new Roo.bootstrap.NavItem(cfg);
3458         this.register(cn);
3459         cn.parentId = this.id;
3460         cn.onRender(this.el, null);
3461         return cn;
3462     },
3463     /**
3464     * register a Navigation item
3465     * @param {Roo.bootstrap.NavItem} the navitem to add
3466     */
3467     register : function(item)
3468     {
3469         this.navItems.push( item);
3470         item.navId = this.navId;
3471     
3472     },
3473   
3474     
3475     getNavItem: function(tabId)
3476     {
3477         var ret = false;
3478         Roo.each(this.navItems, function(e) {
3479             if (e.tabId == tabId) {
3480                ret =  e;
3481                return false;
3482             }
3483             return true;
3484             
3485         });
3486         return ret;
3487     },
3488     
3489     setActiveNext : function()
3490     {
3491         var i = this.indexOfNav(this.getActive());
3492         if (i > this.navItems.length) {
3493             return;
3494         }
3495         this.setActiveItem(this.navItems[i+1]);
3496     },
3497     setActivePrev : function()
3498     {
3499         var i = this.indexOfNav(this.getActive());
3500         if (i  < 1) {
3501             return;
3502         }
3503         this.setActiveItem(this.navItems[i-1]);
3504     },
3505     clearWasActive : function(except) {
3506         Roo.each(this.navItems, function(e) {
3507             if (e.tabId != except.tabId && e.was_active) {
3508                e.was_active = false;
3509                return false;
3510             }
3511             return true;
3512             
3513         });
3514     },
3515     getWasActive : function ()
3516     {
3517         var r = false;
3518         Roo.each(this.navItems, function(e) {
3519             if (e.was_active) {
3520                r = e;
3521                return false;
3522             }
3523             return true;
3524             
3525         });
3526         return r;
3527     }
3528     
3529     
3530 });
3531
3532  
3533 Roo.apply(Roo.bootstrap.NavGroup, {
3534     
3535     groups: {},
3536      /**
3537     * register a Navigation Group
3538     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3539     */
3540     register : function(navgrp)
3541     {
3542         this.groups[navgrp.navId] = navgrp;
3543         
3544     },
3545     /**
3546     * fetch a Navigation Group based on the navigation ID
3547     * @param {string} the navgroup to add
3548     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3549     */
3550     get: function(navId) {
3551         if (typeof(this.groups[navId]) == 'undefined') {
3552             return false;
3553             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3554         }
3555         return this.groups[navId] ;
3556     }
3557     
3558     
3559     
3560 });
3561
3562  /*
3563  * - LGPL
3564  *
3565  * row
3566  * 
3567  */
3568
3569 /**
3570  * @class Roo.bootstrap.NavItem
3571  * @extends Roo.bootstrap.Component
3572  * Bootstrap Navbar.NavItem class
3573  * @cfg {String} href  link to
3574  * @cfg {String} html content of button
3575  * @cfg {String} badge text inside badge
3576  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3577  * @cfg {String} glyphicon name of glyphicon
3578  * @cfg {String} icon name of font awesome icon
3579  * @cfg {Boolean} active Is item active
3580  * @cfg {Boolean} disabled Is item disabled
3581  
3582  * @cfg {Boolean} preventDefault (true | false) default false
3583  * @cfg {String} tabId the tab that this item activates.
3584  * @cfg {String} tagtype (a|span) render as a href or span?
3585   
3586  * @constructor
3587  * Create a new Navbar Item
3588  * @param {Object} config The config object
3589  */
3590 Roo.bootstrap.NavItem = function(config){
3591     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3592     this.addEvents({
3593         // raw events
3594         /**
3595          * @event click
3596          * The raw click event for the entire grid.
3597          * @param {Roo.EventObject} e
3598          */
3599         "click" : true,
3600          /**
3601             * @event changed
3602             * Fires when the active item active state changes
3603             * @param {Roo.bootstrap.NavItem} this
3604             * @param {boolean} state the new state
3605              
3606          */
3607         'changed': true
3608     });
3609    
3610 };
3611
3612 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3613     
3614     href: false,
3615     html: '',
3616     badge: '',
3617     icon: false,
3618     glyphicon: false,
3619     active: false,
3620     preventDefault : false,
3621     tabId : false,
3622     tagtype : 'a',
3623     disabled : false,
3624     
3625     was_active : false,
3626     
3627     getAutoCreate : function(){
3628          
3629         var cfg = {
3630             tag: 'li',
3631             cls: 'nav-item'
3632             
3633         }
3634         if (this.active) {
3635             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3636         }
3637         if (this.disabled) {
3638             cfg.cls += ' disabled';
3639         }
3640         
3641         if (this.href || this.html || this.glyphicon || this.icon) {
3642             cfg.cn = [
3643                 {
3644                     tag: this.tagtype,
3645                     href : this.href || "#",
3646                     html: this.html || ''
3647                 }
3648             ];
3649             
3650             if (this.icon) {
3651                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3652             }
3653
3654             if(this.glyphicon) {
3655                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3656             }
3657             
3658             if (this.menu) {
3659                 
3660                 cfg.cn[0].html += " <span class='caret'></span>";
3661              
3662             }
3663             
3664             if (this.badge !== '') {
3665                  
3666                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3667             }
3668         }
3669         
3670         
3671         
3672         return cfg;
3673     },
3674     initEvents: function() {
3675        // Roo.log('init events?');
3676        // Roo.log(this.el.dom);
3677         if (typeof (this.menu) != 'undefined') {
3678             this.menu.parentType = this.xtype;
3679             this.menu.triggerEl = this.el;
3680             this.addxtype(Roo.apply({}, this.menu));
3681         }
3682
3683        
3684         this.el.select('a',true).on('click', this.onClick, this);
3685         // at this point parent should be available..
3686         this.parent().register(this);
3687     },
3688     
3689     onClick : function(e)
3690     {
3691          
3692         if(this.preventDefault){
3693             e.preventDefault();
3694         }
3695         if (this.disabled) {
3696             return;
3697         }
3698         
3699         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3700         if (tg && tg.transition) {
3701             Roo.log("waiting for the transitionend");
3702             return;
3703         }
3704         
3705         Roo.log("fire event clicked");
3706         if(this.fireEvent('click', this, e) === false){
3707             return;
3708         };
3709         var p = this.parent();
3710         if (['tabs','pills'].indexOf(p.type)!==-1) {
3711             if (typeof(p.setActiveItem) !== 'undefined') {
3712                 p.setActiveItem(this);
3713             }
3714         }
3715         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3716         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3717             // remove the collapsed menu expand...
3718             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3719         }
3720         
3721     },
3722     
3723     isActive: function () {
3724         return this.active
3725     },
3726     setActive : function(state, fire, is_was_active)
3727     {
3728         if (this.active && !state & this.navId) {
3729             this.was_active = true;
3730             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3731             if (nv) {
3732                 nv.clearWasActive(this);
3733             }
3734             
3735         }
3736         this.active = state;
3737         
3738         if (!state ) {
3739             this.el.removeClass('active');
3740         } else if (!this.el.hasClass('active')) {
3741             this.el.addClass('active');
3742         }
3743         if (fire) {
3744             this.fireEvent('changed', this, state);
3745         }
3746         
3747         // show a panel if it's registered and related..
3748         
3749         if (!this.navId || !this.tabId || !state || is_was_active) {
3750             return;
3751         }
3752         
3753         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3754         if (!tg) {
3755             return;
3756         }
3757         var pan = tg.getPanelByName(this.tabId);
3758         if (!pan) {
3759             return;
3760         }
3761         // if we can not flip to new panel - go back to old nav highlight..
3762         if (false == tg.showPanel(pan)) {
3763             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3764             if (nv) {
3765                 var onav = nv.getWasActive();
3766                 if (onav) {
3767                     onav.setActive(true, false, true);
3768                 }
3769             }
3770             
3771         }
3772         
3773         
3774         
3775     },
3776      // this should not be here...
3777     setDisabled : function(state)
3778     {
3779         this.disabled = state;
3780         if (!state ) {
3781             this.el.removeClass('disabled');
3782         } else if (!this.el.hasClass('disabled')) {
3783             this.el.addClass('disabled');
3784         }
3785         
3786     }
3787 });
3788  
3789
3790  /*
3791  * - LGPL
3792  *
3793  * sidebar item
3794  *
3795  *  li
3796  *    <span> icon </span>
3797  *    <span> text </span>
3798  *    <span>badge </span>
3799  */
3800
3801 /**
3802  * @class Roo.bootstrap.NavSidebarItem
3803  * @extends Roo.bootstrap.NavItem
3804  * Bootstrap Navbar.NavSidebarItem class
3805  * @constructor
3806  * Create a new Navbar Button
3807  * @param {Object} config The config object
3808  */
3809 Roo.bootstrap.NavSidebarItem = function(config){
3810     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3811     this.addEvents({
3812         // raw events
3813         /**
3814          * @event click
3815          * The raw click event for the entire grid.
3816          * @param {Roo.EventObject} e
3817          */
3818         "click" : true,
3819          /**
3820             * @event changed
3821             * Fires when the active item active state changes
3822             * @param {Roo.bootstrap.NavSidebarItem} this
3823             * @param {boolean} state the new state
3824              
3825          */
3826         'changed': true
3827     });
3828    
3829 };
3830
3831 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3832     
3833     
3834     getAutoCreate : function(){
3835         
3836         
3837         var a = {
3838                 tag: 'a',
3839                 href : this.href || '#',
3840                 cls: '',
3841                 html : '',
3842                 cn : []
3843         };
3844         var cfg = {
3845             tag: 'li',
3846             cls: '',
3847             cn: [ a ]
3848         }
3849         var span = {
3850             tag: 'span',
3851             html : this.html || ''
3852         }
3853         
3854         
3855         if (this.active) {
3856             cfg.cls += ' active';
3857         }
3858         
3859         // left icon..
3860         if (this.glyphicon || this.icon) {
3861             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3862             a.cn.push({ tag : 'i', cls : c }) ;
3863         }
3864         // html..
3865         a.cn.push(span);
3866         // then badge..
3867         if (this.badge !== '') {
3868             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3869         }
3870         // fi
3871         if (this.menu) {
3872             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3873             a.cls += 'dropdown-toggle treeview' ;
3874             
3875         }
3876         
3877         
3878         
3879         return cfg;
3880          
3881            
3882     }
3883    
3884      
3885  
3886 });
3887  
3888
3889  /*
3890  * - LGPL
3891  *
3892  * row
3893  * 
3894  */
3895
3896 /**
3897  * @class Roo.bootstrap.Row
3898  * @extends Roo.bootstrap.Component
3899  * Bootstrap Row class (contains columns...)
3900  * 
3901  * @constructor
3902  * Create a new Row
3903  * @param {Object} config The config object
3904  */
3905
3906 Roo.bootstrap.Row = function(config){
3907     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3908 };
3909
3910 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3911     
3912     getAutoCreate : function(){
3913        return {
3914             cls: 'row clearfix'
3915        };
3916     }
3917     
3918     
3919 });
3920
3921  
3922
3923  /*
3924  * - LGPL
3925  *
3926  * element
3927  * 
3928  */
3929
3930 /**
3931  * @class Roo.bootstrap.Element
3932  * @extends Roo.bootstrap.Component
3933  * Bootstrap Element class
3934  * @cfg {String} html contents of the element
3935  * @cfg {String} tag tag of the element
3936  * @cfg {String} cls class of the element
3937  * 
3938  * @constructor
3939  * Create a new Element
3940  * @param {Object} config The config object
3941  */
3942
3943 Roo.bootstrap.Element = function(config){
3944     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3945 };
3946
3947 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3948     
3949     tag: 'div',
3950     cls: '',
3951     html: '',
3952      
3953     
3954     getAutoCreate : function(){
3955         
3956         var cfg = {
3957             tag: this.tag,
3958             cls: this.cls,
3959             html: this.html
3960         }
3961         
3962         
3963         
3964         return cfg;
3965     }
3966    
3967 });
3968
3969  
3970
3971  /*
3972  * - LGPL
3973  *
3974  * pagination
3975  * 
3976  */
3977
3978 /**
3979  * @class Roo.bootstrap.Pagination
3980  * @extends Roo.bootstrap.Component
3981  * Bootstrap Pagination class
3982  * @cfg {String} size xs | sm | md | lg
3983  * @cfg {Boolean} inverse false | true
3984  * 
3985  * @constructor
3986  * Create a new Pagination
3987  * @param {Object} config The config object
3988  */
3989
3990 Roo.bootstrap.Pagination = function(config){
3991     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3992 };
3993
3994 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3995     
3996     cls: false,
3997     size: false,
3998     inverse: false,
3999     
4000     getAutoCreate : function(){
4001         var cfg = {
4002             tag: 'ul',
4003                 cls: 'pagination'
4004         };
4005         if (this.inverse) {
4006             cfg.cls += ' inverse';
4007         }
4008         if (this.html) {
4009             cfg.html=this.html;
4010         }
4011         if (this.cls) {
4012             cfg.cls += " " + this.cls;
4013         }
4014         return cfg;
4015     }
4016    
4017 });
4018
4019  
4020
4021  /*
4022  * - LGPL
4023  *
4024  * Pagination item
4025  * 
4026  */
4027
4028
4029 /**
4030  * @class Roo.bootstrap.PaginationItem
4031  * @extends Roo.bootstrap.Component
4032  * Bootstrap PaginationItem class
4033  * @cfg {String} html text
4034  * @cfg {String} href the link
4035  * @cfg {Boolean} preventDefault (true | false) default true
4036  * @cfg {Boolean} active (true | false) default false
4037  * 
4038  * 
4039  * @constructor
4040  * Create a new PaginationItem
4041  * @param {Object} config The config object
4042  */
4043
4044
4045 Roo.bootstrap.PaginationItem = function(config){
4046     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4047     this.addEvents({
4048         // raw events
4049         /**
4050          * @event click
4051          * The raw click event for the entire grid.
4052          * @param {Roo.EventObject} e
4053          */
4054         "click" : true
4055     });
4056 };
4057
4058 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4059     
4060     href : false,
4061     html : false,
4062     preventDefault: true,
4063     active : false,
4064     cls : false,
4065     
4066     getAutoCreate : function(){
4067         var cfg= {
4068             tag: 'li',
4069             cn: [
4070                 {
4071                     tag : 'a',
4072                     href : this.href ? this.href : '#',
4073                     html : this.html ? this.html : ''
4074                 }
4075             ]
4076         };
4077         
4078         if(this.cls){
4079             cfg.cls = this.cls;
4080         }
4081         
4082         if(this.active){
4083             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4084         }
4085         
4086         return cfg;
4087     },
4088     
4089     initEvents: function() {
4090         
4091         this.el.on('click', this.onClick, this);
4092         
4093     },
4094     onClick : function(e)
4095     {
4096         Roo.log('PaginationItem on click ');
4097         if(this.preventDefault){
4098             e.preventDefault();
4099         }
4100         
4101         this.fireEvent('click', this, e);
4102     }
4103    
4104 });
4105
4106  
4107
4108  /*
4109  * - LGPL
4110  *
4111  * slider
4112  * 
4113  */
4114
4115
4116 /**
4117  * @class Roo.bootstrap.Slider
4118  * @extends Roo.bootstrap.Component
4119  * Bootstrap Slider class
4120  *    
4121  * @constructor
4122  * Create a new Slider
4123  * @param {Object} config The config object
4124  */
4125
4126 Roo.bootstrap.Slider = function(config){
4127     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4128 };
4129
4130 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4131     
4132     getAutoCreate : function(){
4133         
4134         var cfg = {
4135             tag: 'div',
4136             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4137             cn: [
4138                 {
4139                     tag: 'a',
4140                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4141                 }
4142             ]
4143         }
4144         
4145         return cfg;
4146     }
4147    
4148 });
4149
4150  /*
4151  * Based on:
4152  * Ext JS Library 1.1.1
4153  * Copyright(c) 2006-2007, Ext JS, LLC.
4154  *
4155  * Originally Released Under LGPL - original licence link has changed is not relivant.
4156  *
4157  * Fork - LGPL
4158  * <script type="text/javascript">
4159  */
4160  
4161
4162 /**
4163  * @class Roo.grid.ColumnModel
4164  * @extends Roo.util.Observable
4165  * This is the default implementation of a ColumnModel used by the Grid. It defines
4166  * the columns in the grid.
4167  * <br>Usage:<br>
4168  <pre><code>
4169  var colModel = new Roo.grid.ColumnModel([
4170         {header: "Ticker", width: 60, sortable: true, locked: true},
4171         {header: "Company Name", width: 150, sortable: true},
4172         {header: "Market Cap.", width: 100, sortable: true},
4173         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4174         {header: "Employees", width: 100, sortable: true, resizable: false}
4175  ]);
4176  </code></pre>
4177  * <p>
4178  
4179  * The config options listed for this class are options which may appear in each
4180  * individual column definition.
4181  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4182  * @constructor
4183  * @param {Object} config An Array of column config objects. See this class's
4184  * config objects for details.
4185 */
4186 Roo.grid.ColumnModel = function(config){
4187         /**
4188      * The config passed into the constructor
4189      */
4190     this.config = config;
4191     this.lookup = {};
4192
4193     // if no id, create one
4194     // if the column does not have a dataIndex mapping,
4195     // map it to the order it is in the config
4196     for(var i = 0, len = config.length; i < len; i++){
4197         var c = config[i];
4198         if(typeof c.dataIndex == "undefined"){
4199             c.dataIndex = i;
4200         }
4201         if(typeof c.renderer == "string"){
4202             c.renderer = Roo.util.Format[c.renderer];
4203         }
4204         if(typeof c.id == "undefined"){
4205             c.id = Roo.id();
4206         }
4207         if(c.editor && c.editor.xtype){
4208             c.editor  = Roo.factory(c.editor, Roo.grid);
4209         }
4210         if(c.editor && c.editor.isFormField){
4211             c.editor = new Roo.grid.GridEditor(c.editor);
4212         }
4213         this.lookup[c.id] = c;
4214     }
4215
4216     /**
4217      * The width of columns which have no width specified (defaults to 100)
4218      * @type Number
4219      */
4220     this.defaultWidth = 100;
4221
4222     /**
4223      * Default sortable of columns which have no sortable specified (defaults to false)
4224      * @type Boolean
4225      */
4226     this.defaultSortable = false;
4227
4228     this.addEvents({
4229         /**
4230              * @event widthchange
4231              * Fires when the width of a column changes.
4232              * @param {ColumnModel} this
4233              * @param {Number} columnIndex The column index
4234              * @param {Number} newWidth The new width
4235              */
4236             "widthchange": true,
4237         /**
4238              * @event headerchange
4239              * Fires when the text of a header changes.
4240              * @param {ColumnModel} this
4241              * @param {Number} columnIndex The column index
4242              * @param {Number} newText The new header text
4243              */
4244             "headerchange": true,
4245         /**
4246              * @event hiddenchange
4247              * Fires when a column is hidden or "unhidden".
4248              * @param {ColumnModel} this
4249              * @param {Number} columnIndex The column index
4250              * @param {Boolean} hidden true if hidden, false otherwise
4251              */
4252             "hiddenchange": true,
4253             /**
4254          * @event columnmoved
4255          * Fires when a column is moved.
4256          * @param {ColumnModel} this
4257          * @param {Number} oldIndex
4258          * @param {Number} newIndex
4259          */
4260         "columnmoved" : true,
4261         /**
4262          * @event columlockchange
4263          * Fires when a column's locked state is changed
4264          * @param {ColumnModel} this
4265          * @param {Number} colIndex
4266          * @param {Boolean} locked true if locked
4267          */
4268         "columnlockchange" : true
4269     });
4270     Roo.grid.ColumnModel.superclass.constructor.call(this);
4271 };
4272 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4273     /**
4274      * @cfg {String} header The header text to display in the Grid view.
4275      */
4276     /**
4277      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4278      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4279      * specified, the column's index is used as an index into the Record's data Array.
4280      */
4281     /**
4282      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4283      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4284      */
4285     /**
4286      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4287      * Defaults to the value of the {@link #defaultSortable} property.
4288      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4289      */
4290     /**
4291      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4292      */
4293     /**
4294      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4295      */
4296     /**
4297      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4298      */
4299     /**
4300      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4301      */
4302     /**
4303      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4304      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4305      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4306      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4307      */
4308        /**
4309      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4310      */
4311     /**
4312      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4313      */
4314
4315     /**
4316      * Returns the id of the column at the specified index.
4317      * @param {Number} index The column index
4318      * @return {String} the id
4319      */
4320     getColumnId : function(index){
4321         return this.config[index].id;
4322     },
4323
4324     /**
4325      * Returns the column for a specified id.
4326      * @param {String} id The column id
4327      * @return {Object} the column
4328      */
4329     getColumnById : function(id){
4330         return this.lookup[id];
4331     },
4332
4333     
4334     /**
4335      * Returns the column for a specified dataIndex.
4336      * @param {String} dataIndex The column dataIndex
4337      * @return {Object|Boolean} the column or false if not found
4338      */
4339     getColumnByDataIndex: function(dataIndex){
4340         var index = this.findColumnIndex(dataIndex);
4341         return index > -1 ? this.config[index] : false;
4342     },
4343     
4344     /**
4345      * Returns the index for a specified column id.
4346      * @param {String} id The column id
4347      * @return {Number} the index, or -1 if not found
4348      */
4349     getIndexById : function(id){
4350         for(var i = 0, len = this.config.length; i < len; i++){
4351             if(this.config[i].id == id){
4352                 return i;
4353             }
4354         }
4355         return -1;
4356     },
4357     
4358     /**
4359      * Returns the index for a specified column dataIndex.
4360      * @param {String} dataIndex The column dataIndex
4361      * @return {Number} the index, or -1 if not found
4362      */
4363     
4364     findColumnIndex : function(dataIndex){
4365         for(var i = 0, len = this.config.length; i < len; i++){
4366             if(this.config[i].dataIndex == dataIndex){
4367                 return i;
4368             }
4369         }
4370         return -1;
4371     },
4372     
4373     
4374     moveColumn : function(oldIndex, newIndex){
4375         var c = this.config[oldIndex];
4376         this.config.splice(oldIndex, 1);
4377         this.config.splice(newIndex, 0, c);
4378         this.dataMap = null;
4379         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4380     },
4381
4382     isLocked : function(colIndex){
4383         return this.config[colIndex].locked === true;
4384     },
4385
4386     setLocked : function(colIndex, value, suppressEvent){
4387         if(this.isLocked(colIndex) == value){
4388             return;
4389         }
4390         this.config[colIndex].locked = value;
4391         if(!suppressEvent){
4392             this.fireEvent("columnlockchange", this, colIndex, value);
4393         }
4394     },
4395
4396     getTotalLockedWidth : function(){
4397         var totalWidth = 0;
4398         for(var i = 0; i < this.config.length; i++){
4399             if(this.isLocked(i) && !this.isHidden(i)){
4400                 this.totalWidth += this.getColumnWidth(i);
4401             }
4402         }
4403         return totalWidth;
4404     },
4405
4406     getLockedCount : function(){
4407         for(var i = 0, len = this.config.length; i < len; i++){
4408             if(!this.isLocked(i)){
4409                 return i;
4410             }
4411         }
4412     },
4413
4414     /**
4415      * Returns the number of columns.
4416      * @return {Number}
4417      */
4418     getColumnCount : function(visibleOnly){
4419         if(visibleOnly === true){
4420             var c = 0;
4421             for(var i = 0, len = this.config.length; i < len; i++){
4422                 if(!this.isHidden(i)){
4423                     c++;
4424                 }
4425             }
4426             return c;
4427         }
4428         return this.config.length;
4429     },
4430
4431     /**
4432      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4433      * @param {Function} fn
4434      * @param {Object} scope (optional)
4435      * @return {Array} result
4436      */
4437     getColumnsBy : function(fn, scope){
4438         var r = [];
4439         for(var i = 0, len = this.config.length; i < len; i++){
4440             var c = this.config[i];
4441             if(fn.call(scope||this, c, i) === true){
4442                 r[r.length] = c;
4443             }
4444         }
4445         return r;
4446     },
4447
4448     /**
4449      * Returns true if the specified column is sortable.
4450      * @param {Number} col The column index
4451      * @return {Boolean}
4452      */
4453     isSortable : function(col){
4454         if(typeof this.config[col].sortable == "undefined"){
4455             return this.defaultSortable;
4456         }
4457         return this.config[col].sortable;
4458     },
4459
4460     /**
4461      * Returns the rendering (formatting) function defined for the column.
4462      * @param {Number} col The column index.
4463      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4464      */
4465     getRenderer : function(col){
4466         if(!this.config[col].renderer){
4467             return Roo.grid.ColumnModel.defaultRenderer;
4468         }
4469         return this.config[col].renderer;
4470     },
4471
4472     /**
4473      * Sets the rendering (formatting) function for a column.
4474      * @param {Number} col The column index
4475      * @param {Function} fn The function to use to process the cell's raw data
4476      * to return HTML markup for the grid view. The render function is called with
4477      * the following parameters:<ul>
4478      * <li>Data value.</li>
4479      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4480      * <li>css A CSS style string to apply to the table cell.</li>
4481      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4482      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4483      * <li>Row index</li>
4484      * <li>Column index</li>
4485      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4486      */
4487     setRenderer : function(col, fn){
4488         this.config[col].renderer = fn;
4489     },
4490
4491     /**
4492      * Returns the width for the specified column.
4493      * @param {Number} col The column index
4494      * @return {Number}
4495      */
4496     getColumnWidth : function(col){
4497         return this.config[col].width * 1 || this.defaultWidth;
4498     },
4499
4500     /**
4501      * Sets the width for a column.
4502      * @param {Number} col The column index
4503      * @param {Number} width The new width
4504      */
4505     setColumnWidth : function(col, width, suppressEvent){
4506         this.config[col].width = width;
4507         this.totalWidth = null;
4508         if(!suppressEvent){
4509              this.fireEvent("widthchange", this, col, width);
4510         }
4511     },
4512
4513     /**
4514      * Returns the total width of all columns.
4515      * @param {Boolean} includeHidden True to include hidden column widths
4516      * @return {Number}
4517      */
4518     getTotalWidth : function(includeHidden){
4519         if(!this.totalWidth){
4520             this.totalWidth = 0;
4521             for(var i = 0, len = this.config.length; i < len; i++){
4522                 if(includeHidden || !this.isHidden(i)){
4523                     this.totalWidth += this.getColumnWidth(i);
4524                 }
4525             }
4526         }
4527         return this.totalWidth;
4528     },
4529
4530     /**
4531      * Returns the header for the specified column.
4532      * @param {Number} col The column index
4533      * @return {String}
4534      */
4535     getColumnHeader : function(col){
4536         return this.config[col].header;
4537     },
4538
4539     /**
4540      * Sets the header for a column.
4541      * @param {Number} col The column index
4542      * @param {String} header The new header
4543      */
4544     setColumnHeader : function(col, header){
4545         this.config[col].header = header;
4546         this.fireEvent("headerchange", this, col, header);
4547     },
4548
4549     /**
4550      * Returns the tooltip for the specified column.
4551      * @param {Number} col The column index
4552      * @return {String}
4553      */
4554     getColumnTooltip : function(col){
4555             return this.config[col].tooltip;
4556     },
4557     /**
4558      * Sets the tooltip for a column.
4559      * @param {Number} col The column index
4560      * @param {String} tooltip The new tooltip
4561      */
4562     setColumnTooltip : function(col, tooltip){
4563             this.config[col].tooltip = tooltip;
4564     },
4565
4566     /**
4567      * Returns the dataIndex for the specified column.
4568      * @param {Number} col The column index
4569      * @return {Number}
4570      */
4571     getDataIndex : function(col){
4572         return this.config[col].dataIndex;
4573     },
4574
4575     /**
4576      * Sets the dataIndex for a column.
4577      * @param {Number} col The column index
4578      * @param {Number} dataIndex The new dataIndex
4579      */
4580     setDataIndex : function(col, dataIndex){
4581         this.config[col].dataIndex = dataIndex;
4582     },
4583
4584     
4585     
4586     /**
4587      * Returns true if the cell is editable.
4588      * @param {Number} colIndex The column index
4589      * @param {Number} rowIndex The row index
4590      * @return {Boolean}
4591      */
4592     isCellEditable : function(colIndex, rowIndex){
4593         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4594     },
4595
4596     /**
4597      * Returns the editor defined for the cell/column.
4598      * return false or null to disable editing.
4599      * @param {Number} colIndex The column index
4600      * @param {Number} rowIndex The row index
4601      * @return {Object}
4602      */
4603     getCellEditor : function(colIndex, rowIndex){
4604         return this.config[colIndex].editor;
4605     },
4606
4607     /**
4608      * Sets if a column is editable.
4609      * @param {Number} col The column index
4610      * @param {Boolean} editable True if the column is editable
4611      */
4612     setEditable : function(col, editable){
4613         this.config[col].editable = editable;
4614     },
4615
4616
4617     /**
4618      * Returns true if the column is hidden.
4619      * @param {Number} colIndex The column index
4620      * @return {Boolean}
4621      */
4622     isHidden : function(colIndex){
4623         return this.config[colIndex].hidden;
4624     },
4625
4626
4627     /**
4628      * Returns true if the column width cannot be changed
4629      */
4630     isFixed : function(colIndex){
4631         return this.config[colIndex].fixed;
4632     },
4633
4634     /**
4635      * Returns true if the column can be resized
4636      * @return {Boolean}
4637      */
4638     isResizable : function(colIndex){
4639         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4640     },
4641     /**
4642      * Sets if a column is hidden.
4643      * @param {Number} colIndex The column index
4644      * @param {Boolean} hidden True if the column is hidden
4645      */
4646     setHidden : function(colIndex, hidden){
4647         this.config[colIndex].hidden = hidden;
4648         this.totalWidth = null;
4649         this.fireEvent("hiddenchange", this, colIndex, hidden);
4650     },
4651
4652     /**
4653      * Sets the editor for a column.
4654      * @param {Number} col The column index
4655      * @param {Object} editor The editor object
4656      */
4657     setEditor : function(col, editor){
4658         this.config[col].editor = editor;
4659     }
4660 });
4661
4662 Roo.grid.ColumnModel.defaultRenderer = function(value){
4663         if(typeof value == "string" && value.length < 1){
4664             return "&#160;";
4665         }
4666         return value;
4667 };
4668
4669 // Alias for backwards compatibility
4670 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4671 /*
4672  * Based on:
4673  * Ext JS Library 1.1.1
4674  * Copyright(c) 2006-2007, Ext JS, LLC.
4675  *
4676  * Originally Released Under LGPL - original licence link has changed is not relivant.
4677  *
4678  * Fork - LGPL
4679  * <script type="text/javascript">
4680  */
4681  
4682 /**
4683  * @class Roo.LoadMask
4684  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4685  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4686  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4687  * element's UpdateManager load indicator and will be destroyed after the initial load.
4688  * @constructor
4689  * Create a new LoadMask
4690  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4691  * @param {Object} config The config object
4692  */
4693 Roo.LoadMask = function(el, config){
4694     this.el = Roo.get(el);
4695     Roo.apply(this, config);
4696     if(this.store){
4697         this.store.on('beforeload', this.onBeforeLoad, this);
4698         this.store.on('load', this.onLoad, this);
4699         this.store.on('loadexception', this.onLoadException, this);
4700         this.removeMask = false;
4701     }else{
4702         var um = this.el.getUpdateManager();
4703         um.showLoadIndicator = false; // disable the default indicator
4704         um.on('beforeupdate', this.onBeforeLoad, this);
4705         um.on('update', this.onLoad, this);
4706         um.on('failure', this.onLoad, this);
4707         this.removeMask = true;
4708     }
4709 };
4710
4711 Roo.LoadMask.prototype = {
4712     /**
4713      * @cfg {Boolean} removeMask
4714      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4715      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4716      */
4717     /**
4718      * @cfg {String} msg
4719      * The text to display in a centered loading message box (defaults to 'Loading...')
4720      */
4721     msg : 'Loading...',
4722     /**
4723      * @cfg {String} msgCls
4724      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4725      */
4726     msgCls : 'x-mask-loading',
4727
4728     /**
4729      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4730      * @type Boolean
4731      */
4732     disabled: false,
4733
4734     /**
4735      * Disables the mask to prevent it from being displayed
4736      */
4737     disable : function(){
4738        this.disabled = true;
4739     },
4740
4741     /**
4742      * Enables the mask so that it can be displayed
4743      */
4744     enable : function(){
4745         this.disabled = false;
4746     },
4747     
4748     onLoadException : function()
4749     {
4750         Roo.log(arguments);
4751         
4752         if (typeof(arguments[3]) != 'undefined') {
4753             Roo.MessageBox.alert("Error loading",arguments[3]);
4754         } 
4755         /*
4756         try {
4757             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4758                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4759             }   
4760         } catch(e) {
4761             
4762         }
4763         */
4764     
4765         
4766         
4767         this.el.unmask(this.removeMask);
4768     },
4769     // private
4770     onLoad : function()
4771     {
4772         this.el.unmask(this.removeMask);
4773     },
4774
4775     // private
4776     onBeforeLoad : function(){
4777         if(!this.disabled){
4778             this.el.mask(this.msg, this.msgCls);
4779         }
4780     },
4781
4782     // private
4783     destroy : function(){
4784         if(this.store){
4785             this.store.un('beforeload', this.onBeforeLoad, this);
4786             this.store.un('load', this.onLoad, this);
4787             this.store.un('loadexception', this.onLoadException, this);
4788         }else{
4789             var um = this.el.getUpdateManager();
4790             um.un('beforeupdate', this.onBeforeLoad, this);
4791             um.un('update', this.onLoad, this);
4792             um.un('failure', this.onLoad, this);
4793         }
4794     }
4795 };/*
4796  * - LGPL
4797  *
4798  * table
4799  * 
4800  */
4801
4802 /**
4803  * @class Roo.bootstrap.Table
4804  * @extends Roo.bootstrap.Component
4805  * Bootstrap Table class
4806  * @cfg {String} cls table class
4807  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4808  * @cfg {String} bgcolor Specifies the background color for a table
4809  * @cfg {Number} border Specifies whether the table cells should have borders or not
4810  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4811  * @cfg {Number} cellspacing Specifies the space between cells
4812  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4813  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4814  * @cfg {String} sortable Specifies that the table should be sortable
4815  * @cfg {String} summary Specifies a summary of the content of a table
4816  * @cfg {Number} width Specifies the width of a table
4817  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4818  * 
4819  * @cfg {boolean} striped Should the rows be alternative striped
4820  * @cfg {boolean} bordered Add borders to the table
4821  * @cfg {boolean} hover Add hover highlighting
4822  * @cfg {boolean} condensed Format condensed
4823  * @cfg {boolean} responsive Format condensed
4824  * @cfg {Boolean} loadMask (true|false) default false
4825  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4826  * @cfg {Boolean} thead (true|false) generate thead, default true
4827  * @cfg {Boolean} RowSelection (true|false) default false
4828  * @cfg {Boolean} CellSelection (true|false) default false
4829  *
4830  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4831  
4832  * 
4833  * @constructor
4834  * Create a new Table
4835  * @param {Object} config The config object
4836  */
4837
4838 Roo.bootstrap.Table = function(config){
4839     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4840     
4841     if (this.sm) {
4842         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4843         this.sm = this.selModel;
4844         this.sm.xmodule = this.xmodule || false;
4845     }
4846     if (this.cm && typeof(this.cm.config) == 'undefined') {
4847         this.colModel = new Roo.grid.ColumnModel(this.cm);
4848         this.cm = this.colModel;
4849         this.cm.xmodule = this.xmodule || false;
4850     }
4851     if (this.store) {
4852         this.store= Roo.factory(this.store, Roo.data);
4853         this.ds = this.store;
4854         this.ds.xmodule = this.xmodule || false;
4855          
4856     }
4857     if (this.footer && this.store) {
4858         this.footer.dataSource = this.ds;
4859         this.footer = Roo.factory(this.footer);
4860     }
4861     
4862     /** @private */
4863     this.addEvents({
4864         /**
4865          * @event cellclick
4866          * Fires when a cell is clicked
4867          * @param {Roo.bootstrap.Table} this
4868          * @param {Roo.Element} el
4869          * @param {Number} rowIndex
4870          * @param {Number} columnIndex
4871          * @param {Roo.EventObject} e
4872          */
4873         "cellclick" : true,
4874         /**
4875          * @event celldblclick
4876          * Fires when a cell is double clicked
4877          * @param {Roo.bootstrap.Table} this
4878          * @param {Roo.Element} el
4879          * @param {Number} rowIndex
4880          * @param {Number} columnIndex
4881          * @param {Roo.EventObject} e
4882          */
4883         "celldblclick" : true,
4884         /**
4885          * @event rowclick
4886          * Fires when a row is clicked
4887          * @param {Roo.bootstrap.Table} this
4888          * @param {Roo.Element} el
4889          * @param {Number} rowIndex
4890          * @param {Roo.EventObject} e
4891          */
4892         "rowclick" : true,
4893         /**
4894          * @event rowdblclick
4895          * Fires when a row is double clicked
4896          * @param {Roo.bootstrap.Table} this
4897          * @param {Roo.Element} el
4898          * @param {Number} rowIndex
4899          * @param {Roo.EventObject} e
4900          */
4901         "rowdblclick" : true,
4902         /**
4903          * @event mouseover
4904          * Fires when a mouseover occur
4905          * @param {Roo.bootstrap.Table} this
4906          * @param {Roo.Element} el
4907          * @param {Number} rowIndex
4908          * @param {Number} columnIndex
4909          * @param {Roo.EventObject} e
4910          */
4911         "mouseover" : true,
4912         /**
4913          * @event mouseout
4914          * Fires when a mouseout occur
4915          * @param {Roo.bootstrap.Table} this
4916          * @param {Roo.Element} el
4917          * @param {Number} rowIndex
4918          * @param {Number} columnIndex
4919          * @param {Roo.EventObject} e
4920          */
4921         "mouseout" : true,
4922         /**
4923          * @event rowclass
4924          * Fires when a row is rendered, so you can change add a style to it.
4925          * @param {Roo.bootstrap.Table} this
4926          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4927          */
4928         'rowclass' : true
4929         
4930     });
4931 };
4932
4933 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4934     
4935     cls: false,
4936     align: false,
4937     bgcolor: false,
4938     border: false,
4939     cellpadding: false,
4940     cellspacing: false,
4941     frame: false,
4942     rules: false,
4943     sortable: false,
4944     summary: false,
4945     width: false,
4946     striped : false,
4947     bordered: false,
4948     hover:  false,
4949     condensed : false,
4950     responsive : false,
4951     sm : false,
4952     cm : false,
4953     store : false,
4954     loadMask : false,
4955     tfoot : true,
4956     thead : true,
4957     RowSelection : false,
4958     CellSelection : false,
4959     layout : false,
4960     
4961     // Roo.Element - the tbody
4962     mainBody: false, 
4963     
4964     getAutoCreate : function(){
4965         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4966         
4967         cfg = {
4968             tag: 'table',
4969             cls : 'table',
4970             cn : []
4971         }
4972             
4973         if (this.striped) {
4974             cfg.cls += ' table-striped';
4975         }
4976         
4977         if (this.hover) {
4978             cfg.cls += ' table-hover';
4979         }
4980         if (this.bordered) {
4981             cfg.cls += ' table-bordered';
4982         }
4983         if (this.condensed) {
4984             cfg.cls += ' table-condensed';
4985         }
4986         if (this.responsive) {
4987             cfg.cls += ' table-responsive';
4988         }
4989         
4990         if (this.cls) {
4991             cfg.cls+=  ' ' +this.cls;
4992         }
4993         
4994         // this lot should be simplifed...
4995         
4996         if (this.align) {
4997             cfg.align=this.align;
4998         }
4999         if (this.bgcolor) {
5000             cfg.bgcolor=this.bgcolor;
5001         }
5002         if (this.border) {
5003             cfg.border=this.border;
5004         }
5005         if (this.cellpadding) {
5006             cfg.cellpadding=this.cellpadding;
5007         }
5008         if (this.cellspacing) {
5009             cfg.cellspacing=this.cellspacing;
5010         }
5011         if (this.frame) {
5012             cfg.frame=this.frame;
5013         }
5014         if (this.rules) {
5015             cfg.rules=this.rules;
5016         }
5017         if (this.sortable) {
5018             cfg.sortable=this.sortable;
5019         }
5020         if (this.summary) {
5021             cfg.summary=this.summary;
5022         }
5023         if (this.width) {
5024             cfg.width=this.width;
5025         }
5026         if (this.layout) {
5027             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5028         }
5029         
5030         if(this.store || this.cm){
5031             if(this.thead){
5032                 cfg.cn.push(this.renderHeader());
5033             }
5034             
5035             cfg.cn.push(this.renderBody());
5036             
5037             if(this.tfoot){
5038                 cfg.cn.push(this.renderFooter());
5039             }
5040             
5041             cfg.cls+=  ' TableGrid';
5042         }
5043         
5044         return { cn : [ cfg ] };
5045     },
5046     
5047     initEvents : function()
5048     {   
5049         if(!this.store || !this.cm){
5050             return;
5051         }
5052         
5053         //Roo.log('initEvents with ds!!!!');
5054         
5055         this.mainBody = this.el.select('tbody', true).first();
5056         
5057         
5058         var _this = this;
5059         
5060         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5061             e.on('click', _this.sort, _this);
5062         });
5063         
5064         this.el.on("click", this.onClick, this);
5065         this.el.on("dblclick", this.onDblClick, this);
5066         
5067         this.parent().el.setStyle('position', 'relative');
5068         if (this.footer) {
5069             this.footer.parentId = this.id;
5070             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5071         }
5072         
5073         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5074         
5075         this.store.on('load', this.onLoad, this);
5076         this.store.on('beforeload', this.onBeforeLoad, this);
5077         this.store.on('update', this.onUpdate, this);
5078         
5079     },
5080     
5081     onMouseover : function(e, el)
5082     {
5083         var cell = Roo.get(el);
5084         
5085         if(!cell){
5086             return;
5087         }
5088         
5089         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5090             cell = cell.findParent('td', false, true);
5091         }
5092         
5093         var row = cell.findParent('tr', false, true);
5094         var cellIndex = cell.dom.cellIndex;
5095         var rowIndex = row.dom.rowIndex - 1; // start from 0
5096         
5097         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5098         
5099     },
5100     
5101     onMouseout : function(e, el)
5102     {
5103         var cell = Roo.get(el);
5104         
5105         if(!cell){
5106             return;
5107         }
5108         
5109         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5110             cell = cell.findParent('td', false, true);
5111         }
5112         
5113         var row = cell.findParent('tr', false, true);
5114         var cellIndex = cell.dom.cellIndex;
5115         var rowIndex = row.dom.rowIndex - 1; // start from 0
5116         
5117         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5118         
5119     },
5120     
5121     onClick : function(e, el)
5122     {
5123         var cell = Roo.get(el);
5124         
5125         if(!cell || (!this.CellSelection && !this.RowSelection)){
5126             return;
5127         }
5128         
5129         
5130         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5131             cell = cell.findParent('td', false, true);
5132         }
5133         
5134         var row = cell.findParent('tr', false, true);
5135         var cellIndex = cell.dom.cellIndex;
5136         var rowIndex = row.dom.rowIndex - 1;
5137         
5138         if(this.CellSelection){
5139             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5140         }
5141         
5142         if(this.RowSelection){
5143             this.fireEvent('rowclick', this, row, rowIndex, e);
5144         }
5145         
5146         
5147     },
5148     
5149     onDblClick : function(e,el)
5150     {
5151         var cell = Roo.get(el);
5152         
5153         if(!cell || (!this.CellSelection && !this.RowSelection)){
5154             return;
5155         }
5156         
5157         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5158             cell = cell.findParent('td', false, true);
5159         }
5160         
5161         var row = cell.findParent('tr', false, true);
5162         var cellIndex = cell.dom.cellIndex;
5163         var rowIndex = row.dom.rowIndex - 1;
5164         
5165         if(this.CellSelection){
5166             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5167         }
5168         
5169         if(this.RowSelection){
5170             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5171         }
5172     },
5173     
5174     sort : function(e,el)
5175     {
5176         var col = Roo.get(el)
5177         
5178         if(!col.hasClass('sortable')){
5179             return;
5180         }
5181         
5182         var sort = col.attr('sort');
5183         var dir = 'ASC';
5184         
5185         if(col.hasClass('glyphicon-arrow-up')){
5186             dir = 'DESC';
5187         }
5188         
5189         this.store.sortInfo = {field : sort, direction : dir};
5190         
5191         if (this.footer) {
5192             Roo.log("calling footer first");
5193             this.footer.onClick('first');
5194         } else {
5195         
5196             this.store.load({ params : { start : 0 } });
5197         }
5198     },
5199     
5200     renderHeader : function()
5201     {
5202         var header = {
5203             tag: 'thead',
5204             cn : []
5205         };
5206         
5207         var cm = this.cm;
5208         
5209         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5210             
5211             var config = cm.config[i];
5212                     
5213             var c = {
5214                 tag: 'th',
5215                 style : '',
5216                 html: cm.getColumnHeader(i)
5217             };
5218             
5219             if(typeof(config.hidden) != 'undefined' && config.hidden){
5220                 c.style += ' display:none;';
5221             }
5222             
5223             if(typeof(config.dataIndex) != 'undefined'){
5224                 c.sort = config.dataIndex;
5225             }
5226             
5227             if(typeof(config.sortable) != 'undefined' && config.sortable){
5228                 c.cls = 'sortable';
5229             }
5230             
5231             if(typeof(config.align) != 'undefined' && config.align.length){
5232                 c.style += ' text-align:' + config.align + ';';
5233             }
5234             
5235             if(typeof(config.width) != 'undefined'){
5236                 c.style += ' width:' + config.width + 'px;';
5237             }
5238             
5239             header.cn.push(c)
5240         }
5241         
5242         return header;
5243     },
5244     
5245     renderBody : function()
5246     {
5247         var body = {
5248             tag: 'tbody',
5249             cn : [
5250                 {
5251                     tag: 'tr',
5252                     cn : [
5253                         {
5254                             tag : 'td',
5255                             colspan :  this.cm.getColumnCount()
5256                         }
5257                     ]
5258                 }
5259             ]
5260         };
5261         
5262         return body;
5263     },
5264     
5265     renderFooter : function()
5266     {
5267         var footer = {
5268             tag: 'tfoot',
5269             cn : [
5270                 {
5271                     tag: 'tr',
5272                     cn : [
5273                         {
5274                             tag : 'td',
5275                             colspan :  this.cm.getColumnCount()
5276                         }
5277                     ]
5278                 }
5279             ]
5280         };
5281         
5282         return footer;
5283     },
5284     
5285     
5286     
5287     onLoad : function()
5288     {
5289         Roo.log('ds onload');
5290         this.clear();
5291         
5292         var _this = this;
5293         var cm = this.cm;
5294         var ds = this.store;
5295         
5296         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5297             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5298             
5299             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5300                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5301             }
5302             
5303             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5304                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5305             }
5306         });
5307         
5308         var tbody =  this.mainBody;
5309               
5310         if(ds.getCount() > 0){
5311             ds.data.each(function(d,rowIndex){
5312                 var row =  this.renderRow(cm, ds, rowIndex);
5313                 
5314                 tbody.createChild(row);
5315                 
5316                 var _this = this;
5317                 
5318                 if(row.cellObjects.length){
5319                     Roo.each(row.cellObjects, function(r){
5320                         _this.renderCellObject(r);
5321                     })
5322                 }
5323                 
5324             }, this);
5325         }
5326         
5327         Roo.each(this.el.select('tbody td', true).elements, function(e){
5328             e.on('mouseover', _this.onMouseover, _this);
5329         });
5330         
5331         Roo.each(this.el.select('tbody td', true).elements, function(e){
5332             e.on('mouseout', _this.onMouseout, _this);
5333         });
5334
5335         //if(this.loadMask){
5336         //    this.maskEl.hide();
5337         //}
5338     },
5339     
5340     
5341     onUpdate : function(ds,record)
5342     {
5343         this.refreshRow(record);
5344     },
5345     onRemove : function(ds, record, index, isUpdate){
5346         if(isUpdate !== true){
5347             this.fireEvent("beforerowremoved", this, index, record);
5348         }
5349         var bt = this.mainBody.dom;
5350         if(bt.rows[index]){
5351             bt.removeChild(bt.rows[index]);
5352         }
5353         
5354         if(isUpdate !== true){
5355             //this.stripeRows(index);
5356             //this.syncRowHeights(index, index);
5357             //this.layout();
5358             this.fireEvent("rowremoved", this, index, record);
5359         }
5360     },
5361     
5362     
5363     refreshRow : function(record){
5364         var ds = this.store, index;
5365         if(typeof record == 'number'){
5366             index = record;
5367             record = ds.getAt(index);
5368         }else{
5369             index = ds.indexOf(record);
5370         }
5371         this.insertRow(ds, index, true);
5372         this.onRemove(ds, record, index+1, true);
5373         //this.syncRowHeights(index, index);
5374         //this.layout();
5375         this.fireEvent("rowupdated", this, index, record);
5376     },
5377     
5378     insertRow : function(dm, rowIndex, isUpdate){
5379         
5380         if(!isUpdate){
5381             this.fireEvent("beforerowsinserted", this, rowIndex);
5382         }
5383             //var s = this.getScrollState();
5384         var row = this.renderRow(this.cm, this.store, rowIndex);
5385         // insert before rowIndex..
5386         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5387         
5388         var _this = this;
5389                 
5390         if(row.cellObjects.length){
5391             Roo.each(row.cellObjects, function(r){
5392                 _this.renderCellObject(r);
5393             })
5394         }
5395             
5396         if(!isUpdate){
5397             this.fireEvent("rowsinserted", this, rowIndex);
5398             //this.syncRowHeights(firstRow, lastRow);
5399             //this.stripeRows(firstRow);
5400             //this.layout();
5401         }
5402         
5403     },
5404     
5405     
5406     getRowDom : function(rowIndex)
5407     {
5408         // not sure if I need to check this.. but let's do it anyway..
5409         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5410                 this.mainBody.dom.rows[rowIndex] : false
5411     },
5412     // returns the object tree for a tr..
5413   
5414     
5415     renderRow : function(cm, ds, rowIndex) {
5416         
5417         var d = ds.getAt(rowIndex);
5418         
5419         var row = {
5420             tag : 'tr',
5421             cn : []
5422         };
5423             
5424         var cellObjects = [];
5425         
5426         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5427             var config = cm.config[i];
5428             
5429             var renderer = cm.getRenderer(i);
5430             var value = '';
5431             var id = false;
5432             
5433             if(typeof(renderer) !== 'undefined'){
5434                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5435             }
5436             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5437             // and are rendered into the cells after the row is rendered - using the id for the element.
5438             
5439             if(typeof(value) === 'object'){
5440                 id = Roo.id();
5441                 cellObjects.push({
5442                     container : id,
5443                     cfg : value 
5444                 })
5445             }
5446             
5447             var rowcfg = {
5448                 record: d,
5449                 rowIndex : rowIndex,
5450                 colIndex : i,
5451                 rowClass : ''
5452             }
5453
5454             this.fireEvent('rowclass', this, rowcfg);
5455             
5456             var td = {
5457                 tag: 'td',
5458                 cls : rowcfg.rowClass,
5459                 style: '',
5460                 html: (typeof(value) === 'object') ? '' : value
5461             };
5462             
5463             if (id) {
5464                 td.id = id;
5465             }
5466             
5467             if(typeof(config.hidden) != 'undefined' && config.hidden){
5468                 td.style += ' display:none;';
5469             }
5470             
5471             if(typeof(config.align) != 'undefined' && config.align.length){
5472                 td.style += ' text-align:' + config.align + ';';
5473             }
5474             
5475             if(typeof(config.width) != 'undefined'){
5476                 td.style += ' width:' +  config.width + 'px;';
5477             }
5478              
5479             row.cn.push(td);
5480            
5481         }
5482         
5483         row.cellObjects = cellObjects;
5484         
5485         return row;
5486           
5487     },
5488     
5489     
5490     
5491     onBeforeLoad : function()
5492     {
5493         //Roo.log('ds onBeforeLoad');
5494         
5495         //this.clear();
5496         
5497         //if(this.loadMask){
5498         //    this.maskEl.show();
5499         //}
5500     },
5501     
5502     clear : function()
5503     {
5504         this.el.select('tbody', true).first().dom.innerHTML = '';
5505     },
5506     
5507     getSelectionModel : function(){
5508         if(!this.selModel){
5509             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5510         }
5511         return this.selModel;
5512     },
5513     /*
5514      * Render the Roo.bootstrap object from renderder
5515      */
5516     renderCellObject : function(r)
5517     {
5518         var _this = this;
5519         
5520         var t = r.cfg.render(r.container);
5521         
5522         if(r.cfg.cn){
5523             Roo.each(r.cfg.cn, function(c){
5524                 var child = {
5525                     container: t.getChildContainer(),
5526                     cfg: c
5527                 }
5528                 _this.renderCellObject(child);
5529             })
5530         }
5531     }
5532    
5533 });
5534
5535  
5536
5537  /*
5538  * - LGPL
5539  *
5540  * table cell
5541  * 
5542  */
5543
5544 /**
5545  * @class Roo.bootstrap.TableCell
5546  * @extends Roo.bootstrap.Component
5547  * Bootstrap TableCell class
5548  * @cfg {String} html cell contain text
5549  * @cfg {String} cls cell class
5550  * @cfg {String} tag cell tag (td|th) default td
5551  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5552  * @cfg {String} align Aligns the content in a cell
5553  * @cfg {String} axis Categorizes cells
5554  * @cfg {String} bgcolor Specifies the background color of a cell
5555  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5556  * @cfg {Number} colspan Specifies the number of columns a cell should span
5557  * @cfg {String} headers Specifies one or more header cells a cell is related to
5558  * @cfg {Number} height Sets the height of a cell
5559  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5560  * @cfg {Number} rowspan Sets the number of rows a cell should span
5561  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5562  * @cfg {String} valign Vertical aligns the content in a cell
5563  * @cfg {Number} width Specifies the width of a cell
5564  * 
5565  * @constructor
5566  * Create a new TableCell
5567  * @param {Object} config The config object
5568  */
5569
5570 Roo.bootstrap.TableCell = function(config){
5571     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5572 };
5573
5574 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5575     
5576     html: false,
5577     cls: false,
5578     tag: false,
5579     abbr: false,
5580     align: false,
5581     axis: false,
5582     bgcolor: false,
5583     charoff: false,
5584     colspan: false,
5585     headers: false,
5586     height: false,
5587     nowrap: false,
5588     rowspan: false,
5589     scope: false,
5590     valign: false,
5591     width: false,
5592     
5593     
5594     getAutoCreate : function(){
5595         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5596         
5597         cfg = {
5598             tag: 'td'
5599         }
5600         
5601         if(this.tag){
5602             cfg.tag = this.tag;
5603         }
5604         
5605         if (this.html) {
5606             cfg.html=this.html
5607         }
5608         if (this.cls) {
5609             cfg.cls=this.cls
5610         }
5611         if (this.abbr) {
5612             cfg.abbr=this.abbr
5613         }
5614         if (this.align) {
5615             cfg.align=this.align
5616         }
5617         if (this.axis) {
5618             cfg.axis=this.axis
5619         }
5620         if (this.bgcolor) {
5621             cfg.bgcolor=this.bgcolor
5622         }
5623         if (this.charoff) {
5624             cfg.charoff=this.charoff
5625         }
5626         if (this.colspan) {
5627             cfg.colspan=this.colspan
5628         }
5629         if (this.headers) {
5630             cfg.headers=this.headers
5631         }
5632         if (this.height) {
5633             cfg.height=this.height
5634         }
5635         if (this.nowrap) {
5636             cfg.nowrap=this.nowrap
5637         }
5638         if (this.rowspan) {
5639             cfg.rowspan=this.rowspan
5640         }
5641         if (this.scope) {
5642             cfg.scope=this.scope
5643         }
5644         if (this.valign) {
5645             cfg.valign=this.valign
5646         }
5647         if (this.width) {
5648             cfg.width=this.width
5649         }
5650         
5651         
5652         return cfg;
5653     }
5654    
5655 });
5656
5657  
5658
5659  /*
5660  * - LGPL
5661  *
5662  * table row
5663  * 
5664  */
5665
5666 /**
5667  * @class Roo.bootstrap.TableRow
5668  * @extends Roo.bootstrap.Component
5669  * Bootstrap TableRow class
5670  * @cfg {String} cls row class
5671  * @cfg {String} align Aligns the content in a table row
5672  * @cfg {String} bgcolor Specifies a background color for a table row
5673  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5674  * @cfg {String} valign Vertical aligns the content in a table row
5675  * 
5676  * @constructor
5677  * Create a new TableRow
5678  * @param {Object} config The config object
5679  */
5680
5681 Roo.bootstrap.TableRow = function(config){
5682     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5683 };
5684
5685 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5686     
5687     cls: false,
5688     align: false,
5689     bgcolor: false,
5690     charoff: false,
5691     valign: false,
5692     
5693     getAutoCreate : function(){
5694         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5695         
5696         cfg = {
5697             tag: 'tr'
5698         }
5699             
5700         if(this.cls){
5701             cfg.cls = this.cls;
5702         }
5703         if(this.align){
5704             cfg.align = this.align;
5705         }
5706         if(this.bgcolor){
5707             cfg.bgcolor = this.bgcolor;
5708         }
5709         if(this.charoff){
5710             cfg.charoff = this.charoff;
5711         }
5712         if(this.valign){
5713             cfg.valign = this.valign;
5714         }
5715         
5716         return cfg;
5717     }
5718    
5719 });
5720
5721  
5722
5723  /*
5724  * - LGPL
5725  *
5726  * table body
5727  * 
5728  */
5729
5730 /**
5731  * @class Roo.bootstrap.TableBody
5732  * @extends Roo.bootstrap.Component
5733  * Bootstrap TableBody class
5734  * @cfg {String} cls element class
5735  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5736  * @cfg {String} align Aligns the content inside the element
5737  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5738  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5739  * 
5740  * @constructor
5741  * Create a new TableBody
5742  * @param {Object} config The config object
5743  */
5744
5745 Roo.bootstrap.TableBody = function(config){
5746     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5747 };
5748
5749 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5750     
5751     cls: false,
5752     tag: false,
5753     align: false,
5754     charoff: false,
5755     valign: false,
5756     
5757     getAutoCreate : function(){
5758         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5759         
5760         cfg = {
5761             tag: 'tbody'
5762         }
5763             
5764         if (this.cls) {
5765             cfg.cls=this.cls
5766         }
5767         if(this.tag){
5768             cfg.tag = this.tag;
5769         }
5770         
5771         if(this.align){
5772             cfg.align = this.align;
5773         }
5774         if(this.charoff){
5775             cfg.charoff = this.charoff;
5776         }
5777         if(this.valign){
5778             cfg.valign = this.valign;
5779         }
5780         
5781         return cfg;
5782     }
5783     
5784     
5785 //    initEvents : function()
5786 //    {
5787 //        
5788 //        if(!this.store){
5789 //            return;
5790 //        }
5791 //        
5792 //        this.store = Roo.factory(this.store, Roo.data);
5793 //        this.store.on('load', this.onLoad, this);
5794 //        
5795 //        this.store.load();
5796 //        
5797 //    },
5798 //    
5799 //    onLoad: function () 
5800 //    {   
5801 //        this.fireEvent('load', this);
5802 //    }
5803 //    
5804 //   
5805 });
5806
5807  
5808
5809  /*
5810  * Based on:
5811  * Ext JS Library 1.1.1
5812  * Copyright(c) 2006-2007, Ext JS, LLC.
5813  *
5814  * Originally Released Under LGPL - original licence link has changed is not relivant.
5815  *
5816  * Fork - LGPL
5817  * <script type="text/javascript">
5818  */
5819
5820 // as we use this in bootstrap.
5821 Roo.namespace('Roo.form');
5822  /**
5823  * @class Roo.form.Action
5824  * Internal Class used to handle form actions
5825  * @constructor
5826  * @param {Roo.form.BasicForm} el The form element or its id
5827  * @param {Object} config Configuration options
5828  */
5829
5830  
5831  
5832 // define the action interface
5833 Roo.form.Action = function(form, options){
5834     this.form = form;
5835     this.options = options || {};
5836 };
5837 /**
5838  * Client Validation Failed
5839  * @const 
5840  */
5841 Roo.form.Action.CLIENT_INVALID = 'client';
5842 /**
5843  * Server Validation Failed
5844  * @const 
5845  */
5846 Roo.form.Action.SERVER_INVALID = 'server';
5847  /**
5848  * Connect to Server Failed
5849  * @const 
5850  */
5851 Roo.form.Action.CONNECT_FAILURE = 'connect';
5852 /**
5853  * Reading Data from Server Failed
5854  * @const 
5855  */
5856 Roo.form.Action.LOAD_FAILURE = 'load';
5857
5858 Roo.form.Action.prototype = {
5859     type : 'default',
5860     failureType : undefined,
5861     response : undefined,
5862     result : undefined,
5863
5864     // interface method
5865     run : function(options){
5866
5867     },
5868
5869     // interface method
5870     success : function(response){
5871
5872     },
5873
5874     // interface method
5875     handleResponse : function(response){
5876
5877     },
5878
5879     // default connection failure
5880     failure : function(response){
5881         
5882         this.response = response;
5883         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5884         this.form.afterAction(this, false);
5885     },
5886
5887     processResponse : function(response){
5888         this.response = response;
5889         if(!response.responseText){
5890             return true;
5891         }
5892         this.result = this.handleResponse(response);
5893         return this.result;
5894     },
5895
5896     // utility functions used internally
5897     getUrl : function(appendParams){
5898         var url = this.options.url || this.form.url || this.form.el.dom.action;
5899         if(appendParams){
5900             var p = this.getParams();
5901             if(p){
5902                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5903             }
5904         }
5905         return url;
5906     },
5907
5908     getMethod : function(){
5909         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5910     },
5911
5912     getParams : function(){
5913         var bp = this.form.baseParams;
5914         var p = this.options.params;
5915         if(p){
5916             if(typeof p == "object"){
5917                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5918             }else if(typeof p == 'string' && bp){
5919                 p += '&' + Roo.urlEncode(bp);
5920             }
5921         }else if(bp){
5922             p = Roo.urlEncode(bp);
5923         }
5924         return p;
5925     },
5926
5927     createCallback : function(){
5928         return {
5929             success: this.success,
5930             failure: this.failure,
5931             scope: this,
5932             timeout: (this.form.timeout*1000),
5933             upload: this.form.fileUpload ? this.success : undefined
5934         };
5935     }
5936 };
5937
5938 Roo.form.Action.Submit = function(form, options){
5939     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5940 };
5941
5942 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5943     type : 'submit',
5944
5945     haveProgress : false,
5946     uploadComplete : false,
5947     
5948     // uploadProgress indicator.
5949     uploadProgress : function()
5950     {
5951         if (!this.form.progressUrl) {
5952             return;
5953         }
5954         
5955         if (!this.haveProgress) {
5956             Roo.MessageBox.progress("Uploading", "Uploading");
5957         }
5958         if (this.uploadComplete) {
5959            Roo.MessageBox.hide();
5960            return;
5961         }
5962         
5963         this.haveProgress = true;
5964    
5965         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5966         
5967         var c = new Roo.data.Connection();
5968         c.request({
5969             url : this.form.progressUrl,
5970             params: {
5971                 id : uid
5972             },
5973             method: 'GET',
5974             success : function(req){
5975                //console.log(data);
5976                 var rdata = false;
5977                 var edata;
5978                 try  {
5979                    rdata = Roo.decode(req.responseText)
5980                 } catch (e) {
5981                     Roo.log("Invalid data from server..");
5982                     Roo.log(edata);
5983                     return;
5984                 }
5985                 if (!rdata || !rdata.success) {
5986                     Roo.log(rdata);
5987                     Roo.MessageBox.alert(Roo.encode(rdata));
5988                     return;
5989                 }
5990                 var data = rdata.data;
5991                 
5992                 if (this.uploadComplete) {
5993                    Roo.MessageBox.hide();
5994                    return;
5995                 }
5996                    
5997                 if (data){
5998                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5999                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6000                     );
6001                 }
6002                 this.uploadProgress.defer(2000,this);
6003             },
6004        
6005             failure: function(data) {
6006                 Roo.log('progress url failed ');
6007                 Roo.log(data);
6008             },
6009             scope : this
6010         });
6011            
6012     },
6013     
6014     
6015     run : function()
6016     {
6017         // run get Values on the form, so it syncs any secondary forms.
6018         this.form.getValues();
6019         
6020         var o = this.options;
6021         var method = this.getMethod();
6022         var isPost = method == 'POST';
6023         if(o.clientValidation === false || this.form.isValid()){
6024             
6025             if (this.form.progressUrl) {
6026                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6027                     (new Date() * 1) + '' + Math.random());
6028                     
6029             } 
6030             
6031             
6032             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6033                 form:this.form.el.dom,
6034                 url:this.getUrl(!isPost),
6035                 method: method,
6036                 params:isPost ? this.getParams() : null,
6037                 isUpload: this.form.fileUpload
6038             }));
6039             
6040             this.uploadProgress();
6041
6042         }else if (o.clientValidation !== false){ // client validation failed
6043             this.failureType = Roo.form.Action.CLIENT_INVALID;
6044             this.form.afterAction(this, false);
6045         }
6046     },
6047
6048     success : function(response)
6049     {
6050         this.uploadComplete= true;
6051         if (this.haveProgress) {
6052             Roo.MessageBox.hide();
6053         }
6054         
6055         
6056         var result = this.processResponse(response);
6057         if(result === true || result.success){
6058             this.form.afterAction(this, true);
6059             return;
6060         }
6061         if(result.errors){
6062             this.form.markInvalid(result.errors);
6063             this.failureType = Roo.form.Action.SERVER_INVALID;
6064         }
6065         this.form.afterAction(this, false);
6066     },
6067     failure : function(response)
6068     {
6069         this.uploadComplete= true;
6070         if (this.haveProgress) {
6071             Roo.MessageBox.hide();
6072         }
6073         
6074         this.response = response;
6075         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6076         this.form.afterAction(this, false);
6077     },
6078     
6079     handleResponse : function(response){
6080         if(this.form.errorReader){
6081             var rs = this.form.errorReader.read(response);
6082             var errors = [];
6083             if(rs.records){
6084                 for(var i = 0, len = rs.records.length; i < len; i++) {
6085                     var r = rs.records[i];
6086                     errors[i] = r.data;
6087                 }
6088             }
6089             if(errors.length < 1){
6090                 errors = null;
6091             }
6092             return {
6093                 success : rs.success,
6094                 errors : errors
6095             };
6096         }
6097         var ret = false;
6098         try {
6099             ret = Roo.decode(response.responseText);
6100         } catch (e) {
6101             ret = {
6102                 success: false,
6103                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6104                 errors : []
6105             };
6106         }
6107         return ret;
6108         
6109     }
6110 });
6111
6112
6113 Roo.form.Action.Load = function(form, options){
6114     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6115     this.reader = this.form.reader;
6116 };
6117
6118 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6119     type : 'load',
6120
6121     run : function(){
6122         
6123         Roo.Ajax.request(Roo.apply(
6124                 this.createCallback(), {
6125                     method:this.getMethod(),
6126                     url:this.getUrl(false),
6127                     params:this.getParams()
6128         }));
6129     },
6130
6131     success : function(response){
6132         
6133         var result = this.processResponse(response);
6134         if(result === true || !result.success || !result.data){
6135             this.failureType = Roo.form.Action.LOAD_FAILURE;
6136             this.form.afterAction(this, false);
6137             return;
6138         }
6139         this.form.clearInvalid();
6140         this.form.setValues(result.data);
6141         this.form.afterAction(this, true);
6142     },
6143
6144     handleResponse : function(response){
6145         if(this.form.reader){
6146             var rs = this.form.reader.read(response);
6147             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6148             return {
6149                 success : rs.success,
6150                 data : data
6151             };
6152         }
6153         return Roo.decode(response.responseText);
6154     }
6155 });
6156
6157 Roo.form.Action.ACTION_TYPES = {
6158     'load' : Roo.form.Action.Load,
6159     'submit' : Roo.form.Action.Submit
6160 };/*
6161  * - LGPL
6162  *
6163  * form
6164  * 
6165  */
6166
6167 /**
6168  * @class Roo.bootstrap.Form
6169  * @extends Roo.bootstrap.Component
6170  * Bootstrap Form class
6171  * @cfg {String} method  GET | POST (default POST)
6172  * @cfg {String} labelAlign top | left (default top)
6173  * @cfg {String} align left  | right - for navbars
6174  * @cfg {Boolean} loadMask load mask when submit (default true)
6175
6176  * 
6177  * @constructor
6178  * Create a new Form
6179  * @param {Object} config The config object
6180  */
6181
6182
6183 Roo.bootstrap.Form = function(config){
6184     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6185     this.addEvents({
6186         /**
6187          * @event clientvalidation
6188          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6189          * @param {Form} this
6190          * @param {Boolean} valid true if the form has passed client-side validation
6191          */
6192         clientvalidation: true,
6193         /**
6194          * @event beforeaction
6195          * Fires before any action is performed. Return false to cancel the action.
6196          * @param {Form} this
6197          * @param {Action} action The action to be performed
6198          */
6199         beforeaction: true,
6200         /**
6201          * @event actionfailed
6202          * Fires when an action fails.
6203          * @param {Form} this
6204          * @param {Action} action The action that failed
6205          */
6206         actionfailed : true,
6207         /**
6208          * @event actioncomplete
6209          * Fires when an action is completed.
6210          * @param {Form} this
6211          * @param {Action} action The action that completed
6212          */
6213         actioncomplete : true
6214     });
6215     
6216 };
6217
6218 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6219       
6220      /**
6221      * @cfg {String} method
6222      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6223      */
6224     method : 'POST',
6225     /**
6226      * @cfg {String} url
6227      * The URL to use for form actions if one isn't supplied in the action options.
6228      */
6229     /**
6230      * @cfg {Boolean} fileUpload
6231      * Set to true if this form is a file upload.
6232      */
6233      
6234     /**
6235      * @cfg {Object} baseParams
6236      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6237      */
6238       
6239     /**
6240      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6241      */
6242     timeout: 30,
6243     /**
6244      * @cfg {Sting} align (left|right) for navbar forms
6245      */
6246     align : 'left',
6247
6248     // private
6249     activeAction : null,
6250  
6251     /**
6252      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6253      * element by passing it or its id or mask the form itself by passing in true.
6254      * @type Mixed
6255      */
6256     waitMsgTarget : false,
6257     
6258     loadMask : true,
6259     
6260     getAutoCreate : function(){
6261         
6262         var cfg = {
6263             tag: 'form',
6264             method : this.method || 'POST',
6265             id : this.id || Roo.id(),
6266             cls : ''
6267         }
6268         if (this.parent().xtype.match(/^Nav/)) {
6269             cfg.cls = 'navbar-form navbar-' + this.align;
6270             
6271         }
6272         
6273         if (this.labelAlign == 'left' ) {
6274             cfg.cls += ' form-horizontal';
6275         }
6276         
6277         
6278         return cfg;
6279     },
6280     initEvents : function()
6281     {
6282         this.el.on('submit', this.onSubmit, this);
6283         // this was added as random key presses on the form where triggering form submit.
6284         this.el.on('keypress', function(e) {
6285             if (e.getCharCode() != 13) {
6286                 return true;
6287             }
6288             // we might need to allow it for textareas.. and some other items.
6289             // check e.getTarget().
6290             
6291             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6292                 return true;
6293             }
6294         
6295             Roo.log("keypress blocked");
6296             
6297             e.preventDefault();
6298             return false;
6299         });
6300         
6301     },
6302     // private
6303     onSubmit : function(e){
6304         e.stopEvent();
6305     },
6306     
6307      /**
6308      * Returns true if client-side validation on the form is successful.
6309      * @return Boolean
6310      */
6311     isValid : function(){
6312         var items = this.getItems();
6313         var valid = true;
6314         items.each(function(f){
6315            if(!f.validate()){
6316                valid = false;
6317                
6318            }
6319         });
6320         return valid;
6321     },
6322     /**
6323      * Returns true if any fields in this form have changed since their original load.
6324      * @return Boolean
6325      */
6326     isDirty : function(){
6327         var dirty = false;
6328         var items = this.getItems();
6329         items.each(function(f){
6330            if(f.isDirty()){
6331                dirty = true;
6332                return false;
6333            }
6334            return true;
6335         });
6336         return dirty;
6337     },
6338      /**
6339      * Performs a predefined action (submit or load) or custom actions you define on this form.
6340      * @param {String} actionName The name of the action type
6341      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6342      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6343      * accept other config options):
6344      * <pre>
6345 Property          Type             Description
6346 ----------------  ---------------  ----------------------------------------------------------------------------------
6347 url               String           The url for the action (defaults to the form's url)
6348 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6349 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6350 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6351                                    validate the form on the client (defaults to false)
6352      * </pre>
6353      * @return {BasicForm} this
6354      */
6355     doAction : function(action, options){
6356         if(typeof action == 'string'){
6357             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6358         }
6359         if(this.fireEvent('beforeaction', this, action) !== false){
6360             this.beforeAction(action);
6361             action.run.defer(100, action);
6362         }
6363         return this;
6364     },
6365     
6366     // private
6367     beforeAction : function(action){
6368         var o = action.options;
6369         
6370         if(this.loadMask){
6371             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6372         }
6373         // not really supported yet.. ??
6374         
6375         //if(this.waitMsgTarget === true){
6376         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6377         //}else if(this.waitMsgTarget){
6378         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6379         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6380         //}else {
6381         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6382        // }
6383          
6384     },
6385
6386     // private
6387     afterAction : function(action, success){
6388         this.activeAction = null;
6389         var o = action.options;
6390         
6391         //if(this.waitMsgTarget === true){
6392             this.el.unmask();
6393         //}else if(this.waitMsgTarget){
6394         //    this.waitMsgTarget.unmask();
6395         //}else{
6396         //    Roo.MessageBox.updateProgress(1);
6397         //    Roo.MessageBox.hide();
6398        // }
6399         // 
6400         if(success){
6401             if(o.reset){
6402                 this.reset();
6403             }
6404             Roo.callback(o.success, o.scope, [this, action]);
6405             this.fireEvent('actioncomplete', this, action);
6406             
6407         }else{
6408             
6409             // failure condition..
6410             // we have a scenario where updates need confirming.
6411             // eg. if a locking scenario exists..
6412             // we look for { errors : { needs_confirm : true }} in the response.
6413             if (
6414                 (typeof(action.result) != 'undefined')  &&
6415                 (typeof(action.result.errors) != 'undefined')  &&
6416                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6417            ){
6418                 var _t = this;
6419                 Roo.log("not supported yet");
6420                  /*
6421                 
6422                 Roo.MessageBox.confirm(
6423                     "Change requires confirmation",
6424                     action.result.errorMsg,
6425                     function(r) {
6426                         if (r != 'yes') {
6427                             return;
6428                         }
6429                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6430                     }
6431                     
6432                 );
6433                 */
6434                 
6435                 
6436                 return;
6437             }
6438             
6439             Roo.callback(o.failure, o.scope, [this, action]);
6440             // show an error message if no failed handler is set..
6441             if (!this.hasListener('actionfailed')) {
6442                 Roo.log("need to add dialog support");
6443                 /*
6444                 Roo.MessageBox.alert("Error",
6445                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6446                         action.result.errorMsg :
6447                         "Saving Failed, please check your entries or try again"
6448                 );
6449                 */
6450             }
6451             
6452             this.fireEvent('actionfailed', this, action);
6453         }
6454         
6455     },
6456     /**
6457      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6458      * @param {String} id The value to search for
6459      * @return Field
6460      */
6461     findField : function(id){
6462         var items = this.getItems();
6463         var field = items.get(id);
6464         if(!field){
6465              items.each(function(f){
6466                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6467                     field = f;
6468                     return false;
6469                 }
6470                 return true;
6471             });
6472         }
6473         return field || null;
6474     },
6475      /**
6476      * Mark fields in this form invalid in bulk.
6477      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6478      * @return {BasicForm} this
6479      */
6480     markInvalid : function(errors){
6481         if(errors instanceof Array){
6482             for(var i = 0, len = errors.length; i < len; i++){
6483                 var fieldError = errors[i];
6484                 var f = this.findField(fieldError.id);
6485                 if(f){
6486                     f.markInvalid(fieldError.msg);
6487                 }
6488             }
6489         }else{
6490             var field, id;
6491             for(id in errors){
6492                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6493                     field.markInvalid(errors[id]);
6494                 }
6495             }
6496         }
6497         //Roo.each(this.childForms || [], function (f) {
6498         //    f.markInvalid(errors);
6499         //});
6500         
6501         return this;
6502     },
6503
6504     /**
6505      * Set values for fields in this form in bulk.
6506      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6507      * @return {BasicForm} this
6508      */
6509     setValues : function(values){
6510         if(values instanceof Array){ // array of objects
6511             for(var i = 0, len = values.length; i < len; i++){
6512                 var v = values[i];
6513                 var f = this.findField(v.id);
6514                 if(f){
6515                     f.setValue(v.value);
6516                     if(this.trackResetOnLoad){
6517                         f.originalValue = f.getValue();
6518                     }
6519                 }
6520             }
6521         }else{ // object hash
6522             var field, id;
6523             for(id in values){
6524                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6525                     
6526                     if (field.setFromData && 
6527                         field.valueField && 
6528                         field.displayField &&
6529                         // combos' with local stores can 
6530                         // be queried via setValue()
6531                         // to set their value..
6532                         (field.store && !field.store.isLocal)
6533                         ) {
6534                         // it's a combo
6535                         var sd = { };
6536                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6537                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6538                         field.setFromData(sd);
6539                         
6540                     } else {
6541                         field.setValue(values[id]);
6542                     }
6543                     
6544                     
6545                     if(this.trackResetOnLoad){
6546                         field.originalValue = field.getValue();
6547                     }
6548                 }
6549             }
6550         }
6551          
6552         //Roo.each(this.childForms || [], function (f) {
6553         //    f.setValues(values);
6554         //});
6555                 
6556         return this;
6557     },
6558
6559     /**
6560      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6561      * they are returned as an array.
6562      * @param {Boolean} asString
6563      * @return {Object}
6564      */
6565     getValues : function(asString){
6566         //if (this.childForms) {
6567             // copy values from the child forms
6568         //    Roo.each(this.childForms, function (f) {
6569         //        this.setValues(f.getValues());
6570         //    }, this);
6571         //}
6572         
6573         
6574         
6575         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6576         if(asString === true){
6577             return fs;
6578         }
6579         return Roo.urlDecode(fs);
6580     },
6581     
6582     /**
6583      * Returns the fields in this form as an object with key/value pairs. 
6584      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6585      * @return {Object}
6586      */
6587     getFieldValues : function(with_hidden)
6588     {
6589         var items = this.getItems();
6590         var ret = {};
6591         items.each(function(f){
6592             if (!f.getName()) {
6593                 return;
6594             }
6595             var v = f.getValue();
6596             if (f.inputType =='radio') {
6597                 if (typeof(ret[f.getName()]) == 'undefined') {
6598                     ret[f.getName()] = ''; // empty..
6599                 }
6600                 
6601                 if (!f.el.dom.checked) {
6602                     return;
6603                     
6604                 }
6605                 v = f.el.dom.value;
6606                 
6607             }
6608             
6609             // not sure if this supported any more..
6610             if ((typeof(v) == 'object') && f.getRawValue) {
6611                 v = f.getRawValue() ; // dates..
6612             }
6613             // combo boxes where name != hiddenName...
6614             if (f.name != f.getName()) {
6615                 ret[f.name] = f.getRawValue();
6616             }
6617             ret[f.getName()] = v;
6618         });
6619         
6620         return ret;
6621     },
6622
6623     /**
6624      * Clears all invalid messages in this form.
6625      * @return {BasicForm} this
6626      */
6627     clearInvalid : function(){
6628         var items = this.getItems();
6629         
6630         items.each(function(f){
6631            f.clearInvalid();
6632         });
6633         
6634         
6635         
6636         return this;
6637     },
6638
6639     /**
6640      * Resets this form.
6641      * @return {BasicForm} this
6642      */
6643     reset : function(){
6644         var items = this.getItems();
6645         items.each(function(f){
6646             f.reset();
6647         });
6648         
6649         Roo.each(this.childForms || [], function (f) {
6650             f.reset();
6651         });
6652        
6653         
6654         return this;
6655     },
6656     getItems : function()
6657     {
6658         var r=new Roo.util.MixedCollection(false, function(o){
6659             return o.id || (o.id = Roo.id());
6660         });
6661         var iter = function(el) {
6662             if (el.inputEl) {
6663                 r.add(el);
6664             }
6665             if (!el.items) {
6666                 return;
6667             }
6668             Roo.each(el.items,function(e) {
6669                 iter(e);
6670             });
6671             
6672             
6673         };
6674         iter(this);
6675         return r;
6676         
6677         
6678         
6679         
6680     }
6681     
6682 });
6683
6684  
6685 /*
6686  * Based on:
6687  * Ext JS Library 1.1.1
6688  * Copyright(c) 2006-2007, Ext JS, LLC.
6689  *
6690  * Originally Released Under LGPL - original licence link has changed is not relivant.
6691  *
6692  * Fork - LGPL
6693  * <script type="text/javascript">
6694  */
6695 /**
6696  * @class Roo.form.VTypes
6697  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6698  * @singleton
6699  */
6700 Roo.form.VTypes = function(){
6701     // closure these in so they are only created once.
6702     var alpha = /^[a-zA-Z_]+$/;
6703     var alphanum = /^[a-zA-Z0-9_]+$/;
6704     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6705     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6706
6707     // All these messages and functions are configurable
6708     return {
6709         /**
6710          * The function used to validate email addresses
6711          * @param {String} value The email address
6712          */
6713         'email' : function(v){
6714             return email.test(v);
6715         },
6716         /**
6717          * The error text to display when the email validation function returns false
6718          * @type String
6719          */
6720         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6721         /**
6722          * The keystroke filter mask to be applied on email input
6723          * @type RegExp
6724          */
6725         'emailMask' : /[a-z0-9_\.\-@]/i,
6726
6727         /**
6728          * The function used to validate URLs
6729          * @param {String} value The URL
6730          */
6731         'url' : function(v){
6732             return url.test(v);
6733         },
6734         /**
6735          * The error text to display when the url validation function returns false
6736          * @type String
6737          */
6738         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6739         
6740         /**
6741          * The function used to validate alpha values
6742          * @param {String} value The value
6743          */
6744         'alpha' : function(v){
6745             return alpha.test(v);
6746         },
6747         /**
6748          * The error text to display when the alpha validation function returns false
6749          * @type String
6750          */
6751         'alphaText' : 'This field should only contain letters and _',
6752         /**
6753          * The keystroke filter mask to be applied on alpha input
6754          * @type RegExp
6755          */
6756         'alphaMask' : /[a-z_]/i,
6757
6758         /**
6759          * The function used to validate alphanumeric values
6760          * @param {String} value The value
6761          */
6762         'alphanum' : function(v){
6763             return alphanum.test(v);
6764         },
6765         /**
6766          * The error text to display when the alphanumeric validation function returns false
6767          * @type String
6768          */
6769         'alphanumText' : 'This field should only contain letters, numbers and _',
6770         /**
6771          * The keystroke filter mask to be applied on alphanumeric input
6772          * @type RegExp
6773          */
6774         'alphanumMask' : /[a-z0-9_]/i
6775     };
6776 }();/*
6777  * - LGPL
6778  *
6779  * Input
6780  * 
6781  */
6782
6783 /**
6784  * @class Roo.bootstrap.Input
6785  * @extends Roo.bootstrap.Component
6786  * Bootstrap Input class
6787  * @cfg {Boolean} disabled is it disabled
6788  * @cfg {String} fieldLabel - the label associated
6789  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6790  * @cfg {String} name name of the input
6791  * @cfg {string} fieldLabel - the label associated
6792  * @cfg {string}  inputType - input / file submit ...
6793  * @cfg {string} placeholder - placeholder to put in text.
6794  * @cfg {string}  before - input group add on before
6795  * @cfg {string} after - input group add on after
6796  * @cfg {string} size - (lg|sm) or leave empty..
6797  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6798  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6799  * @cfg {Number} md colspan out of 12 for computer-sized screens
6800  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6801  * @cfg {string} value default value of the input
6802  * @cfg {Number} labelWidth set the width of label (0-12)
6803  * @cfg {String} labelAlign (top|left)
6804  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6805  * @cfg {String} align (left|center|right) Default left
6806  * 
6807  * 
6808  * @constructor
6809  * Create a new Input
6810  * @param {Object} config The config object
6811  */
6812
6813 Roo.bootstrap.Input = function(config){
6814     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6815    
6816         this.addEvents({
6817             /**
6818              * @event focus
6819              * Fires when this field receives input focus.
6820              * @param {Roo.form.Field} this
6821              */
6822             focus : true,
6823             /**
6824              * @event blur
6825              * Fires when this field loses input focus.
6826              * @param {Roo.form.Field} this
6827              */
6828             blur : true,
6829             /**
6830              * @event specialkey
6831              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6832              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6833              * @param {Roo.form.Field} this
6834              * @param {Roo.EventObject} e The event object
6835              */
6836             specialkey : true,
6837             /**
6838              * @event change
6839              * Fires just before the field blurs if the field value has changed.
6840              * @param {Roo.form.Field} this
6841              * @param {Mixed} newValue The new value
6842              * @param {Mixed} oldValue The original value
6843              */
6844             change : true,
6845             /**
6846              * @event invalid
6847              * Fires after the field has been marked as invalid.
6848              * @param {Roo.form.Field} this
6849              * @param {String} msg The validation message
6850              */
6851             invalid : true,
6852             /**
6853              * @event valid
6854              * Fires after the field has been validated with no errors.
6855              * @param {Roo.form.Field} this
6856              */
6857             valid : true,
6858              /**
6859              * @event keyup
6860              * Fires after the key up
6861              * @param {Roo.form.Field} this
6862              * @param {Roo.EventObject}  e The event Object
6863              */
6864             keyup : true
6865         });
6866 };
6867
6868 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6869      /**
6870      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6871       automatic validation (defaults to "keyup").
6872      */
6873     validationEvent : "keyup",
6874      /**
6875      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6876      */
6877     validateOnBlur : true,
6878     /**
6879      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6880      */
6881     validationDelay : 250,
6882      /**
6883      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6884      */
6885     focusClass : "x-form-focus",  // not needed???
6886     
6887        
6888     /**
6889      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6890      */
6891     invalidClass : "has-error",
6892     
6893     /**
6894      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6895      */
6896     selectOnFocus : false,
6897     
6898      /**
6899      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6900      */
6901     maskRe : null,
6902        /**
6903      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6904      */
6905     vtype : null,
6906     
6907       /**
6908      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6909      */
6910     disableKeyFilter : false,
6911     
6912        /**
6913      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6914      */
6915     disabled : false,
6916      /**
6917      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6918      */
6919     allowBlank : true,
6920     /**
6921      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6922      */
6923     blankText : "This field is required",
6924     
6925      /**
6926      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6927      */
6928     minLength : 0,
6929     /**
6930      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6931      */
6932     maxLength : Number.MAX_VALUE,
6933     /**
6934      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6935      */
6936     minLengthText : "The minimum length for this field is {0}",
6937     /**
6938      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6939      */
6940     maxLengthText : "The maximum length for this field is {0}",
6941   
6942     
6943     /**
6944      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6945      * If available, this function will be called only after the basic validators all return true, and will be passed the
6946      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6947      */
6948     validator : null,
6949     /**
6950      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6951      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6952      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6953      */
6954     regex : null,
6955     /**
6956      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6957      */
6958     regexText : "",
6959     
6960     
6961     
6962     fieldLabel : '',
6963     inputType : 'text',
6964     
6965     name : false,
6966     placeholder: false,
6967     before : false,
6968     after : false,
6969     size : false,
6970     // private
6971     hasFocus : false,
6972     preventMark: false,
6973     isFormField : true,
6974     value : '',
6975     labelWidth : 2,
6976     labelAlign : false,
6977     readOnly : false,
6978     align : false,
6979     formatedValue : false,
6980     
6981     parentLabelAlign : function()
6982     {
6983         var parent = this;
6984         while (parent.parent()) {
6985             parent = parent.parent();
6986             if (typeof(parent.labelAlign) !='undefined') {
6987                 return parent.labelAlign;
6988             }
6989         }
6990         return 'left';
6991         
6992     },
6993     
6994     getAutoCreate : function(){
6995         
6996         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6997         
6998         var id = Roo.id();
6999         
7000         var cfg = {};
7001         
7002         if(this.inputType != 'hidden'){
7003             cfg.cls = 'form-group' //input-group
7004         }
7005         
7006         var input =  {
7007             tag: 'input',
7008             id : id,
7009             type : this.inputType,
7010             value : this.value,
7011             cls : 'form-control',
7012             placeholder : this.placeholder || ''
7013             
7014         };
7015         
7016         if(this.align){
7017             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7018         }
7019         
7020         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7021             input.maxLength = this.maxLength;
7022         }
7023         
7024         if (this.disabled) {
7025             input.disabled=true;
7026         }
7027         
7028         if (this.readOnly) {
7029             input.readonly=true;
7030         }
7031         
7032         if (this.name) {
7033             input.name = this.name;
7034         }
7035         if (this.size) {
7036             input.cls += ' input-' + this.size;
7037         }
7038         var settings=this;
7039         ['xs','sm','md','lg'].map(function(size){
7040             if (settings[size]) {
7041                 cfg.cls += ' col-' + size + '-' + settings[size];
7042             }
7043         });
7044         
7045         var inputblock = input;
7046         
7047         if (this.before || this.after) {
7048             
7049             inputblock = {
7050                 cls : 'input-group',
7051                 cn :  [] 
7052             };
7053             if (this.before && typeof(this.before) == 'string') {
7054                 
7055                 inputblock.cn.push({
7056                     tag :'span',
7057                     cls : 'roo-input-before input-group-addon',
7058                     html : this.before
7059                 });
7060             }
7061             if (this.before && typeof(this.before) == 'object') {
7062                 this.before = Roo.factory(this.before);
7063                 Roo.log(this.before);
7064                 inputblock.cn.push({
7065                     tag :'span',
7066                     cls : 'roo-input-before input-group-' +
7067                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7068                 });
7069             }
7070             
7071             inputblock.cn.push(input);
7072             
7073             if (this.after && typeof(this.after) == 'string') {
7074                 inputblock.cn.push({
7075                     tag :'span',
7076                     cls : 'roo-input-after input-group-addon',
7077                     html : this.after
7078                 });
7079             }
7080             if (this.after && typeof(this.after) == 'object') {
7081                 this.after = Roo.factory(this.after);
7082                 Roo.log(this.after);
7083                 inputblock.cn.push({
7084                     tag :'span',
7085                     cls : 'roo-input-after input-group-' +
7086                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7087                 });
7088             }
7089         };
7090         
7091         if (align ==='left' && this.fieldLabel.length) {
7092                 Roo.log("left and has label");
7093                 cfg.cn = [
7094                     
7095                     {
7096                         tag: 'label',
7097                         'for' :  id,
7098                         cls : 'control-label col-sm-' + this.labelWidth,
7099                         html : this.fieldLabel
7100                         
7101                     },
7102                     {
7103                         cls : "col-sm-" + (12 - this.labelWidth), 
7104                         cn: [
7105                             inputblock
7106                         ]
7107                     }
7108                     
7109                 ];
7110         } else if ( this.fieldLabel.length) {
7111                 Roo.log(" label");
7112                  cfg.cn = [
7113                    
7114                     {
7115                         tag: 'label',
7116                         //cls : 'input-group-addon',
7117                         html : this.fieldLabel
7118                         
7119                     },
7120                     
7121                     inputblock
7122                     
7123                 ];
7124
7125         } else {
7126             
7127                 Roo.log(" no label && no align");
7128                 cfg.cn = [
7129                     
7130                         inputblock
7131                     
7132                 ];
7133                 
7134                 
7135         };
7136         Roo.log('input-parentType: ' + this.parentType);
7137         
7138         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7139            cfg.cls += ' navbar-form';
7140            Roo.log(cfg);
7141         }
7142         
7143         return cfg;
7144         
7145     },
7146     /**
7147      * return the real input element.
7148      */
7149     inputEl: function ()
7150     {
7151         return this.el.select('input.form-control',true).first();
7152     },
7153     setDisabled : function(v)
7154     {
7155         var i  = this.inputEl().dom;
7156         if (!v) {
7157             i.removeAttribute('disabled');
7158             return;
7159             
7160         }
7161         i.setAttribute('disabled','true');
7162     },
7163     initEvents : function()
7164     {
7165         
7166         this.inputEl().on("keydown" , this.fireKey,  this);
7167         this.inputEl().on("focus", this.onFocus,  this);
7168         this.inputEl().on("blur", this.onBlur,  this);
7169         
7170         this.inputEl().relayEvent('keyup', this);
7171
7172         // reference to original value for reset
7173         this.originalValue = this.getValue();
7174         //Roo.form.TextField.superclass.initEvents.call(this);
7175         if(this.validationEvent == 'keyup'){
7176             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7177             this.inputEl().on('keyup', this.filterValidation, this);
7178         }
7179         else if(this.validationEvent !== false){
7180             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7181         }
7182         
7183         if(this.selectOnFocus){
7184             this.on("focus", this.preFocus, this);
7185             
7186         }
7187         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7188             this.inputEl().on("keypress", this.filterKeys, this);
7189         }
7190        /* if(this.grow){
7191             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7192             this.el.on("click", this.autoSize,  this);
7193         }
7194         */
7195         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7196             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7197         }
7198         
7199         if (typeof(this.before) == 'object') {
7200             this.before.render(this.el.select('.roo-input-before',true).first());
7201         }
7202         if (typeof(this.after) == 'object') {
7203             this.after.render(this.el.select('.roo-input-after',true).first());
7204         }
7205         
7206         
7207     },
7208     filterValidation : function(e){
7209         if(!e.isNavKeyPress()){
7210             this.validationTask.delay(this.validationDelay);
7211         }
7212     },
7213      /**
7214      * Validates the field value
7215      * @return {Boolean} True if the value is valid, else false
7216      */
7217     validate : function(){
7218         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7219         if(this.disabled || this.validateValue(this.getRawValue())){
7220             this.clearInvalid();
7221             return true;
7222         }
7223         return false;
7224     },
7225     
7226     
7227     /**
7228      * Validates a value according to the field's validation rules and marks the field as invalid
7229      * if the validation fails
7230      * @param {Mixed} value The value to validate
7231      * @return {Boolean} True if the value is valid, else false
7232      */
7233     validateValue : function(value){
7234         if(value.length < 1)  { // if it's blank
7235              if(this.allowBlank){
7236                 this.clearInvalid();
7237                 return true;
7238              }else{
7239                 this.markInvalid(this.blankText);
7240                 return false;
7241              }
7242         }
7243         if(value.length < this.minLength){
7244             this.markInvalid(String.format(this.minLengthText, this.minLength));
7245             return false;
7246         }
7247         if(value.length > this.maxLength){
7248             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7249             return false;
7250         }
7251         if(this.vtype){
7252             var vt = Roo.form.VTypes;
7253             if(!vt[this.vtype](value, this)){
7254                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7255                 return false;
7256             }
7257         }
7258         if(typeof this.validator == "function"){
7259             var msg = this.validator(value);
7260             if(msg !== true){
7261                 this.markInvalid(msg);
7262                 return false;
7263             }
7264         }
7265         if(this.regex && !this.regex.test(value)){
7266             this.markInvalid(this.regexText);
7267             return false;
7268         }
7269         return true;
7270     },
7271
7272     
7273     
7274      // private
7275     fireKey : function(e){
7276         //Roo.log('field ' + e.getKey());
7277         if(e.isNavKeyPress()){
7278             this.fireEvent("specialkey", this, e);
7279         }
7280     },
7281     focus : function (selectText){
7282         if(this.rendered){
7283             this.inputEl().focus();
7284             if(selectText === true){
7285                 this.inputEl().dom.select();
7286             }
7287         }
7288         return this;
7289     } ,
7290     
7291     onFocus : function(){
7292         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7293            // this.el.addClass(this.focusClass);
7294         }
7295         if(!this.hasFocus){
7296             this.hasFocus = true;
7297             this.startValue = this.getValue();
7298             this.fireEvent("focus", this);
7299         }
7300     },
7301     
7302     beforeBlur : Roo.emptyFn,
7303
7304     
7305     // private
7306     onBlur : function(){
7307         this.beforeBlur();
7308         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7309             //this.el.removeClass(this.focusClass);
7310         }
7311         this.hasFocus = false;
7312         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7313             this.validate();
7314         }
7315         var v = this.getValue();
7316         if(String(v) !== String(this.startValue)){
7317             this.fireEvent('change', this, v, this.startValue);
7318         }
7319         this.fireEvent("blur", this);
7320     },
7321     
7322     /**
7323      * Resets the current field value to the originally loaded value and clears any validation messages
7324      */
7325     reset : function(){
7326         this.setValue(this.originalValue);
7327         this.clearInvalid();
7328     },
7329      /**
7330      * Returns the name of the field
7331      * @return {Mixed} name The name field
7332      */
7333     getName: function(){
7334         return this.name;
7335     },
7336      /**
7337      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7338      * @return {Mixed} value The field value
7339      */
7340     getValue : function(){
7341         
7342         var v = this.inputEl().getValue();
7343         
7344         return v;
7345     },
7346     /**
7347      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7348      * @return {Mixed} value The field value
7349      */
7350     getRawValue : function(){
7351         var v = this.inputEl().getValue();
7352         
7353         return v;
7354     },
7355     
7356     /**
7357      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7358      * @param {Mixed} value The value to set
7359      */
7360     setRawValue : function(v){
7361         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7362     },
7363     
7364     selectText : function(start, end){
7365         var v = this.getRawValue();
7366         if(v.length > 0){
7367             start = start === undefined ? 0 : start;
7368             end = end === undefined ? v.length : end;
7369             var d = this.inputEl().dom;
7370             if(d.setSelectionRange){
7371                 d.setSelectionRange(start, end);
7372             }else if(d.createTextRange){
7373                 var range = d.createTextRange();
7374                 range.moveStart("character", start);
7375                 range.moveEnd("character", v.length-end);
7376                 range.select();
7377             }
7378         }
7379     },
7380     
7381     /**
7382      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7383      * @param {Mixed} value The value to set
7384      */
7385     setValue : function(v){
7386         this.value = v;
7387         if(this.rendered){
7388             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7389             this.validate();
7390         }
7391     },
7392     
7393     /*
7394     processValue : function(value){
7395         if(this.stripCharsRe){
7396             var newValue = value.replace(this.stripCharsRe, '');
7397             if(newValue !== value){
7398                 this.setRawValue(newValue);
7399                 return newValue;
7400             }
7401         }
7402         return value;
7403     },
7404   */
7405     preFocus : function(){
7406         
7407         if(this.selectOnFocus){
7408             this.inputEl().dom.select();
7409         }
7410     },
7411     filterKeys : function(e){
7412         var k = e.getKey();
7413         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7414             return;
7415         }
7416         var c = e.getCharCode(), cc = String.fromCharCode(c);
7417         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7418             return;
7419         }
7420         if(!this.maskRe.test(cc)){
7421             e.stopEvent();
7422         }
7423     },
7424      /**
7425      * Clear any invalid styles/messages for this field
7426      */
7427     clearInvalid : function(){
7428         
7429         if(!this.el || this.preventMark){ // not rendered
7430             return;
7431         }
7432         this.el.removeClass(this.invalidClass);
7433         /*
7434         switch(this.msgTarget){
7435             case 'qtip':
7436                 this.el.dom.qtip = '';
7437                 break;
7438             case 'title':
7439                 this.el.dom.title = '';
7440                 break;
7441             case 'under':
7442                 if(this.errorEl){
7443                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7444                 }
7445                 break;
7446             case 'side':
7447                 if(this.errorIcon){
7448                     this.errorIcon.dom.qtip = '';
7449                     this.errorIcon.hide();
7450                     this.un('resize', this.alignErrorIcon, this);
7451                 }
7452                 break;
7453             default:
7454                 var t = Roo.getDom(this.msgTarget);
7455                 t.innerHTML = '';
7456                 t.style.display = 'none';
7457                 break;
7458         }
7459         */
7460         this.fireEvent('valid', this);
7461     },
7462      /**
7463      * Mark this field as invalid
7464      * @param {String} msg The validation message
7465      */
7466     markInvalid : function(msg){
7467         if(!this.el  || this.preventMark){ // not rendered
7468             return;
7469         }
7470         this.el.addClass(this.invalidClass);
7471         /*
7472         msg = msg || this.invalidText;
7473         switch(this.msgTarget){
7474             case 'qtip':
7475                 this.el.dom.qtip = msg;
7476                 this.el.dom.qclass = 'x-form-invalid-tip';
7477                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7478                     Roo.QuickTips.enable();
7479                 }
7480                 break;
7481             case 'title':
7482                 this.el.dom.title = msg;
7483                 break;
7484             case 'under':
7485                 if(!this.errorEl){
7486                     var elp = this.el.findParent('.x-form-element', 5, true);
7487                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7488                     this.errorEl.setWidth(elp.getWidth(true)-20);
7489                 }
7490                 this.errorEl.update(msg);
7491                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7492                 break;
7493             case 'side':
7494                 if(!this.errorIcon){
7495                     var elp = this.el.findParent('.x-form-element', 5, true);
7496                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7497                 }
7498                 this.alignErrorIcon();
7499                 this.errorIcon.dom.qtip = msg;
7500                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7501                 this.errorIcon.show();
7502                 this.on('resize', this.alignErrorIcon, this);
7503                 break;
7504             default:
7505                 var t = Roo.getDom(this.msgTarget);
7506                 t.innerHTML = msg;
7507                 t.style.display = this.msgDisplay;
7508                 break;
7509         }
7510         */
7511         this.fireEvent('invalid', this, msg);
7512     },
7513     // private
7514     SafariOnKeyDown : function(event)
7515     {
7516         // this is a workaround for a password hang bug on chrome/ webkit.
7517         
7518         var isSelectAll = false;
7519         
7520         if(this.inputEl().dom.selectionEnd > 0){
7521             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7522         }
7523         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7524             event.preventDefault();
7525             this.setValue('');
7526             return;
7527         }
7528         
7529         if(isSelectAll){ // backspace and delete key
7530             
7531             event.preventDefault();
7532             // this is very hacky as keydown always get's upper case.
7533             //
7534             var cc = String.fromCharCode(event.getCharCode());
7535             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7536             
7537         }
7538     },
7539     adjustWidth : function(tag, w){
7540         tag = tag.toLowerCase();
7541         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7542             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7543                 if(tag == 'input'){
7544                     return w + 2;
7545                 }
7546                 if(tag == 'textarea'){
7547                     return w-2;
7548                 }
7549             }else if(Roo.isOpera){
7550                 if(tag == 'input'){
7551                     return w + 2;
7552                 }
7553                 if(tag == 'textarea'){
7554                     return w-2;
7555                 }
7556             }
7557         }
7558         return w;
7559     }
7560     
7561 });
7562
7563  
7564 /*
7565  * - LGPL
7566  *
7567  * Input
7568  * 
7569  */
7570
7571 /**
7572  * @class Roo.bootstrap.TextArea
7573  * @extends Roo.bootstrap.Input
7574  * Bootstrap TextArea class
7575  * @cfg {Number} cols Specifies the visible width of a text area
7576  * @cfg {Number} rows Specifies the visible number of lines in a text area
7577  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7578  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7579  * @cfg {string} html text
7580  * 
7581  * @constructor
7582  * Create a new TextArea
7583  * @param {Object} config The config object
7584  */
7585
7586 Roo.bootstrap.TextArea = function(config){
7587     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7588    
7589 };
7590
7591 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7592      
7593     cols : false,
7594     rows : 5,
7595     readOnly : false,
7596     warp : 'soft',
7597     resize : false,
7598     value: false,
7599     html: false,
7600     
7601     getAutoCreate : function(){
7602         
7603         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7604         
7605         var id = Roo.id();
7606         
7607         var cfg = {};
7608         
7609         var input =  {
7610             tag: 'textarea',
7611             id : id,
7612             warp : this.warp,
7613             rows : this.rows,
7614             value : this.value || '',
7615             html: this.html || '',
7616             cls : 'form-control',
7617             placeholder : this.placeholder || '' 
7618             
7619         };
7620         
7621         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7622             input.maxLength = this.maxLength;
7623         }
7624         
7625         if(this.resize){
7626             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7627         }
7628         
7629         if(this.cols){
7630             input.cols = this.cols;
7631         }
7632         
7633         if (this.readOnly) {
7634             input.readonly = true;
7635         }
7636         
7637         if (this.name) {
7638             input.name = this.name;
7639         }
7640         
7641         if (this.size) {
7642             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7643         }
7644         
7645         var settings=this;
7646         ['xs','sm','md','lg'].map(function(size){
7647             if (settings[size]) {
7648                 cfg.cls += ' col-' + size + '-' + settings[size];
7649             }
7650         });
7651         
7652         var inputblock = input;
7653         
7654         if (this.before || this.after) {
7655             
7656             inputblock = {
7657                 cls : 'input-group',
7658                 cn :  [] 
7659             };
7660             if (this.before) {
7661                 inputblock.cn.push({
7662                     tag :'span',
7663                     cls : 'input-group-addon',
7664                     html : this.before
7665                 });
7666             }
7667             inputblock.cn.push(input);
7668             if (this.after) {
7669                 inputblock.cn.push({
7670                     tag :'span',
7671                     cls : 'input-group-addon',
7672                     html : this.after
7673                 });
7674             }
7675             
7676         }
7677         
7678         if (align ==='left' && this.fieldLabel.length) {
7679                 Roo.log("left and has label");
7680                 cfg.cn = [
7681                     
7682                     {
7683                         tag: 'label',
7684                         'for' :  id,
7685                         cls : 'control-label col-sm-' + this.labelWidth,
7686                         html : this.fieldLabel
7687                         
7688                     },
7689                     {
7690                         cls : "col-sm-" + (12 - this.labelWidth), 
7691                         cn: [
7692                             inputblock
7693                         ]
7694                     }
7695                     
7696                 ];
7697         } else if ( this.fieldLabel.length) {
7698                 Roo.log(" label");
7699                  cfg.cn = [
7700                    
7701                     {
7702                         tag: 'label',
7703                         //cls : 'input-group-addon',
7704                         html : this.fieldLabel
7705                         
7706                     },
7707                     
7708                     inputblock
7709                     
7710                 ];
7711
7712         } else {
7713             
7714                    Roo.log(" no label && no align");
7715                 cfg.cn = [
7716                     
7717                         inputblock
7718                     
7719                 ];
7720                 
7721                 
7722         }
7723         
7724         if (this.disabled) {
7725             input.disabled=true;
7726         }
7727         
7728         return cfg;
7729         
7730     },
7731     /**
7732      * return the real textarea element.
7733      */
7734     inputEl: function ()
7735     {
7736         return this.el.select('textarea.form-control',true).first();
7737     }
7738 });
7739
7740  
7741 /*
7742  * - LGPL
7743  *
7744  * trigger field - base class for combo..
7745  * 
7746  */
7747  
7748 /**
7749  * @class Roo.bootstrap.TriggerField
7750  * @extends Roo.bootstrap.Input
7751  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7752  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7753  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7754  * for which you can provide a custom implementation.  For example:
7755  * <pre><code>
7756 var trigger = new Roo.bootstrap.TriggerField();
7757 trigger.onTriggerClick = myTriggerFn;
7758 trigger.applyTo('my-field');
7759 </code></pre>
7760  *
7761  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7762  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7763  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7764  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7765  * @constructor
7766  * Create a new TriggerField.
7767  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7768  * to the base TextField)
7769  */
7770 Roo.bootstrap.TriggerField = function(config){
7771     this.mimicing = false;
7772     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7773 };
7774
7775 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7776     /**
7777      * @cfg {String} triggerClass A CSS class to apply to the trigger
7778      */
7779      /**
7780      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7781      */
7782     hideTrigger:false,
7783
7784     /** @cfg {Boolean} grow @hide */
7785     /** @cfg {Number} growMin @hide */
7786     /** @cfg {Number} growMax @hide */
7787
7788     /**
7789      * @hide 
7790      * @method
7791      */
7792     autoSize: Roo.emptyFn,
7793     // private
7794     monitorTab : true,
7795     // private
7796     deferHeight : true,
7797
7798     
7799     actionMode : 'wrap',
7800     
7801     
7802     
7803     getAutoCreate : function(){
7804        
7805         var align = this.labelAlign || this.parentLabelAlign();
7806         
7807         var id = Roo.id();
7808         
7809         var cfg = {
7810             cls: 'form-group' //input-group
7811         };
7812         
7813         
7814         var input =  {
7815             tag: 'input',
7816             id : id,
7817             type : this.inputType,
7818             cls : 'form-control',
7819             autocomplete: 'off',
7820             placeholder : this.placeholder || '' 
7821             
7822         };
7823         if (this.name) {
7824             input.name = this.name;
7825         }
7826         if (this.size) {
7827             input.cls += ' input-' + this.size;
7828         }
7829         
7830         if (this.disabled) {
7831             input.disabled=true;
7832         }
7833         
7834         var inputblock = input;
7835         
7836         if (this.before || this.after) {
7837             
7838             inputblock = {
7839                 cls : 'input-group',
7840                 cn :  [] 
7841             };
7842             if (this.before) {
7843                 inputblock.cn.push({
7844                     tag :'span',
7845                     cls : 'input-group-addon',
7846                     html : this.before
7847                 });
7848             }
7849             inputblock.cn.push(input);
7850             if (this.after) {
7851                 inputblock.cn.push({
7852                     tag :'span',
7853                     cls : 'input-group-addon',
7854                     html : this.after
7855                 });
7856             }
7857             
7858         };
7859         
7860         var box = {
7861             tag: 'div',
7862             cn: [
7863                 {
7864                     tag: 'input',
7865                     type : 'hidden',
7866                     cls: 'form-hidden-field'
7867                 },
7868                 inputblock
7869             ]
7870             
7871         };
7872         
7873         if(this.multiple){
7874             Roo.log('multiple');
7875             
7876             box = {
7877                 tag: 'div',
7878                 cn: [
7879                     {
7880                         tag: 'input',
7881                         type : 'hidden',
7882                         cls: 'form-hidden-field'
7883                     },
7884                     {
7885                         tag: 'ul',
7886                         cls: 'select2-choices',
7887                         cn:[
7888                             {
7889                                 tag: 'li',
7890                                 cls: 'select2-search-field',
7891                                 cn: [
7892
7893                                     inputblock
7894                                 ]
7895                             }
7896                         ]
7897                     }
7898                 ]
7899             }
7900         };
7901         
7902         var combobox = {
7903             cls: 'select2-container input-group',
7904             cn: [
7905                 box
7906 //                {
7907 //                    tag: 'ul',
7908 //                    cls: 'typeahead typeahead-long dropdown-menu',
7909 //                    style: 'display:none'
7910 //                }
7911             ]
7912         };
7913         
7914         if(!this.multiple && this.showToggleBtn){
7915             combobox.cn.push({
7916                 tag :'span',
7917                 cls : 'input-group-addon btn dropdown-toggle',
7918                 cn : [
7919                     {
7920                         tag: 'span',
7921                         cls: 'caret'
7922                     },
7923                     {
7924                         tag: 'span',
7925                         cls: 'combobox-clear',
7926                         cn  : [
7927                             {
7928                                 tag : 'i',
7929                                 cls: 'icon-remove'
7930                             }
7931                         ]
7932                     }
7933                 ]
7934
7935             })
7936         }
7937         
7938         if(this.multiple){
7939             combobox.cls += ' select2-container-multi';
7940         }
7941         
7942         if (align ==='left' && this.fieldLabel.length) {
7943             
7944                 Roo.log("left and has label");
7945                 cfg.cn = [
7946                     
7947                     {
7948                         tag: 'label',
7949                         'for' :  id,
7950                         cls : 'control-label col-sm-' + this.labelWidth,
7951                         html : this.fieldLabel
7952                         
7953                     },
7954                     {
7955                         cls : "col-sm-" + (12 - this.labelWidth), 
7956                         cn: [
7957                             combobox
7958                         ]
7959                     }
7960                     
7961                 ];
7962         } else if ( this.fieldLabel.length) {
7963                 Roo.log(" label");
7964                  cfg.cn = [
7965                    
7966                     {
7967                         tag: 'label',
7968                         //cls : 'input-group-addon',
7969                         html : this.fieldLabel
7970                         
7971                     },
7972                     
7973                     combobox
7974                     
7975                 ];
7976
7977         } else {
7978             
7979                 Roo.log(" no label && no align");
7980                 cfg = combobox
7981                      
7982                 
7983         }
7984          
7985         var settings=this;
7986         ['xs','sm','md','lg'].map(function(size){
7987             if (settings[size]) {
7988                 cfg.cls += ' col-' + size + '-' + settings[size];
7989             }
7990         });
7991         
7992         return cfg;
7993         
7994     },
7995     
7996     
7997     
7998     // private
7999     onResize : function(w, h){
8000 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8001 //        if(typeof w == 'number'){
8002 //            var x = w - this.trigger.getWidth();
8003 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8004 //            this.trigger.setStyle('left', x+'px');
8005 //        }
8006     },
8007
8008     // private
8009     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8010
8011     // private
8012     getResizeEl : function(){
8013         return this.inputEl();
8014     },
8015
8016     // private
8017     getPositionEl : function(){
8018         return this.inputEl();
8019     },
8020
8021     // private
8022     alignErrorIcon : function(){
8023         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8024     },
8025
8026     // private
8027     initEvents : function(){
8028         
8029         this.createList();
8030         
8031         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8032         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8033         if(!this.multiple && this.showToggleBtn){
8034             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8035             if(this.hideTrigger){
8036                 this.trigger.setDisplayed(false);
8037             }
8038             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8039         }
8040         
8041         if(this.multiple){
8042             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8043         }
8044         
8045         //this.trigger.addClassOnOver('x-form-trigger-over');
8046         //this.trigger.addClassOnClick('x-form-trigger-click');
8047         
8048         //if(!this.width){
8049         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8050         //}
8051     },
8052     
8053     createList : function()
8054     {
8055         this.list = Roo.get(document.body).createChild({
8056             tag: 'ul',
8057             cls: 'typeahead typeahead-long dropdown-menu',
8058             style: 'display:none'
8059         });
8060         
8061         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8062         
8063     },
8064
8065     // private
8066     initTrigger : function(){
8067        
8068     },
8069
8070     // private
8071     onDestroy : function(){
8072         if(this.trigger){
8073             this.trigger.removeAllListeners();
8074           //  this.trigger.remove();
8075         }
8076         //if(this.wrap){
8077         //    this.wrap.remove();
8078         //}
8079         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8080     },
8081
8082     // private
8083     onFocus : function(){
8084         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8085         /*
8086         if(!this.mimicing){
8087             this.wrap.addClass('x-trigger-wrap-focus');
8088             this.mimicing = true;
8089             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8090             if(this.monitorTab){
8091                 this.el.on("keydown", this.checkTab, this);
8092             }
8093         }
8094         */
8095     },
8096
8097     // private
8098     checkTab : function(e){
8099         if(e.getKey() == e.TAB){
8100             this.triggerBlur();
8101         }
8102     },
8103
8104     // private
8105     onBlur : function(){
8106         // do nothing
8107     },
8108
8109     // private
8110     mimicBlur : function(e, t){
8111         /*
8112         if(!this.wrap.contains(t) && this.validateBlur()){
8113             this.triggerBlur();
8114         }
8115         */
8116     },
8117
8118     // private
8119     triggerBlur : function(){
8120         this.mimicing = false;
8121         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8122         if(this.monitorTab){
8123             this.el.un("keydown", this.checkTab, this);
8124         }
8125         //this.wrap.removeClass('x-trigger-wrap-focus');
8126         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8127     },
8128
8129     // private
8130     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8131     validateBlur : function(e, t){
8132         return true;
8133     },
8134
8135     // private
8136     onDisable : function(){
8137         this.inputEl().dom.disabled = true;
8138         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8139         //if(this.wrap){
8140         //    this.wrap.addClass('x-item-disabled');
8141         //}
8142     },
8143
8144     // private
8145     onEnable : function(){
8146         this.inputEl().dom.disabled = false;
8147         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8148         //if(this.wrap){
8149         //    this.el.removeClass('x-item-disabled');
8150         //}
8151     },
8152
8153     // private
8154     onShow : function(){
8155         var ae = this.getActionEl();
8156         
8157         if(ae){
8158             ae.dom.style.display = '';
8159             ae.dom.style.visibility = 'visible';
8160         }
8161     },
8162
8163     // private
8164     
8165     onHide : function(){
8166         var ae = this.getActionEl();
8167         ae.dom.style.display = 'none';
8168     },
8169
8170     /**
8171      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8172      * by an implementing function.
8173      * @method
8174      * @param {EventObject} e
8175      */
8176     onTriggerClick : Roo.emptyFn
8177 });
8178  /*
8179  * Based on:
8180  * Ext JS Library 1.1.1
8181  * Copyright(c) 2006-2007, Ext JS, LLC.
8182  *
8183  * Originally Released Under LGPL - original licence link has changed is not relivant.
8184  *
8185  * Fork - LGPL
8186  * <script type="text/javascript">
8187  */
8188
8189
8190 /**
8191  * @class Roo.data.SortTypes
8192  * @singleton
8193  * Defines the default sorting (casting?) comparison functions used when sorting data.
8194  */
8195 Roo.data.SortTypes = {
8196     /**
8197      * Default sort that does nothing
8198      * @param {Mixed} s The value being converted
8199      * @return {Mixed} The comparison value
8200      */
8201     none : function(s){
8202         return s;
8203     },
8204     
8205     /**
8206      * The regular expression used to strip tags
8207      * @type {RegExp}
8208      * @property
8209      */
8210     stripTagsRE : /<\/?[^>]+>/gi,
8211     
8212     /**
8213      * Strips all HTML tags to sort on text only
8214      * @param {Mixed} s The value being converted
8215      * @return {String} The comparison value
8216      */
8217     asText : function(s){
8218         return String(s).replace(this.stripTagsRE, "");
8219     },
8220     
8221     /**
8222      * Strips all HTML tags to sort on text only - Case insensitive
8223      * @param {Mixed} s The value being converted
8224      * @return {String} The comparison value
8225      */
8226     asUCText : function(s){
8227         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8228     },
8229     
8230     /**
8231      * Case insensitive string
8232      * @param {Mixed} s The value being converted
8233      * @return {String} The comparison value
8234      */
8235     asUCString : function(s) {
8236         return String(s).toUpperCase();
8237     },
8238     
8239     /**
8240      * Date sorting
8241      * @param {Mixed} s The value being converted
8242      * @return {Number} The comparison value
8243      */
8244     asDate : function(s) {
8245         if(!s){
8246             return 0;
8247         }
8248         if(s instanceof Date){
8249             return s.getTime();
8250         }
8251         return Date.parse(String(s));
8252     },
8253     
8254     /**
8255      * Float sorting
8256      * @param {Mixed} s The value being converted
8257      * @return {Float} The comparison value
8258      */
8259     asFloat : function(s) {
8260         var val = parseFloat(String(s).replace(/,/g, ""));
8261         if(isNaN(val)) val = 0;
8262         return val;
8263     },
8264     
8265     /**
8266      * Integer sorting
8267      * @param {Mixed} s The value being converted
8268      * @return {Number} The comparison value
8269      */
8270     asInt : function(s) {
8271         var val = parseInt(String(s).replace(/,/g, ""));
8272         if(isNaN(val)) val = 0;
8273         return val;
8274     }
8275 };/*
8276  * Based on:
8277  * Ext JS Library 1.1.1
8278  * Copyright(c) 2006-2007, Ext JS, LLC.
8279  *
8280  * Originally Released Under LGPL - original licence link has changed is not relivant.
8281  *
8282  * Fork - LGPL
8283  * <script type="text/javascript">
8284  */
8285
8286 /**
8287 * @class Roo.data.Record
8288  * Instances of this class encapsulate both record <em>definition</em> information, and record
8289  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8290  * to access Records cached in an {@link Roo.data.Store} object.<br>
8291  * <p>
8292  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8293  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8294  * objects.<br>
8295  * <p>
8296  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8297  * @constructor
8298  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8299  * {@link #create}. The parameters are the same.
8300  * @param {Array} data An associative Array of data values keyed by the field name.
8301  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8302  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8303  * not specified an integer id is generated.
8304  */
8305 Roo.data.Record = function(data, id){
8306     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8307     this.data = data;
8308 };
8309
8310 /**
8311  * Generate a constructor for a specific record layout.
8312  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8313  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8314  * Each field definition object may contain the following properties: <ul>
8315  * <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,
8316  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8317  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8318  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8319  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8320  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8321  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8322  * this may be omitted.</p></li>
8323  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8324  * <ul><li>auto (Default, implies no conversion)</li>
8325  * <li>string</li>
8326  * <li>int</li>
8327  * <li>float</li>
8328  * <li>boolean</li>
8329  * <li>date</li></ul></p></li>
8330  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8331  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8332  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8333  * by the Reader into an object that will be stored in the Record. It is passed the
8334  * following parameters:<ul>
8335  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8336  * </ul></p></li>
8337  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8338  * </ul>
8339  * <br>usage:<br><pre><code>
8340 var TopicRecord = Roo.data.Record.create(
8341     {name: 'title', mapping: 'topic_title'},
8342     {name: 'author', mapping: 'username'},
8343     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8344     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8345     {name: 'lastPoster', mapping: 'user2'},
8346     {name: 'excerpt', mapping: 'post_text'}
8347 );
8348
8349 var myNewRecord = new TopicRecord({
8350     title: 'Do my job please',
8351     author: 'noobie',
8352     totalPosts: 1,
8353     lastPost: new Date(),
8354     lastPoster: 'Animal',
8355     excerpt: 'No way dude!'
8356 });
8357 myStore.add(myNewRecord);
8358 </code></pre>
8359  * @method create
8360  * @static
8361  */
8362 Roo.data.Record.create = function(o){
8363     var f = function(){
8364         f.superclass.constructor.apply(this, arguments);
8365     };
8366     Roo.extend(f, Roo.data.Record);
8367     var p = f.prototype;
8368     p.fields = new Roo.util.MixedCollection(false, function(field){
8369         return field.name;
8370     });
8371     for(var i = 0, len = o.length; i < len; i++){
8372         p.fields.add(new Roo.data.Field(o[i]));
8373     }
8374     f.getField = function(name){
8375         return p.fields.get(name);  
8376     };
8377     return f;
8378 };
8379
8380 Roo.data.Record.AUTO_ID = 1000;
8381 Roo.data.Record.EDIT = 'edit';
8382 Roo.data.Record.REJECT = 'reject';
8383 Roo.data.Record.COMMIT = 'commit';
8384
8385 Roo.data.Record.prototype = {
8386     /**
8387      * Readonly flag - true if this record has been modified.
8388      * @type Boolean
8389      */
8390     dirty : false,
8391     editing : false,
8392     error: null,
8393     modified: null,
8394
8395     // private
8396     join : function(store){
8397         this.store = store;
8398     },
8399
8400     /**
8401      * Set the named field to the specified value.
8402      * @param {String} name The name of the field to set.
8403      * @param {Object} value The value to set the field to.
8404      */
8405     set : function(name, value){
8406         if(this.data[name] == value){
8407             return;
8408         }
8409         this.dirty = true;
8410         if(!this.modified){
8411             this.modified = {};
8412         }
8413         if(typeof this.modified[name] == 'undefined'){
8414             this.modified[name] = this.data[name];
8415         }
8416         this.data[name] = value;
8417         if(!this.editing && this.store){
8418             this.store.afterEdit(this);
8419         }       
8420     },
8421
8422     /**
8423      * Get the value of the named field.
8424      * @param {String} name The name of the field to get the value of.
8425      * @return {Object} The value of the field.
8426      */
8427     get : function(name){
8428         return this.data[name]; 
8429     },
8430
8431     // private
8432     beginEdit : function(){
8433         this.editing = true;
8434         this.modified = {}; 
8435     },
8436
8437     // private
8438     cancelEdit : function(){
8439         this.editing = false;
8440         delete this.modified;
8441     },
8442
8443     // private
8444     endEdit : function(){
8445         this.editing = false;
8446         if(this.dirty && this.store){
8447             this.store.afterEdit(this);
8448         }
8449     },
8450
8451     /**
8452      * Usually called by the {@link Roo.data.Store} which owns the Record.
8453      * Rejects all changes made to the Record since either creation, or the last commit operation.
8454      * Modified fields are reverted to their original values.
8455      * <p>
8456      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8457      * of reject operations.
8458      */
8459     reject : function(){
8460         var m = this.modified;
8461         for(var n in m){
8462             if(typeof m[n] != "function"){
8463                 this.data[n] = m[n];
8464             }
8465         }
8466         this.dirty = false;
8467         delete this.modified;
8468         this.editing = false;
8469         if(this.store){
8470             this.store.afterReject(this);
8471         }
8472     },
8473
8474     /**
8475      * Usually called by the {@link Roo.data.Store} which owns the Record.
8476      * Commits all changes made to the Record since either creation, or the last commit operation.
8477      * <p>
8478      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8479      * of commit operations.
8480      */
8481     commit : function(){
8482         this.dirty = false;
8483         delete this.modified;
8484         this.editing = false;
8485         if(this.store){
8486             this.store.afterCommit(this);
8487         }
8488     },
8489
8490     // private
8491     hasError : function(){
8492         return this.error != null;
8493     },
8494
8495     // private
8496     clearError : function(){
8497         this.error = null;
8498     },
8499
8500     /**
8501      * Creates a copy of this record.
8502      * @param {String} id (optional) A new record id if you don't want to use this record's id
8503      * @return {Record}
8504      */
8505     copy : function(newId) {
8506         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8507     }
8508 };/*
8509  * Based on:
8510  * Ext JS Library 1.1.1
8511  * Copyright(c) 2006-2007, Ext JS, LLC.
8512  *
8513  * Originally Released Under LGPL - original licence link has changed is not relivant.
8514  *
8515  * Fork - LGPL
8516  * <script type="text/javascript">
8517  */
8518
8519
8520
8521 /**
8522  * @class Roo.data.Store
8523  * @extends Roo.util.Observable
8524  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8525  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8526  * <p>
8527  * 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
8528  * has no knowledge of the format of the data returned by the Proxy.<br>
8529  * <p>
8530  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8531  * instances from the data object. These records are cached and made available through accessor functions.
8532  * @constructor
8533  * Creates a new Store.
8534  * @param {Object} config A config object containing the objects needed for the Store to access data,
8535  * and read the data into Records.
8536  */
8537 Roo.data.Store = function(config){
8538     this.data = new Roo.util.MixedCollection(false);
8539     this.data.getKey = function(o){
8540         return o.id;
8541     };
8542     this.baseParams = {};
8543     // private
8544     this.paramNames = {
8545         "start" : "start",
8546         "limit" : "limit",
8547         "sort" : "sort",
8548         "dir" : "dir",
8549         "multisort" : "_multisort"
8550     };
8551
8552     if(config && config.data){
8553         this.inlineData = config.data;
8554         delete config.data;
8555     }
8556
8557     Roo.apply(this, config);
8558     
8559     if(this.reader){ // reader passed
8560         this.reader = Roo.factory(this.reader, Roo.data);
8561         this.reader.xmodule = this.xmodule || false;
8562         if(!this.recordType){
8563             this.recordType = this.reader.recordType;
8564         }
8565         if(this.reader.onMetaChange){
8566             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8567         }
8568     }
8569
8570     if(this.recordType){
8571         this.fields = this.recordType.prototype.fields;
8572     }
8573     this.modified = [];
8574
8575     this.addEvents({
8576         /**
8577          * @event datachanged
8578          * Fires when the data cache has changed, and a widget which is using this Store
8579          * as a Record cache should refresh its view.
8580          * @param {Store} this
8581          */
8582         datachanged : true,
8583         /**
8584          * @event metachange
8585          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8586          * @param {Store} this
8587          * @param {Object} meta The JSON metadata
8588          */
8589         metachange : true,
8590         /**
8591          * @event add
8592          * Fires when Records have been added to the Store
8593          * @param {Store} this
8594          * @param {Roo.data.Record[]} records The array of Records added
8595          * @param {Number} index The index at which the record(s) were added
8596          */
8597         add : true,
8598         /**
8599          * @event remove
8600          * Fires when a Record has been removed from the Store
8601          * @param {Store} this
8602          * @param {Roo.data.Record} record The Record that was removed
8603          * @param {Number} index The index at which the record was removed
8604          */
8605         remove : true,
8606         /**
8607          * @event update
8608          * Fires when a Record has been updated
8609          * @param {Store} this
8610          * @param {Roo.data.Record} record The Record that was updated
8611          * @param {String} operation The update operation being performed.  Value may be one of:
8612          * <pre><code>
8613  Roo.data.Record.EDIT
8614  Roo.data.Record.REJECT
8615  Roo.data.Record.COMMIT
8616          * </code></pre>
8617          */
8618         update : true,
8619         /**
8620          * @event clear
8621          * Fires when the data cache has been cleared.
8622          * @param {Store} this
8623          */
8624         clear : true,
8625         /**
8626          * @event beforeload
8627          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8628          * the load action will be canceled.
8629          * @param {Store} this
8630          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8631          */
8632         beforeload : true,
8633         /**
8634          * @event beforeloadadd
8635          * Fires after a new set of Records has been loaded.
8636          * @param {Store} this
8637          * @param {Roo.data.Record[]} records The Records that were loaded
8638          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8639          */
8640         beforeloadadd : true,
8641         /**
8642          * @event load
8643          * Fires after a new set of Records has been loaded, before they are added to the store.
8644          * @param {Store} this
8645          * @param {Roo.data.Record[]} records The Records that were loaded
8646          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8647          * @params {Object} return from reader
8648          */
8649         load : true,
8650         /**
8651          * @event loadexception
8652          * Fires if an exception occurs in the Proxy during loading.
8653          * Called with the signature of the Proxy's "loadexception" event.
8654          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8655          * 
8656          * @param {Proxy} 
8657          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8658          * @param {Object} load options 
8659          * @param {Object} jsonData from your request (normally this contains the Exception)
8660          */
8661         loadexception : true
8662     });
8663     
8664     if(this.proxy){
8665         this.proxy = Roo.factory(this.proxy, Roo.data);
8666         this.proxy.xmodule = this.xmodule || false;
8667         this.relayEvents(this.proxy,  ["loadexception"]);
8668     }
8669     this.sortToggle = {};
8670     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8671
8672     Roo.data.Store.superclass.constructor.call(this);
8673
8674     if(this.inlineData){
8675         this.loadData(this.inlineData);
8676         delete this.inlineData;
8677     }
8678 };
8679
8680 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8681      /**
8682     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8683     * without a remote query - used by combo/forms at present.
8684     */
8685     
8686     /**
8687     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8688     */
8689     /**
8690     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8691     */
8692     /**
8693     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8694     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8695     */
8696     /**
8697     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8698     * on any HTTP request
8699     */
8700     /**
8701     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8702     */
8703     /**
8704     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8705     */
8706     multiSort: false,
8707     /**
8708     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8709     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8710     */
8711     remoteSort : false,
8712
8713     /**
8714     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8715      * loaded or when a record is removed. (defaults to false).
8716     */
8717     pruneModifiedRecords : false,
8718
8719     // private
8720     lastOptions : null,
8721
8722     /**
8723      * Add Records to the Store and fires the add event.
8724      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8725      */
8726     add : function(records){
8727         records = [].concat(records);
8728         for(var i = 0, len = records.length; i < len; i++){
8729             records[i].join(this);
8730         }
8731         var index = this.data.length;
8732         this.data.addAll(records);
8733         this.fireEvent("add", this, records, index);
8734     },
8735
8736     /**
8737      * Remove a Record from the Store and fires the remove event.
8738      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8739      */
8740     remove : function(record){
8741         var index = this.data.indexOf(record);
8742         this.data.removeAt(index);
8743         if(this.pruneModifiedRecords){
8744             this.modified.remove(record);
8745         }
8746         this.fireEvent("remove", this, record, index);
8747     },
8748
8749     /**
8750      * Remove all Records from the Store and fires the clear event.
8751      */
8752     removeAll : function(){
8753         this.data.clear();
8754         if(this.pruneModifiedRecords){
8755             this.modified = [];
8756         }
8757         this.fireEvent("clear", this);
8758     },
8759
8760     /**
8761      * Inserts Records to the Store at the given index and fires the add event.
8762      * @param {Number} index The start index at which to insert the passed Records.
8763      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8764      */
8765     insert : function(index, records){
8766         records = [].concat(records);
8767         for(var i = 0, len = records.length; i < len; i++){
8768             this.data.insert(index, records[i]);
8769             records[i].join(this);
8770         }
8771         this.fireEvent("add", this, records, index);
8772     },
8773
8774     /**
8775      * Get the index within the cache of the passed Record.
8776      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8777      * @return {Number} The index of the passed Record. Returns -1 if not found.
8778      */
8779     indexOf : function(record){
8780         return this.data.indexOf(record);
8781     },
8782
8783     /**
8784      * Get the index within the cache of the Record with the passed id.
8785      * @param {String} id The id of the Record to find.
8786      * @return {Number} The index of the Record. Returns -1 if not found.
8787      */
8788     indexOfId : function(id){
8789         return this.data.indexOfKey(id);
8790     },
8791
8792     /**
8793      * Get the Record with the specified id.
8794      * @param {String} id The id of the Record to find.
8795      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8796      */
8797     getById : function(id){
8798         return this.data.key(id);
8799     },
8800
8801     /**
8802      * Get the Record at the specified index.
8803      * @param {Number} index The index of the Record to find.
8804      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8805      */
8806     getAt : function(index){
8807         return this.data.itemAt(index);
8808     },
8809
8810     /**
8811      * Returns a range of Records between specified indices.
8812      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8813      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8814      * @return {Roo.data.Record[]} An array of Records
8815      */
8816     getRange : function(start, end){
8817         return this.data.getRange(start, end);
8818     },
8819
8820     // private
8821     storeOptions : function(o){
8822         o = Roo.apply({}, o);
8823         delete o.callback;
8824         delete o.scope;
8825         this.lastOptions = o;
8826     },
8827
8828     /**
8829      * Loads the Record cache from the configured Proxy using the configured Reader.
8830      * <p>
8831      * If using remote paging, then the first load call must specify the <em>start</em>
8832      * and <em>limit</em> properties in the options.params property to establish the initial
8833      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8834      * <p>
8835      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8836      * and this call will return before the new data has been loaded. Perform any post-processing
8837      * in a callback function, or in a "load" event handler.</strong>
8838      * <p>
8839      * @param {Object} options An object containing properties which control loading options:<ul>
8840      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8841      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8842      * passed the following arguments:<ul>
8843      * <li>r : Roo.data.Record[]</li>
8844      * <li>options: Options object from the load call</li>
8845      * <li>success: Boolean success indicator</li></ul></li>
8846      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8847      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8848      * </ul>
8849      */
8850     load : function(options){
8851         options = options || {};
8852         if(this.fireEvent("beforeload", this, options) !== false){
8853             this.storeOptions(options);
8854             var p = Roo.apply(options.params || {}, this.baseParams);
8855             // if meta was not loaded from remote source.. try requesting it.
8856             if (!this.reader.metaFromRemote) {
8857                 p._requestMeta = 1;
8858             }
8859             if(this.sortInfo && this.remoteSort){
8860                 var pn = this.paramNames;
8861                 p[pn["sort"]] = this.sortInfo.field;
8862                 p[pn["dir"]] = this.sortInfo.direction;
8863             }
8864             if (this.multiSort) {
8865                 var pn = this.paramNames;
8866                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8867             }
8868             
8869             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8870         }
8871     },
8872
8873     /**
8874      * Reloads the Record cache from the configured Proxy using the configured Reader and
8875      * the options from the last load operation performed.
8876      * @param {Object} options (optional) An object containing properties which may override the options
8877      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8878      * the most recently used options are reused).
8879      */
8880     reload : function(options){
8881         this.load(Roo.applyIf(options||{}, this.lastOptions));
8882     },
8883
8884     // private
8885     // Called as a callback by the Reader during a load operation.
8886     loadRecords : function(o, options, success){
8887         if(!o || success === false){
8888             if(success !== false){
8889                 this.fireEvent("load", this, [], options, o);
8890             }
8891             if(options.callback){
8892                 options.callback.call(options.scope || this, [], options, false);
8893             }
8894             return;
8895         }
8896         // if data returned failure - throw an exception.
8897         if (o.success === false) {
8898             // show a message if no listener is registered.
8899             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8900                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8901             }
8902             // loadmask wil be hooked into this..
8903             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8904             return;
8905         }
8906         var r = o.records, t = o.totalRecords || r.length;
8907         
8908         this.fireEvent("beforeloadadd", this, r, options, o);
8909         
8910         if(!options || options.add !== true){
8911             if(this.pruneModifiedRecords){
8912                 this.modified = [];
8913             }
8914             for(var i = 0, len = r.length; i < len; i++){
8915                 r[i].join(this);
8916             }
8917             if(this.snapshot){
8918                 this.data = this.snapshot;
8919                 delete this.snapshot;
8920             }
8921             this.data.clear();
8922             this.data.addAll(r);
8923             this.totalLength = t;
8924             this.applySort();
8925             this.fireEvent("datachanged", this);
8926         }else{
8927             this.totalLength = Math.max(t, this.data.length+r.length);
8928             this.add(r);
8929         }
8930         this.fireEvent("load", this, r, options, o);
8931         if(options.callback){
8932             options.callback.call(options.scope || this, r, options, true);
8933         }
8934     },
8935
8936
8937     /**
8938      * Loads data from a passed data block. A Reader which understands the format of the data
8939      * must have been configured in the constructor.
8940      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8941      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8942      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8943      */
8944     loadData : function(o, append){
8945         var r = this.reader.readRecords(o);
8946         this.loadRecords(r, {add: append}, true);
8947     },
8948
8949     /**
8950      * Gets the number of cached records.
8951      * <p>
8952      * <em>If using paging, this may not be the total size of the dataset. If the data object
8953      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8954      * the data set size</em>
8955      */
8956     getCount : function(){
8957         return this.data.length || 0;
8958     },
8959
8960     /**
8961      * Gets the total number of records in the dataset as returned by the server.
8962      * <p>
8963      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8964      * the dataset size</em>
8965      */
8966     getTotalCount : function(){
8967         return this.totalLength || 0;
8968     },
8969
8970     /**
8971      * Returns the sort state of the Store as an object with two properties:
8972      * <pre><code>
8973  field {String} The name of the field by which the Records are sorted
8974  direction {String} The sort order, "ASC" or "DESC"
8975      * </code></pre>
8976      */
8977     getSortState : function(){
8978         return this.sortInfo;
8979     },
8980
8981     // private
8982     applySort : function(){
8983         if(this.sortInfo && !this.remoteSort){
8984             var s = this.sortInfo, f = s.field;
8985             var st = this.fields.get(f).sortType;
8986             var fn = function(r1, r2){
8987                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8988                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8989             };
8990             this.data.sort(s.direction, fn);
8991             if(this.snapshot && this.snapshot != this.data){
8992                 this.snapshot.sort(s.direction, fn);
8993             }
8994         }
8995     },
8996
8997     /**
8998      * Sets the default sort column and order to be used by the next load operation.
8999      * @param {String} fieldName The name of the field to sort by.
9000      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9001      */
9002     setDefaultSort : function(field, dir){
9003         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9004     },
9005
9006     /**
9007      * Sort the Records.
9008      * If remote sorting is used, the sort is performed on the server, and the cache is
9009      * reloaded. If local sorting is used, the cache is sorted internally.
9010      * @param {String} fieldName The name of the field to sort by.
9011      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9012      */
9013     sort : function(fieldName, dir){
9014         var f = this.fields.get(fieldName);
9015         if(!dir){
9016             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9017             
9018             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9019                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9020             }else{
9021                 dir = f.sortDir;
9022             }
9023         }
9024         this.sortToggle[f.name] = dir;
9025         this.sortInfo = {field: f.name, direction: dir};
9026         if(!this.remoteSort){
9027             this.applySort();
9028             this.fireEvent("datachanged", this);
9029         }else{
9030             this.load(this.lastOptions);
9031         }
9032     },
9033
9034     /**
9035      * Calls the specified function for each of the Records in the cache.
9036      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9037      * Returning <em>false</em> aborts and exits the iteration.
9038      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9039      */
9040     each : function(fn, scope){
9041         this.data.each(fn, scope);
9042     },
9043
9044     /**
9045      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9046      * (e.g., during paging).
9047      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9048      */
9049     getModifiedRecords : function(){
9050         return this.modified;
9051     },
9052
9053     // private
9054     createFilterFn : function(property, value, anyMatch){
9055         if(!value.exec){ // not a regex
9056             value = String(value);
9057             if(value.length == 0){
9058                 return false;
9059             }
9060             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9061         }
9062         return function(r){
9063             return value.test(r.data[property]);
9064         };
9065     },
9066
9067     /**
9068      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9069      * @param {String} property A field on your records
9070      * @param {Number} start The record index to start at (defaults to 0)
9071      * @param {Number} end The last record index to include (defaults to length - 1)
9072      * @return {Number} The sum
9073      */
9074     sum : function(property, start, end){
9075         var rs = this.data.items, v = 0;
9076         start = start || 0;
9077         end = (end || end === 0) ? end : rs.length-1;
9078
9079         for(var i = start; i <= end; i++){
9080             v += (rs[i].data[property] || 0);
9081         }
9082         return v;
9083     },
9084
9085     /**
9086      * Filter the records by a specified property.
9087      * @param {String} field A field on your records
9088      * @param {String/RegExp} value Either a string that the field
9089      * should start with or a RegExp to test against the field
9090      * @param {Boolean} anyMatch True to match any part not just the beginning
9091      */
9092     filter : function(property, value, anyMatch){
9093         var fn = this.createFilterFn(property, value, anyMatch);
9094         return fn ? this.filterBy(fn) : this.clearFilter();
9095     },
9096
9097     /**
9098      * Filter by a function. The specified function will be called with each
9099      * record in this data source. If the function returns true the record is included,
9100      * otherwise it is filtered.
9101      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9102      * @param {Object} scope (optional) The scope of the function (defaults to this)
9103      */
9104     filterBy : function(fn, scope){
9105         this.snapshot = this.snapshot || this.data;
9106         this.data = this.queryBy(fn, scope||this);
9107         this.fireEvent("datachanged", this);
9108     },
9109
9110     /**
9111      * Query the records by a specified property.
9112      * @param {String} field A field on your records
9113      * @param {String/RegExp} value Either a string that the field
9114      * should start with or a RegExp to test against the field
9115      * @param {Boolean} anyMatch True to match any part not just the beginning
9116      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9117      */
9118     query : function(property, value, anyMatch){
9119         var fn = this.createFilterFn(property, value, anyMatch);
9120         return fn ? this.queryBy(fn) : this.data.clone();
9121     },
9122
9123     /**
9124      * Query by a function. The specified function will be called with each
9125      * record in this data source. If the function returns true the record is included
9126      * in the results.
9127      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9128      * @param {Object} scope (optional) The scope of the function (defaults to this)
9129       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9130      **/
9131     queryBy : function(fn, scope){
9132         var data = this.snapshot || this.data;
9133         return data.filterBy(fn, scope||this);
9134     },
9135
9136     /**
9137      * Collects unique values for a particular dataIndex from this store.
9138      * @param {String} dataIndex The property to collect
9139      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9140      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9141      * @return {Array} An array of the unique values
9142      **/
9143     collect : function(dataIndex, allowNull, bypassFilter){
9144         var d = (bypassFilter === true && this.snapshot) ?
9145                 this.snapshot.items : this.data.items;
9146         var v, sv, r = [], l = {};
9147         for(var i = 0, len = d.length; i < len; i++){
9148             v = d[i].data[dataIndex];
9149             sv = String(v);
9150             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9151                 l[sv] = true;
9152                 r[r.length] = v;
9153             }
9154         }
9155         return r;
9156     },
9157
9158     /**
9159      * Revert to a view of the Record cache with no filtering applied.
9160      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9161      */
9162     clearFilter : function(suppressEvent){
9163         if(this.snapshot && this.snapshot != this.data){
9164             this.data = this.snapshot;
9165             delete this.snapshot;
9166             if(suppressEvent !== true){
9167                 this.fireEvent("datachanged", this);
9168             }
9169         }
9170     },
9171
9172     // private
9173     afterEdit : function(record){
9174         if(this.modified.indexOf(record) == -1){
9175             this.modified.push(record);
9176         }
9177         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9178     },
9179     
9180     // private
9181     afterReject : function(record){
9182         this.modified.remove(record);
9183         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9184     },
9185
9186     // private
9187     afterCommit : function(record){
9188         this.modified.remove(record);
9189         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9190     },
9191
9192     /**
9193      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9194      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9195      */
9196     commitChanges : function(){
9197         var m = this.modified.slice(0);
9198         this.modified = [];
9199         for(var i = 0, len = m.length; i < len; i++){
9200             m[i].commit();
9201         }
9202     },
9203
9204     /**
9205      * Cancel outstanding changes on all changed records.
9206      */
9207     rejectChanges : function(){
9208         var m = this.modified.slice(0);
9209         this.modified = [];
9210         for(var i = 0, len = m.length; i < len; i++){
9211             m[i].reject();
9212         }
9213     },
9214
9215     onMetaChange : function(meta, rtype, o){
9216         this.recordType = rtype;
9217         this.fields = rtype.prototype.fields;
9218         delete this.snapshot;
9219         this.sortInfo = meta.sortInfo || this.sortInfo;
9220         this.modified = [];
9221         this.fireEvent('metachange', this, this.reader.meta);
9222     },
9223     
9224     moveIndex : function(data, type)
9225     {
9226         var index = this.indexOf(data);
9227         
9228         var newIndex = index + type;
9229         
9230         this.remove(data);
9231         
9232         this.insert(newIndex, data);
9233         
9234     }
9235 });/*
9236  * Based on:
9237  * Ext JS Library 1.1.1
9238  * Copyright(c) 2006-2007, Ext JS, LLC.
9239  *
9240  * Originally Released Under LGPL - original licence link has changed is not relivant.
9241  *
9242  * Fork - LGPL
9243  * <script type="text/javascript">
9244  */
9245
9246 /**
9247  * @class Roo.data.SimpleStore
9248  * @extends Roo.data.Store
9249  * Small helper class to make creating Stores from Array data easier.
9250  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9251  * @cfg {Array} fields An array of field definition objects, or field name strings.
9252  * @cfg {Array} data The multi-dimensional array of data
9253  * @constructor
9254  * @param {Object} config
9255  */
9256 Roo.data.SimpleStore = function(config){
9257     Roo.data.SimpleStore.superclass.constructor.call(this, {
9258         isLocal : true,
9259         reader: new Roo.data.ArrayReader({
9260                 id: config.id
9261             },
9262             Roo.data.Record.create(config.fields)
9263         ),
9264         proxy : new Roo.data.MemoryProxy(config.data)
9265     });
9266     this.load();
9267 };
9268 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9269  * Based on:
9270  * Ext JS Library 1.1.1
9271  * Copyright(c) 2006-2007, Ext JS, LLC.
9272  *
9273  * Originally Released Under LGPL - original licence link has changed is not relivant.
9274  *
9275  * Fork - LGPL
9276  * <script type="text/javascript">
9277  */
9278
9279 /**
9280 /**
9281  * @extends Roo.data.Store
9282  * @class Roo.data.JsonStore
9283  * Small helper class to make creating Stores for JSON data easier. <br/>
9284 <pre><code>
9285 var store = new Roo.data.JsonStore({
9286     url: 'get-images.php',
9287     root: 'images',
9288     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9289 });
9290 </code></pre>
9291  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9292  * JsonReader and HttpProxy (unless inline data is provided).</b>
9293  * @cfg {Array} fields An array of field definition objects, or field name strings.
9294  * @constructor
9295  * @param {Object} config
9296  */
9297 Roo.data.JsonStore = function(c){
9298     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9299         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9300         reader: new Roo.data.JsonReader(c, c.fields)
9301     }));
9302 };
9303 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9304  * Based on:
9305  * Ext JS Library 1.1.1
9306  * Copyright(c) 2006-2007, Ext JS, LLC.
9307  *
9308  * Originally Released Under LGPL - original licence link has changed is not relivant.
9309  *
9310  * Fork - LGPL
9311  * <script type="text/javascript">
9312  */
9313
9314  
9315 Roo.data.Field = function(config){
9316     if(typeof config == "string"){
9317         config = {name: config};
9318     }
9319     Roo.apply(this, config);
9320     
9321     if(!this.type){
9322         this.type = "auto";
9323     }
9324     
9325     var st = Roo.data.SortTypes;
9326     // named sortTypes are supported, here we look them up
9327     if(typeof this.sortType == "string"){
9328         this.sortType = st[this.sortType];
9329     }
9330     
9331     // set default sortType for strings and dates
9332     if(!this.sortType){
9333         switch(this.type){
9334             case "string":
9335                 this.sortType = st.asUCString;
9336                 break;
9337             case "date":
9338                 this.sortType = st.asDate;
9339                 break;
9340             default:
9341                 this.sortType = st.none;
9342         }
9343     }
9344
9345     // define once
9346     var stripRe = /[\$,%]/g;
9347
9348     // prebuilt conversion function for this field, instead of
9349     // switching every time we're reading a value
9350     if(!this.convert){
9351         var cv, dateFormat = this.dateFormat;
9352         switch(this.type){
9353             case "":
9354             case "auto":
9355             case undefined:
9356                 cv = function(v){ return v; };
9357                 break;
9358             case "string":
9359                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9360                 break;
9361             case "int":
9362                 cv = function(v){
9363                     return v !== undefined && v !== null && v !== '' ?
9364                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9365                     };
9366                 break;
9367             case "float":
9368                 cv = function(v){
9369                     return v !== undefined && v !== null && v !== '' ?
9370                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9371                     };
9372                 break;
9373             case "bool":
9374             case "boolean":
9375                 cv = function(v){ return v === true || v === "true" || v == 1; };
9376                 break;
9377             case "date":
9378                 cv = function(v){
9379                     if(!v){
9380                         return '';
9381                     }
9382                     if(v instanceof Date){
9383                         return v;
9384                     }
9385                     if(dateFormat){
9386                         if(dateFormat == "timestamp"){
9387                             return new Date(v*1000);
9388                         }
9389                         return Date.parseDate(v, dateFormat);
9390                     }
9391                     var parsed = Date.parse(v);
9392                     return parsed ? new Date(parsed) : null;
9393                 };
9394              break;
9395             
9396         }
9397         this.convert = cv;
9398     }
9399 };
9400
9401 Roo.data.Field.prototype = {
9402     dateFormat: null,
9403     defaultValue: "",
9404     mapping: null,
9405     sortType : null,
9406     sortDir : "ASC"
9407 };/*
9408  * Based on:
9409  * Ext JS Library 1.1.1
9410  * Copyright(c) 2006-2007, Ext JS, LLC.
9411  *
9412  * Originally Released Under LGPL - original licence link has changed is not relivant.
9413  *
9414  * Fork - LGPL
9415  * <script type="text/javascript">
9416  */
9417  
9418 // Base class for reading structured data from a data source.  This class is intended to be
9419 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9420
9421 /**
9422  * @class Roo.data.DataReader
9423  * Base class for reading structured data from a data source.  This class is intended to be
9424  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9425  */
9426
9427 Roo.data.DataReader = function(meta, recordType){
9428     
9429     this.meta = meta;
9430     
9431     this.recordType = recordType instanceof Array ? 
9432         Roo.data.Record.create(recordType) : recordType;
9433 };
9434
9435 Roo.data.DataReader.prototype = {
9436      /**
9437      * Create an empty record
9438      * @param {Object} data (optional) - overlay some values
9439      * @return {Roo.data.Record} record created.
9440      */
9441     newRow :  function(d) {
9442         var da =  {};
9443         this.recordType.prototype.fields.each(function(c) {
9444             switch( c.type) {
9445                 case 'int' : da[c.name] = 0; break;
9446                 case 'date' : da[c.name] = new Date(); break;
9447                 case 'float' : da[c.name] = 0.0; break;
9448                 case 'boolean' : da[c.name] = false; break;
9449                 default : da[c.name] = ""; break;
9450             }
9451             
9452         });
9453         return new this.recordType(Roo.apply(da, d));
9454     }
9455     
9456 };/*
9457  * Based on:
9458  * Ext JS Library 1.1.1
9459  * Copyright(c) 2006-2007, Ext JS, LLC.
9460  *
9461  * Originally Released Under LGPL - original licence link has changed is not relivant.
9462  *
9463  * Fork - LGPL
9464  * <script type="text/javascript">
9465  */
9466
9467 /**
9468  * @class Roo.data.DataProxy
9469  * @extends Roo.data.Observable
9470  * This class is an abstract base class for implementations which provide retrieval of
9471  * unformatted data objects.<br>
9472  * <p>
9473  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9474  * (of the appropriate type which knows how to parse the data object) to provide a block of
9475  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9476  * <p>
9477  * Custom implementations must implement the load method as described in
9478  * {@link Roo.data.HttpProxy#load}.
9479  */
9480 Roo.data.DataProxy = function(){
9481     this.addEvents({
9482         /**
9483          * @event beforeload
9484          * Fires before a network request is made to retrieve a data object.
9485          * @param {Object} This DataProxy object.
9486          * @param {Object} params The params parameter to the load function.
9487          */
9488         beforeload : true,
9489         /**
9490          * @event load
9491          * Fires before the load method's callback is called.
9492          * @param {Object} This DataProxy object.
9493          * @param {Object} o The data object.
9494          * @param {Object} arg The callback argument object passed to the load function.
9495          */
9496         load : true,
9497         /**
9498          * @event loadexception
9499          * Fires if an Exception occurs during data retrieval.
9500          * @param {Object} This DataProxy object.
9501          * @param {Object} o The data object.
9502          * @param {Object} arg The callback argument object passed to the load function.
9503          * @param {Object} e The Exception.
9504          */
9505         loadexception : true
9506     });
9507     Roo.data.DataProxy.superclass.constructor.call(this);
9508 };
9509
9510 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9511
9512     /**
9513      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9514      */
9515 /*
9516  * Based on:
9517  * Ext JS Library 1.1.1
9518  * Copyright(c) 2006-2007, Ext JS, LLC.
9519  *
9520  * Originally Released Under LGPL - original licence link has changed is not relivant.
9521  *
9522  * Fork - LGPL
9523  * <script type="text/javascript">
9524  */
9525 /**
9526  * @class Roo.data.MemoryProxy
9527  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9528  * to the Reader when its load method is called.
9529  * @constructor
9530  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9531  */
9532 Roo.data.MemoryProxy = function(data){
9533     if (data.data) {
9534         data = data.data;
9535     }
9536     Roo.data.MemoryProxy.superclass.constructor.call(this);
9537     this.data = data;
9538 };
9539
9540 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9541     /**
9542      * Load data from the requested source (in this case an in-memory
9543      * data object passed to the constructor), read the data object into
9544      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9545      * process that block using the passed callback.
9546      * @param {Object} params This parameter is not used by the MemoryProxy class.
9547      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9548      * object into a block of Roo.data.Records.
9549      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9550      * The function must be passed <ul>
9551      * <li>The Record block object</li>
9552      * <li>The "arg" argument from the load function</li>
9553      * <li>A boolean success indicator</li>
9554      * </ul>
9555      * @param {Object} scope The scope in which to call the callback
9556      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9557      */
9558     load : function(params, reader, callback, scope, arg){
9559         params = params || {};
9560         var result;
9561         try {
9562             result = reader.readRecords(this.data);
9563         }catch(e){
9564             this.fireEvent("loadexception", this, arg, null, e);
9565             callback.call(scope, null, arg, false);
9566             return;
9567         }
9568         callback.call(scope, result, arg, true);
9569     },
9570     
9571     // private
9572     update : function(params, records){
9573         
9574     }
9575 });/*
9576  * Based on:
9577  * Ext JS Library 1.1.1
9578  * Copyright(c) 2006-2007, Ext JS, LLC.
9579  *
9580  * Originally Released Under LGPL - original licence link has changed is not relivant.
9581  *
9582  * Fork - LGPL
9583  * <script type="text/javascript">
9584  */
9585 /**
9586  * @class Roo.data.HttpProxy
9587  * @extends Roo.data.DataProxy
9588  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9589  * configured to reference a certain URL.<br><br>
9590  * <p>
9591  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9592  * from which the running page was served.<br><br>
9593  * <p>
9594  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9595  * <p>
9596  * Be aware that to enable the browser to parse an XML document, the server must set
9597  * the Content-Type header in the HTTP response to "text/xml".
9598  * @constructor
9599  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9600  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9601  * will be used to make the request.
9602  */
9603 Roo.data.HttpProxy = function(conn){
9604     Roo.data.HttpProxy.superclass.constructor.call(this);
9605     // is conn a conn config or a real conn?
9606     this.conn = conn;
9607     this.useAjax = !conn || !conn.events;
9608   
9609 };
9610
9611 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9612     // thse are take from connection...
9613     
9614     /**
9615      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9616      */
9617     /**
9618      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9619      * extra parameters to each request made by this object. (defaults to undefined)
9620      */
9621     /**
9622      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9623      *  to each request made by this object. (defaults to undefined)
9624      */
9625     /**
9626      * @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)
9627      */
9628     /**
9629      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9630      */
9631      /**
9632      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9633      * @type Boolean
9634      */
9635   
9636
9637     /**
9638      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9639      * @type Boolean
9640      */
9641     /**
9642      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9643      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9644      * a finer-grained basis than the DataProxy events.
9645      */
9646     getConnection : function(){
9647         return this.useAjax ? Roo.Ajax : this.conn;
9648     },
9649
9650     /**
9651      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9652      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9653      * process that block using the passed callback.
9654      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9655      * for the request to the remote server.
9656      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9657      * object into a block of Roo.data.Records.
9658      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9659      * The function must be passed <ul>
9660      * <li>The Record block object</li>
9661      * <li>The "arg" argument from the load function</li>
9662      * <li>A boolean success indicator</li>
9663      * </ul>
9664      * @param {Object} scope The scope in which to call the callback
9665      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9666      */
9667     load : function(params, reader, callback, scope, arg){
9668         if(this.fireEvent("beforeload", this, params) !== false){
9669             var  o = {
9670                 params : params || {},
9671                 request: {
9672                     callback : callback,
9673                     scope : scope,
9674                     arg : arg
9675                 },
9676                 reader: reader,
9677                 callback : this.loadResponse,
9678                 scope: this
9679             };
9680             if(this.useAjax){
9681                 Roo.applyIf(o, this.conn);
9682                 if(this.activeRequest){
9683                     Roo.Ajax.abort(this.activeRequest);
9684                 }
9685                 this.activeRequest = Roo.Ajax.request(o);
9686             }else{
9687                 this.conn.request(o);
9688             }
9689         }else{
9690             callback.call(scope||this, null, arg, false);
9691         }
9692     },
9693
9694     // private
9695     loadResponse : function(o, success, response){
9696         delete this.activeRequest;
9697         if(!success){
9698             this.fireEvent("loadexception", this, o, response);
9699             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9700             return;
9701         }
9702         var result;
9703         try {
9704             result = o.reader.read(response);
9705         }catch(e){
9706             this.fireEvent("loadexception", this, o, response, e);
9707             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9708             return;
9709         }
9710         
9711         this.fireEvent("load", this, o, o.request.arg);
9712         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9713     },
9714
9715     // private
9716     update : function(dataSet){
9717
9718     },
9719
9720     // private
9721     updateResponse : function(dataSet){
9722
9723     }
9724 });/*
9725  * Based on:
9726  * Ext JS Library 1.1.1
9727  * Copyright(c) 2006-2007, Ext JS, LLC.
9728  *
9729  * Originally Released Under LGPL - original licence link has changed is not relivant.
9730  *
9731  * Fork - LGPL
9732  * <script type="text/javascript">
9733  */
9734
9735 /**
9736  * @class Roo.data.ScriptTagProxy
9737  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9738  * other than the originating domain of the running page.<br><br>
9739  * <p>
9740  * <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
9741  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9742  * <p>
9743  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9744  * source code that is used as the source inside a &lt;script> tag.<br><br>
9745  * <p>
9746  * In order for the browser to process the returned data, the server must wrap the data object
9747  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9748  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9749  * depending on whether the callback name was passed:
9750  * <p>
9751  * <pre><code>
9752 boolean scriptTag = false;
9753 String cb = request.getParameter("callback");
9754 if (cb != null) {
9755     scriptTag = true;
9756     response.setContentType("text/javascript");
9757 } else {
9758     response.setContentType("application/x-json");
9759 }
9760 Writer out = response.getWriter();
9761 if (scriptTag) {
9762     out.write(cb + "(");
9763 }
9764 out.print(dataBlock.toJsonString());
9765 if (scriptTag) {
9766     out.write(");");
9767 }
9768 </pre></code>
9769  *
9770  * @constructor
9771  * @param {Object} config A configuration object.
9772  */
9773 Roo.data.ScriptTagProxy = function(config){
9774     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9775     Roo.apply(this, config);
9776     this.head = document.getElementsByTagName("head")[0];
9777 };
9778
9779 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9780
9781 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9782     /**
9783      * @cfg {String} url The URL from which to request the data object.
9784      */
9785     /**
9786      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9787      */
9788     timeout : 30000,
9789     /**
9790      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9791      * the server the name of the callback function set up by the load call to process the returned data object.
9792      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9793      * javascript output which calls this named function passing the data object as its only parameter.
9794      */
9795     callbackParam : "callback",
9796     /**
9797      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9798      * name to the request.
9799      */
9800     nocache : true,
9801
9802     /**
9803      * Load data from the configured URL, read the data object into
9804      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9805      * process that block using the passed callback.
9806      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9807      * for the request to the remote server.
9808      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9809      * object into a block of Roo.data.Records.
9810      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9811      * The function must be passed <ul>
9812      * <li>The Record block object</li>
9813      * <li>The "arg" argument from the load function</li>
9814      * <li>A boolean success indicator</li>
9815      * </ul>
9816      * @param {Object} scope The scope in which to call the callback
9817      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9818      */
9819     load : function(params, reader, callback, scope, arg){
9820         if(this.fireEvent("beforeload", this, params) !== false){
9821
9822             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9823
9824             var url = this.url;
9825             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9826             if(this.nocache){
9827                 url += "&_dc=" + (new Date().getTime());
9828             }
9829             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9830             var trans = {
9831                 id : transId,
9832                 cb : "stcCallback"+transId,
9833                 scriptId : "stcScript"+transId,
9834                 params : params,
9835                 arg : arg,
9836                 url : url,
9837                 callback : callback,
9838                 scope : scope,
9839                 reader : reader
9840             };
9841             var conn = this;
9842
9843             window[trans.cb] = function(o){
9844                 conn.handleResponse(o, trans);
9845             };
9846
9847             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9848
9849             if(this.autoAbort !== false){
9850                 this.abort();
9851             }
9852
9853             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9854
9855             var script = document.createElement("script");
9856             script.setAttribute("src", url);
9857             script.setAttribute("type", "text/javascript");
9858             script.setAttribute("id", trans.scriptId);
9859             this.head.appendChild(script);
9860
9861             this.trans = trans;
9862         }else{
9863             callback.call(scope||this, null, arg, false);
9864         }
9865     },
9866
9867     // private
9868     isLoading : function(){
9869         return this.trans ? true : false;
9870     },
9871
9872     /**
9873      * Abort the current server request.
9874      */
9875     abort : function(){
9876         if(this.isLoading()){
9877             this.destroyTrans(this.trans);
9878         }
9879     },
9880
9881     // private
9882     destroyTrans : function(trans, isLoaded){
9883         this.head.removeChild(document.getElementById(trans.scriptId));
9884         clearTimeout(trans.timeoutId);
9885         if(isLoaded){
9886             window[trans.cb] = undefined;
9887             try{
9888                 delete window[trans.cb];
9889             }catch(e){}
9890         }else{
9891             // if hasn't been loaded, wait for load to remove it to prevent script error
9892             window[trans.cb] = function(){
9893                 window[trans.cb] = undefined;
9894                 try{
9895                     delete window[trans.cb];
9896                 }catch(e){}
9897             };
9898         }
9899     },
9900
9901     // private
9902     handleResponse : function(o, trans){
9903         this.trans = false;
9904         this.destroyTrans(trans, true);
9905         var result;
9906         try {
9907             result = trans.reader.readRecords(o);
9908         }catch(e){
9909             this.fireEvent("loadexception", this, o, trans.arg, e);
9910             trans.callback.call(trans.scope||window, null, trans.arg, false);
9911             return;
9912         }
9913         this.fireEvent("load", this, o, trans.arg);
9914         trans.callback.call(trans.scope||window, result, trans.arg, true);
9915     },
9916
9917     // private
9918     handleFailure : function(trans){
9919         this.trans = false;
9920         this.destroyTrans(trans, false);
9921         this.fireEvent("loadexception", this, null, trans.arg);
9922         trans.callback.call(trans.scope||window, null, trans.arg, false);
9923     }
9924 });/*
9925  * Based on:
9926  * Ext JS Library 1.1.1
9927  * Copyright(c) 2006-2007, Ext JS, LLC.
9928  *
9929  * Originally Released Under LGPL - original licence link has changed is not relivant.
9930  *
9931  * Fork - LGPL
9932  * <script type="text/javascript">
9933  */
9934
9935 /**
9936  * @class Roo.data.JsonReader
9937  * @extends Roo.data.DataReader
9938  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9939  * based on mappings in a provided Roo.data.Record constructor.
9940  * 
9941  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9942  * in the reply previously. 
9943  * 
9944  * <p>
9945  * Example code:
9946  * <pre><code>
9947 var RecordDef = Roo.data.Record.create([
9948     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9949     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9950 ]);
9951 var myReader = new Roo.data.JsonReader({
9952     totalProperty: "results",    // The property which contains the total dataset size (optional)
9953     root: "rows",                // The property which contains an Array of row objects
9954     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9955 }, RecordDef);
9956 </code></pre>
9957  * <p>
9958  * This would consume a JSON file like this:
9959  * <pre><code>
9960 { 'results': 2, 'rows': [
9961     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9962     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9963 }
9964 </code></pre>
9965  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9966  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9967  * paged from the remote server.
9968  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9969  * @cfg {String} root name of the property which contains the Array of row objects.
9970  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9971  * @constructor
9972  * Create a new JsonReader
9973  * @param {Object} meta Metadata configuration options
9974  * @param {Object} recordType Either an Array of field definition objects,
9975  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9976  */
9977 Roo.data.JsonReader = function(meta, recordType){
9978     
9979     meta = meta || {};
9980     // set some defaults:
9981     Roo.applyIf(meta, {
9982         totalProperty: 'total',
9983         successProperty : 'success',
9984         root : 'data',
9985         id : 'id'
9986     });
9987     
9988     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9989 };
9990 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9991     
9992     /**
9993      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9994      * Used by Store query builder to append _requestMeta to params.
9995      * 
9996      */
9997     metaFromRemote : false,
9998     /**
9999      * This method is only used by a DataProxy which has retrieved data from a remote server.
10000      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10001      * @return {Object} data A data block which is used by an Roo.data.Store object as
10002      * a cache of Roo.data.Records.
10003      */
10004     read : function(response){
10005         var json = response.responseText;
10006        
10007         var o = /* eval:var:o */ eval("("+json+")");
10008         if(!o) {
10009             throw {message: "JsonReader.read: Json object not found"};
10010         }
10011         
10012         if(o.metaData){
10013             
10014             delete this.ef;
10015             this.metaFromRemote = true;
10016             this.meta = o.metaData;
10017             this.recordType = Roo.data.Record.create(o.metaData.fields);
10018             this.onMetaChange(this.meta, this.recordType, o);
10019         }
10020         return this.readRecords(o);
10021     },
10022
10023     // private function a store will implement
10024     onMetaChange : function(meta, recordType, o){
10025
10026     },
10027
10028     /**
10029          * @ignore
10030          */
10031     simpleAccess: function(obj, subsc) {
10032         return obj[subsc];
10033     },
10034
10035         /**
10036          * @ignore
10037          */
10038     getJsonAccessor: function(){
10039         var re = /[\[\.]/;
10040         return function(expr) {
10041             try {
10042                 return(re.test(expr))
10043                     ? new Function("obj", "return obj." + expr)
10044                     : function(obj){
10045                         return obj[expr];
10046                     };
10047             } catch(e){}
10048             return Roo.emptyFn;
10049         };
10050     }(),
10051
10052     /**
10053      * Create a data block containing Roo.data.Records from an XML document.
10054      * @param {Object} o An object which contains an Array of row objects in the property specified
10055      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10056      * which contains the total size of the dataset.
10057      * @return {Object} data A data block which is used by an Roo.data.Store object as
10058      * a cache of Roo.data.Records.
10059      */
10060     readRecords : function(o){
10061         /**
10062          * After any data loads, the raw JSON data is available for further custom processing.
10063          * @type Object
10064          */
10065         this.o = o;
10066         var s = this.meta, Record = this.recordType,
10067             f = Record.prototype.fields, fi = f.items, fl = f.length;
10068
10069 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10070         if (!this.ef) {
10071             if(s.totalProperty) {
10072                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10073                 }
10074                 if(s.successProperty) {
10075                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10076                 }
10077                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10078                 if (s.id) {
10079                         var g = this.getJsonAccessor(s.id);
10080                         this.getId = function(rec) {
10081                                 var r = g(rec);
10082                                 return (r === undefined || r === "") ? null : r;
10083                         };
10084                 } else {
10085                         this.getId = function(){return null;};
10086                 }
10087             this.ef = [];
10088             for(var jj = 0; jj < fl; jj++){
10089                 f = fi[jj];
10090                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10091                 this.ef[jj] = this.getJsonAccessor(map);
10092             }
10093         }
10094
10095         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10096         if(s.totalProperty){
10097             var vt = parseInt(this.getTotal(o), 10);
10098             if(!isNaN(vt)){
10099                 totalRecords = vt;
10100             }
10101         }
10102         if(s.successProperty){
10103             var vs = this.getSuccess(o);
10104             if(vs === false || vs === 'false'){
10105                 success = false;
10106             }
10107         }
10108         var records = [];
10109             for(var i = 0; i < c; i++){
10110                     var n = root[i];
10111                 var values = {};
10112                 var id = this.getId(n);
10113                 for(var j = 0; j < fl; j++){
10114                     f = fi[j];
10115                 var v = this.ef[j](n);
10116                 if (!f.convert) {
10117                     Roo.log('missing convert for ' + f.name);
10118                     Roo.log(f);
10119                     continue;
10120                 }
10121                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10122                 }
10123                 var record = new Record(values, id);
10124                 record.json = n;
10125                 records[i] = record;
10126             }
10127             return {
10128             raw : o,
10129                 success : success,
10130                 records : records,
10131                 totalRecords : totalRecords
10132             };
10133     }
10134 });/*
10135  * Based on:
10136  * Ext JS Library 1.1.1
10137  * Copyright(c) 2006-2007, Ext JS, LLC.
10138  *
10139  * Originally Released Under LGPL - original licence link has changed is not relivant.
10140  *
10141  * Fork - LGPL
10142  * <script type="text/javascript">
10143  */
10144
10145 /**
10146  * @class Roo.data.ArrayReader
10147  * @extends Roo.data.DataReader
10148  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10149  * Each element of that Array represents a row of data fields. The
10150  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10151  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10152  * <p>
10153  * Example code:.
10154  * <pre><code>
10155 var RecordDef = Roo.data.Record.create([
10156     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10157     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10158 ]);
10159 var myReader = new Roo.data.ArrayReader({
10160     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10161 }, RecordDef);
10162 </code></pre>
10163  * <p>
10164  * This would consume an Array like this:
10165  * <pre><code>
10166 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10167   </code></pre>
10168  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10169  * @constructor
10170  * Create a new JsonReader
10171  * @param {Object} meta Metadata configuration options.
10172  * @param {Object} recordType Either an Array of field definition objects
10173  * as specified to {@link Roo.data.Record#create},
10174  * or an {@link Roo.data.Record} object
10175  * created using {@link Roo.data.Record#create}.
10176  */
10177 Roo.data.ArrayReader = function(meta, recordType){
10178     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10179 };
10180
10181 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10182     /**
10183      * Create a data block containing Roo.data.Records from an XML document.
10184      * @param {Object} o An Array of row objects which represents the dataset.
10185      * @return {Object} data A data block which is used by an Roo.data.Store object as
10186      * a cache of Roo.data.Records.
10187      */
10188     readRecords : function(o){
10189         var sid = this.meta ? this.meta.id : null;
10190         var recordType = this.recordType, fields = recordType.prototype.fields;
10191         var records = [];
10192         var root = o;
10193             for(var i = 0; i < root.length; i++){
10194                     var n = root[i];
10195                 var values = {};
10196                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10197                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10198                 var f = fields.items[j];
10199                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10200                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10201                 v = f.convert(v);
10202                 values[f.name] = v;
10203             }
10204                 var record = new recordType(values, id);
10205                 record.json = n;
10206                 records[records.length] = record;
10207             }
10208             return {
10209                 records : records,
10210                 totalRecords : records.length
10211             };
10212     }
10213 });/*
10214  * - LGPL
10215  * * 
10216  */
10217
10218 /**
10219  * @class Roo.bootstrap.ComboBox
10220  * @extends Roo.bootstrap.TriggerField
10221  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10222  * @cfg {Boolean} append (true|false) default false
10223  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10224  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10225  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10226  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10227  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10228  * @constructor
10229  * Create a new ComboBox.
10230  * @param {Object} config Configuration options
10231  */
10232 Roo.bootstrap.ComboBox = function(config){
10233     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10234     this.addEvents({
10235         /**
10236          * @event expand
10237          * Fires when the dropdown list is expanded
10238              * @param {Roo.bootstrap.ComboBox} combo This combo box
10239              */
10240         'expand' : true,
10241         /**
10242          * @event collapse
10243          * Fires when the dropdown list is collapsed
10244              * @param {Roo.bootstrap.ComboBox} combo This combo box
10245              */
10246         'collapse' : true,
10247         /**
10248          * @event beforeselect
10249          * Fires before a list item is selected. Return false to cancel the selection.
10250              * @param {Roo.bootstrap.ComboBox} combo This combo box
10251              * @param {Roo.data.Record} record The data record returned from the underlying store
10252              * @param {Number} index The index of the selected item in the dropdown list
10253              */
10254         'beforeselect' : true,
10255         /**
10256          * @event select
10257          * Fires when a list item is selected
10258              * @param {Roo.bootstrap.ComboBox} combo This combo box
10259              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10260              * @param {Number} index The index of the selected item in the dropdown list
10261              */
10262         'select' : true,
10263         /**
10264          * @event beforequery
10265          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10266          * The event object passed has these properties:
10267              * @param {Roo.bootstrap.ComboBox} combo This combo box
10268              * @param {String} query The query
10269              * @param {Boolean} forceAll true to force "all" query
10270              * @param {Boolean} cancel true to cancel the query
10271              * @param {Object} e The query event object
10272              */
10273         'beforequery': true,
10274          /**
10275          * @event add
10276          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10277              * @param {Roo.bootstrap.ComboBox} combo This combo box
10278              */
10279         'add' : true,
10280         /**
10281          * @event edit
10282          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10283              * @param {Roo.bootstrap.ComboBox} combo This combo box
10284              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10285              */
10286         'edit' : true,
10287         /**
10288          * @event remove
10289          * Fires when the remove value from the combobox array
10290              * @param {Roo.bootstrap.ComboBox} combo This combo box
10291              */
10292         'remove' : true
10293         
10294     });
10295     
10296     this.item = [];
10297     this.tickItems = [];
10298     
10299     this.selectedIndex = -1;
10300     if(this.mode == 'local'){
10301         if(config.queryDelay === undefined){
10302             this.queryDelay = 10;
10303         }
10304         if(config.minChars === undefined){
10305             this.minChars = 0;
10306         }
10307     }
10308 };
10309
10310 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10311      
10312     /**
10313      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10314      * rendering into an Roo.Editor, defaults to false)
10315      */
10316     /**
10317      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10318      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10319      */
10320     /**
10321      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10322      */
10323     /**
10324      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10325      * the dropdown list (defaults to undefined, with no header element)
10326      */
10327
10328      /**
10329      * @cfg {String/Roo.Template} tpl The template to use to render the output
10330      */
10331      
10332      /**
10333      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10334      */
10335     listWidth: undefined,
10336     /**
10337      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10338      * mode = 'remote' or 'text' if mode = 'local')
10339      */
10340     displayField: undefined,
10341     /**
10342      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10343      * mode = 'remote' or 'value' if mode = 'local'). 
10344      * Note: use of a valueField requires the user make a selection
10345      * in order for a value to be mapped.
10346      */
10347     valueField: undefined,
10348     
10349     
10350     /**
10351      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10352      * field's data value (defaults to the underlying DOM element's name)
10353      */
10354     hiddenName: undefined,
10355     /**
10356      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10357      */
10358     listClass: '',
10359     /**
10360      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10361      */
10362     selectedClass: 'active',
10363     
10364     /**
10365      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10366      */
10367     shadow:'sides',
10368     /**
10369      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10370      * anchor positions (defaults to 'tl-bl')
10371      */
10372     listAlign: 'tl-bl?',
10373     /**
10374      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10375      */
10376     maxHeight: 300,
10377     /**
10378      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10379      * query specified by the allQuery config option (defaults to 'query')
10380      */
10381     triggerAction: 'query',
10382     /**
10383      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10384      * (defaults to 4, does not apply if editable = false)
10385      */
10386     minChars : 4,
10387     /**
10388      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10389      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10390      */
10391     typeAhead: false,
10392     /**
10393      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10394      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10395      */
10396     queryDelay: 500,
10397     /**
10398      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10399      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10400      */
10401     pageSize: 0,
10402     /**
10403      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10404      * when editable = true (defaults to false)
10405      */
10406     selectOnFocus:false,
10407     /**
10408      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10409      */
10410     queryParam: 'query',
10411     /**
10412      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10413      * when mode = 'remote' (defaults to 'Loading...')
10414      */
10415     loadingText: 'Loading...',
10416     /**
10417      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10418      */
10419     resizable: false,
10420     /**
10421      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10422      */
10423     handleHeight : 8,
10424     /**
10425      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10426      * traditional select (defaults to true)
10427      */
10428     editable: true,
10429     /**
10430      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10431      */
10432     allQuery: '',
10433     /**
10434      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10435      */
10436     mode: 'remote',
10437     /**
10438      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10439      * listWidth has a higher value)
10440      */
10441     minListWidth : 70,
10442     /**
10443      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10444      * allow the user to set arbitrary text into the field (defaults to false)
10445      */
10446     forceSelection:false,
10447     /**
10448      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10449      * if typeAhead = true (defaults to 250)
10450      */
10451     typeAheadDelay : 250,
10452     /**
10453      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10454      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10455      */
10456     valueNotFoundText : undefined,
10457     /**
10458      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10459      */
10460     blockFocus : false,
10461     
10462     /**
10463      * @cfg {Boolean} disableClear Disable showing of clear button.
10464      */
10465     disableClear : false,
10466     /**
10467      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10468      */
10469     alwaysQuery : false,
10470     
10471     /**
10472      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10473      */
10474     multiple : false,
10475     
10476     //private
10477     addicon : false,
10478     editicon: false,
10479     
10480     page: 0,
10481     hasQuery: false,
10482     append: false,
10483     loadNext: false,
10484     autoFocus : true,
10485     tickable : false,
10486     btnPosition : 'right',
10487     triggerList : true,
10488     showToggleBtn : true,
10489     // element that contains real text value.. (when hidden is used..)
10490     
10491     getAutoCreate : function()
10492     {
10493         var cfg = false;
10494         
10495         /*
10496          *  Normal ComboBox
10497          */
10498         if(!this.tickable){
10499             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10500             return cfg;
10501         }
10502         
10503         /*
10504          *  ComboBox with tickable selections
10505          */
10506              
10507         var align = this.labelAlign || this.parentLabelAlign();
10508         
10509         cfg = {
10510             cls : 'form-group roo-combobox-tickable' //input-group
10511         };
10512         
10513         
10514         var buttons = {
10515             tag : 'div',
10516             cls : 'tickable-buttons',
10517             cn : [
10518                 {
10519                     tag : 'button',
10520                     type : 'button',
10521                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10522                     html : 'Edit'
10523                 },
10524                 {
10525                     tag : 'button',
10526                     type : 'button',
10527                     name : 'ok',
10528                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10529                     html : 'Done'
10530                 },
10531                 {
10532                     tag : 'button',
10533                     type : 'button',
10534                     name : 'cancel',
10535                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10536                     html : 'Cancel'
10537                 }
10538             ]
10539         };
10540         
10541         var _this = this;
10542         Roo.each(buttons.cn, function(c){
10543             if (_this.size) {
10544                 c.cls += ' btn-' + _this.size;
10545             }
10546
10547             if (_this.disabled) {
10548                 c.disabled = true;
10549             }
10550         });
10551         
10552         var box = {
10553             tag: 'div',
10554             cn: [
10555                 {
10556                     tag: 'input',
10557                     type : 'hidden',
10558                     cls: 'form-hidden-field'
10559                 },
10560                 {
10561                     tag: 'ul',
10562                     cls: 'select2-choices',
10563                     cn:[
10564                         {
10565                             tag: 'li',
10566                             cls: 'select2-search-field',
10567                             cn: [
10568
10569                                 buttons
10570                             ]
10571                         }
10572                     ]
10573                 }
10574             ]
10575         }
10576         
10577         var combobox = {
10578             cls: 'select2-container input-group select2-container-multi',
10579             cn: [
10580                 box
10581 //                {
10582 //                    tag: 'ul',
10583 //                    cls: 'typeahead typeahead-long dropdown-menu',
10584 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10585 //                }
10586             ]
10587         };
10588         
10589         if (align ==='left' && this.fieldLabel.length) {
10590             
10591                 Roo.log("left and has label");
10592                 cfg.cn = [
10593                     
10594                     {
10595                         tag: 'label',
10596                         'for' :  id,
10597                         cls : 'control-label col-sm-' + this.labelWidth,
10598                         html : this.fieldLabel
10599                         
10600                     },
10601                     {
10602                         cls : "col-sm-" + (12 - this.labelWidth), 
10603                         cn: [
10604                             combobox
10605                         ]
10606                     }
10607                     
10608                 ];
10609         } else if ( this.fieldLabel.length) {
10610                 Roo.log(" label");
10611                  cfg.cn = [
10612                    
10613                     {
10614                         tag: 'label',
10615                         //cls : 'input-group-addon',
10616                         html : this.fieldLabel
10617                         
10618                     },
10619                     
10620                     combobox
10621                     
10622                 ];
10623
10624         } else {
10625             
10626                 Roo.log(" no label && no align");
10627                 cfg = combobox
10628                      
10629                 
10630         }
10631          
10632         var settings=this;
10633         ['xs','sm','md','lg'].map(function(size){
10634             if (settings[size]) {
10635                 cfg.cls += ' col-' + size + '-' + settings[size];
10636             }
10637         });
10638         
10639         return cfg;
10640         
10641     },
10642     
10643     // private
10644     initEvents: function()
10645     {
10646         
10647         if (!this.store) {
10648             throw "can not find store for combo";
10649         }
10650         this.store = Roo.factory(this.store, Roo.data);
10651         
10652         if(this.tickable){
10653             this.initTickableEvents();
10654             return;
10655         }
10656         
10657         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10658         
10659         if(this.hiddenName){
10660             
10661             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10662             
10663             this.hiddenField.dom.value =
10664                 this.hiddenValue !== undefined ? this.hiddenValue :
10665                 this.value !== undefined ? this.value : '';
10666
10667             // prevent input submission
10668             this.el.dom.removeAttribute('name');
10669             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10670              
10671              
10672         }
10673         //if(Roo.isGecko){
10674         //    this.el.dom.setAttribute('autocomplete', 'off');
10675         //}
10676         
10677         var cls = 'x-combo-list';
10678         
10679         //this.list = new Roo.Layer({
10680         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10681         //});
10682         
10683         var _this = this;
10684         
10685         (function(){
10686             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10687             _this.list.setWidth(lw);
10688         }).defer(100);
10689         
10690         this.list.on('mouseover', this.onViewOver, this);
10691         this.list.on('mousemove', this.onViewMove, this);
10692         
10693         this.list.on('scroll', this.onViewScroll, this);
10694         
10695         /*
10696         this.list.swallowEvent('mousewheel');
10697         this.assetHeight = 0;
10698
10699         if(this.title){
10700             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10701             this.assetHeight += this.header.getHeight();
10702         }
10703
10704         this.innerList = this.list.createChild({cls:cls+'-inner'});
10705         this.innerList.on('mouseover', this.onViewOver, this);
10706         this.innerList.on('mousemove', this.onViewMove, this);
10707         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10708         
10709         if(this.allowBlank && !this.pageSize && !this.disableClear){
10710             this.footer = this.list.createChild({cls:cls+'-ft'});
10711             this.pageTb = new Roo.Toolbar(this.footer);
10712            
10713         }
10714         if(this.pageSize){
10715             this.footer = this.list.createChild({cls:cls+'-ft'});
10716             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10717                     {pageSize: this.pageSize});
10718             
10719         }
10720         
10721         if (this.pageTb && this.allowBlank && !this.disableClear) {
10722             var _this = this;
10723             this.pageTb.add(new Roo.Toolbar.Fill(), {
10724                 cls: 'x-btn-icon x-btn-clear',
10725                 text: '&#160;',
10726                 handler: function()
10727                 {
10728                     _this.collapse();
10729                     _this.clearValue();
10730                     _this.onSelect(false, -1);
10731                 }
10732             });
10733         }
10734         if (this.footer) {
10735             this.assetHeight += this.footer.getHeight();
10736         }
10737         */
10738             
10739         if(!this.tpl){
10740             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10741         }
10742
10743         this.view = new Roo.View(this.list, this.tpl, {
10744             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10745         });
10746         //this.view.wrapEl.setDisplayed(false);
10747         this.view.on('click', this.onViewClick, this);
10748         
10749         
10750         
10751         this.store.on('beforeload', this.onBeforeLoad, this);
10752         this.store.on('load', this.onLoad, this);
10753         this.store.on('loadexception', this.onLoadException, this);
10754         /*
10755         if(this.resizable){
10756             this.resizer = new Roo.Resizable(this.list,  {
10757                pinned:true, handles:'se'
10758             });
10759             this.resizer.on('resize', function(r, w, h){
10760                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10761                 this.listWidth = w;
10762                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10763                 this.restrictHeight();
10764             }, this);
10765             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10766         }
10767         */
10768         if(!this.editable){
10769             this.editable = true;
10770             this.setEditable(false);
10771         }
10772         
10773         /*
10774         
10775         if (typeof(this.events.add.listeners) != 'undefined') {
10776             
10777             this.addicon = this.wrap.createChild(
10778                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10779        
10780             this.addicon.on('click', function(e) {
10781                 this.fireEvent('add', this);
10782             }, this);
10783         }
10784         if (typeof(this.events.edit.listeners) != 'undefined') {
10785             
10786             this.editicon = this.wrap.createChild(
10787                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10788             if (this.addicon) {
10789                 this.editicon.setStyle('margin-left', '40px');
10790             }
10791             this.editicon.on('click', function(e) {
10792                 
10793                 // we fire even  if inothing is selected..
10794                 this.fireEvent('edit', this, this.lastData );
10795                 
10796             }, this);
10797         }
10798         */
10799         
10800         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10801             "up" : function(e){
10802                 this.inKeyMode = true;
10803                 this.selectPrev();
10804             },
10805
10806             "down" : function(e){
10807                 if(!this.isExpanded()){
10808                     this.onTriggerClick();
10809                 }else{
10810                     this.inKeyMode = true;
10811                     this.selectNext();
10812                 }
10813             },
10814
10815             "enter" : function(e){
10816 //                this.onViewClick();
10817                 //return true;
10818                 this.collapse();
10819                 
10820                 if(this.fireEvent("specialkey", this, e)){
10821                     this.onViewClick(false);
10822                 }
10823                 
10824                 return true;
10825             },
10826
10827             "esc" : function(e){
10828                 this.collapse();
10829             },
10830
10831             "tab" : function(e){
10832                 this.collapse();
10833                 
10834                 if(this.fireEvent("specialkey", this, e)){
10835                     this.onViewClick(false);
10836                 }
10837                 
10838                 return true;
10839             },
10840
10841             scope : this,
10842
10843             doRelay : function(foo, bar, hname){
10844                 if(hname == 'down' || this.scope.isExpanded()){
10845                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10846                 }
10847                 return true;
10848             },
10849
10850             forceKeyDown: true
10851         });
10852         
10853         
10854         this.queryDelay = Math.max(this.queryDelay || 10,
10855                 this.mode == 'local' ? 10 : 250);
10856         
10857         
10858         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10859         
10860         if(this.typeAhead){
10861             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10862         }
10863         if(this.editable !== false){
10864             this.inputEl().on("keyup", this.onKeyUp, this);
10865         }
10866         if(this.forceSelection){
10867             this.inputEl().on('blur', this.doForce, this);
10868         }
10869         
10870         if(this.multiple){
10871             this.choices = this.el.select('ul.select2-choices', true).first();
10872             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10873         }
10874     },
10875     
10876     initTickableEvents: function()
10877     {   
10878         this.createList();
10879         
10880         if(this.hiddenName){
10881             
10882             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10883             
10884             this.hiddenField.dom.value =
10885                 this.hiddenValue !== undefined ? this.hiddenValue :
10886                 this.value !== undefined ? this.value : '';
10887
10888             // prevent input submission
10889             this.el.dom.removeAttribute('name');
10890             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10891              
10892              
10893         }
10894         
10895 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10896         
10897         this.choices = this.el.select('ul.select2-choices', true).first();
10898         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10899         if(this.triggerList){
10900             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10901         }
10902          
10903         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10904         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10905         
10906         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10907         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10908         
10909         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10910         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10911         
10912         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10913         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10914         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10915         
10916         this.okBtn.hide();
10917         this.cancelBtn.hide();
10918         
10919         var _this = this;
10920         
10921         (function(){
10922             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10923             _this.list.setWidth(lw);
10924         }).defer(100);
10925         
10926         this.list.on('mouseover', this.onViewOver, this);
10927         this.list.on('mousemove', this.onViewMove, this);
10928         
10929         this.list.on('scroll', this.onViewScroll, this);
10930         
10931         if(!this.tpl){
10932             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>';
10933         }
10934
10935         this.view = new Roo.View(this.list, this.tpl, {
10936             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10937         });
10938         
10939         //this.view.wrapEl.setDisplayed(false);
10940         this.view.on('click', this.onViewClick, this);
10941         
10942         
10943         
10944         this.store.on('beforeload', this.onBeforeLoad, this);
10945         this.store.on('load', this.onLoad, this);
10946         this.store.on('loadexception', this.onLoadException, this);
10947         
10948 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10949 //            "up" : function(e){
10950 //                this.inKeyMode = true;
10951 //                this.selectPrev();
10952 //            },
10953 //
10954 //            "down" : function(e){
10955 //                if(!this.isExpanded()){
10956 //                    this.onTriggerClick();
10957 //                }else{
10958 //                    this.inKeyMode = true;
10959 //                    this.selectNext();
10960 //                }
10961 //            },
10962 //
10963 //            "enter" : function(e){
10964 ////                this.onViewClick();
10965 //                //return true;
10966 //                this.collapse();
10967 //                
10968 //                if(this.fireEvent("specialkey", this, e)){
10969 //                    this.onViewClick(false);
10970 //                }
10971 //                
10972 //                return true;
10973 //            },
10974 //
10975 //            "esc" : function(e){
10976 //                this.collapse();
10977 //            },
10978 //
10979 //            "tab" : function(e){
10980 //                this.collapse();
10981 //                
10982 //                if(this.fireEvent("specialkey", this, e)){
10983 //                    this.onViewClick(false);
10984 //                }
10985 //                
10986 //                return true;
10987 //            },
10988 //
10989 //            scope : this,
10990 //
10991 //            doRelay : function(foo, bar, hname){
10992 //                if(hname == 'down' || this.scope.isExpanded()){
10993 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10994 //                }
10995 //                return true;
10996 //            },
10997 //
10998 //            forceKeyDown: true
10999 //        });
11000         
11001         
11002         this.queryDelay = Math.max(this.queryDelay || 10,
11003                 this.mode == 'local' ? 10 : 250);
11004         
11005         
11006         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11007         
11008         if(this.typeAhead){
11009             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11010         }
11011     },
11012
11013     onDestroy : function(){
11014         if(this.view){
11015             this.view.setStore(null);
11016             this.view.el.removeAllListeners();
11017             this.view.el.remove();
11018             this.view.purgeListeners();
11019         }
11020         if(this.list){
11021             this.list.dom.innerHTML  = '';
11022         }
11023         
11024         if(this.store){
11025             this.store.un('beforeload', this.onBeforeLoad, this);
11026             this.store.un('load', this.onLoad, this);
11027             this.store.un('loadexception', this.onLoadException, this);
11028         }
11029         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11030     },
11031
11032     // private
11033     fireKey : function(e){
11034         if(e.isNavKeyPress() && !this.list.isVisible()){
11035             this.fireEvent("specialkey", this, e);
11036         }
11037     },
11038
11039     // private
11040     onResize: function(w, h){
11041 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11042 //        
11043 //        if(typeof w != 'number'){
11044 //            // we do not handle it!?!?
11045 //            return;
11046 //        }
11047 //        var tw = this.trigger.getWidth();
11048 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11049 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11050 //        var x = w - tw;
11051 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11052 //            
11053 //        //this.trigger.setStyle('left', x+'px');
11054 //        
11055 //        if(this.list && this.listWidth === undefined){
11056 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11057 //            this.list.setWidth(lw);
11058 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11059 //        }
11060         
11061     
11062         
11063     },
11064
11065     /**
11066      * Allow or prevent the user from directly editing the field text.  If false is passed,
11067      * the user will only be able to select from the items defined in the dropdown list.  This method
11068      * is the runtime equivalent of setting the 'editable' config option at config time.
11069      * @param {Boolean} value True to allow the user to directly edit the field text
11070      */
11071     setEditable : function(value){
11072         if(value == this.editable){
11073             return;
11074         }
11075         this.editable = value;
11076         if(!value){
11077             this.inputEl().dom.setAttribute('readOnly', true);
11078             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11079             this.inputEl().addClass('x-combo-noedit');
11080         }else{
11081             this.inputEl().dom.setAttribute('readOnly', false);
11082             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11083             this.inputEl().removeClass('x-combo-noedit');
11084         }
11085     },
11086
11087     // private
11088     
11089     onBeforeLoad : function(combo,opts){
11090         if(!this.hasFocus){
11091             return;
11092         }
11093          if (!opts.add) {
11094             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11095          }
11096 //        this.restrictHeight();
11097         this.selectedIndex = -1;
11098     },
11099
11100     // private
11101     onLoad : function(){
11102         
11103         this.hasQuery = false;
11104         
11105         if(!this.hasFocus){
11106             return;
11107         }
11108         
11109         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11110             this.loading.hide();
11111         }
11112         
11113         if(this.store.getCount() > 0){
11114             this.expand();
11115 //            this.restrictHeight();
11116             if(this.lastQuery == this.allQuery){
11117                 if(this.editable && !this.tickable){
11118                     this.inputEl().dom.select();
11119                 }
11120                 
11121                 if(
11122                     !this.selectByValue(this.value, true) &&
11123                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11124                     this.store.lastOptions.add != true)
11125                 ){
11126                     this.select(0, true);
11127                 }
11128             }else{
11129                 if(this.autoFocus){
11130                     this.selectNext();
11131                 }
11132                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11133                     this.taTask.delay(this.typeAheadDelay);
11134                 }
11135             }
11136         }else{
11137             this.onEmptyResults();
11138         }
11139         
11140         //this.el.focus();
11141     },
11142     // private
11143     onLoadException : function()
11144     {
11145         this.hasQuery = false;
11146         
11147         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11148             this.loading.hide();
11149         }
11150         
11151         this.collapse();
11152         Roo.log(this.store.reader.jsonData);
11153         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11154             // fixme
11155             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11156         }
11157         
11158         
11159     },
11160     // private
11161     onTypeAhead : function(){
11162         if(this.store.getCount() > 0){
11163             var r = this.store.getAt(0);
11164             var newValue = r.data[this.displayField];
11165             var len = newValue.length;
11166             var selStart = this.getRawValue().length;
11167             
11168             if(selStart != len){
11169                 this.setRawValue(newValue);
11170                 this.selectText(selStart, newValue.length);
11171             }
11172         }
11173     },
11174
11175     // private
11176     onSelect : function(record, index){
11177         
11178         if(this.fireEvent('beforeselect', this, record, index) !== false){
11179         
11180             this.setFromData(index > -1 ? record.data : false);
11181             
11182             this.collapse();
11183             this.fireEvent('select', this, record, index);
11184         }
11185     },
11186
11187     /**
11188      * Returns the currently selected field value or empty string if no value is set.
11189      * @return {String} value The selected value
11190      */
11191     getValue : function(){
11192         
11193         if(this.multiple){
11194             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11195         }
11196         
11197         if(this.valueField){
11198             return typeof this.value != 'undefined' ? this.value : '';
11199         }else{
11200             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11201         }
11202     },
11203
11204     /**
11205      * Clears any text/value currently set in the field
11206      */
11207     clearValue : function(){
11208         if(this.hiddenField){
11209             this.hiddenField.dom.value = '';
11210         }
11211         this.value = '';
11212         this.setRawValue('');
11213         this.lastSelectionText = '';
11214         
11215     },
11216
11217     /**
11218      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11219      * will be displayed in the field.  If the value does not match the data value of an existing item,
11220      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11221      * Otherwise the field will be blank (although the value will still be set).
11222      * @param {String} value The value to match
11223      */
11224     setValue : function(v){
11225         if(this.multiple){
11226             this.syncValue();
11227             return;
11228         }
11229         
11230         var text = v;
11231         if(this.valueField){
11232             var r = this.findRecord(this.valueField, v);
11233             if(r){
11234                 text = r.data[this.displayField];
11235             }else if(this.valueNotFoundText !== undefined){
11236                 text = this.valueNotFoundText;
11237             }
11238         }
11239         this.lastSelectionText = text;
11240         if(this.hiddenField){
11241             this.hiddenField.dom.value = v;
11242         }
11243         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11244         this.value = v;
11245     },
11246     /**
11247      * @property {Object} the last set data for the element
11248      */
11249     
11250     lastData : false,
11251     /**
11252      * Sets the value of the field based on a object which is related to the record format for the store.
11253      * @param {Object} value the value to set as. or false on reset?
11254      */
11255     setFromData : function(o){
11256         
11257         if(this.multiple){
11258             if(typeof o.display_name !== 'string'){
11259                 for(var i=0;i<o.display_name.length;i++){
11260                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11261                 }
11262                 return;
11263             }
11264             this.addItem(o);
11265             return;
11266         }
11267             
11268         var dv = ''; // display value
11269         var vv = ''; // value value..
11270         this.lastData = o;
11271         if (this.displayField) {
11272             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11273         } else {
11274             // this is an error condition!!!
11275             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11276         }
11277         
11278         if(this.valueField){
11279             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11280         }
11281         
11282         if(this.hiddenField){
11283             this.hiddenField.dom.value = vv;
11284             
11285             this.lastSelectionText = dv;
11286             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11287             this.value = vv;
11288             return;
11289         }
11290         // no hidden field.. - we store the value in 'value', but still display
11291         // display field!!!!
11292         this.lastSelectionText = dv;
11293         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11294         this.value = vv;
11295         
11296         
11297     },
11298     // private
11299     reset : function(){
11300         // overridden so that last data is reset..
11301         this.setValue(this.originalValue);
11302         this.clearInvalid();
11303         this.lastData = false;
11304         if (this.view) {
11305             this.view.clearSelections();
11306         }
11307     },
11308     // private
11309     findRecord : function(prop, value){
11310         var record;
11311         if(this.store.getCount() > 0){
11312             this.store.each(function(r){
11313                 if(r.data[prop] == value){
11314                     record = r;
11315                     return false;
11316                 }
11317                 return true;
11318             });
11319         }
11320         return record;
11321     },
11322     
11323     getName: function()
11324     {
11325         // returns hidden if it's set..
11326         if (!this.rendered) {return ''};
11327         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11328         
11329     },
11330     // private
11331     onViewMove : function(e, t){
11332         this.inKeyMode = false;
11333     },
11334
11335     // private
11336     onViewOver : function(e, t){
11337         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11338             return;
11339         }
11340         var item = this.view.findItemFromChild(t);
11341         
11342         if(item){
11343             var index = this.view.indexOf(item);
11344             this.select(index, false);
11345         }
11346     },
11347
11348     // private
11349     onViewClick : function(view, doFocus, el, e)
11350     {
11351         var index = this.view.getSelectedIndexes()[0];
11352         
11353         var r = this.store.getAt(index);
11354         
11355         if(this.tickable){
11356             
11357             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11358                 return;
11359             }
11360             
11361             var rm = false;
11362             var _this = this;
11363             
11364             Roo.each(this.tickItems, function(v,k){
11365                 
11366                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11367                     _this.tickItems.splice(k, 1);
11368                     rm = true;
11369                     return;
11370                 }
11371             })
11372             
11373             if(rm){
11374                 return;
11375             }
11376             
11377             this.tickItems.push(r.data);
11378             return;
11379         }
11380         
11381         if(r){
11382             this.onSelect(r, index);
11383         }
11384         if(doFocus !== false && !this.blockFocus){
11385             this.inputEl().focus();
11386         }
11387     },
11388
11389     // private
11390     restrictHeight : function(){
11391         //this.innerList.dom.style.height = '';
11392         //var inner = this.innerList.dom;
11393         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11394         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11395         //this.list.beginUpdate();
11396         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11397         this.list.alignTo(this.inputEl(), this.listAlign);
11398         this.list.alignTo(this.inputEl(), this.listAlign);
11399         //this.list.endUpdate();
11400     },
11401
11402     // private
11403     onEmptyResults : function(){
11404         this.collapse();
11405     },
11406
11407     /**
11408      * Returns true if the dropdown list is expanded, else false.
11409      */
11410     isExpanded : function(){
11411         return this.list.isVisible();
11412     },
11413
11414     /**
11415      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11416      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11417      * @param {String} value The data value of the item to select
11418      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11419      * selected item if it is not currently in view (defaults to true)
11420      * @return {Boolean} True if the value matched an item in the list, else false
11421      */
11422     selectByValue : function(v, scrollIntoView){
11423         if(v !== undefined && v !== null){
11424             var r = this.findRecord(this.valueField || this.displayField, v);
11425             if(r){
11426                 this.select(this.store.indexOf(r), scrollIntoView);
11427                 return true;
11428             }
11429         }
11430         return false;
11431     },
11432
11433     /**
11434      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11435      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11436      * @param {Number} index The zero-based index of the list item to select
11437      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11438      * selected item if it is not currently in view (defaults to true)
11439      */
11440     select : function(index, scrollIntoView){
11441         this.selectedIndex = index;
11442         this.view.select(index);
11443         if(scrollIntoView !== false){
11444             var el = this.view.getNode(index);
11445             if(el && !this.multiple && !this.tickable){
11446                 this.list.scrollChildIntoView(el, false);
11447             }
11448         }
11449     },
11450
11451     // private
11452     selectNext : function(){
11453         var ct = this.store.getCount();
11454         if(ct > 0){
11455             if(this.selectedIndex == -1){
11456                 this.select(0);
11457             }else if(this.selectedIndex < ct-1){
11458                 this.select(this.selectedIndex+1);
11459             }
11460         }
11461     },
11462
11463     // private
11464     selectPrev : function(){
11465         var ct = this.store.getCount();
11466         if(ct > 0){
11467             if(this.selectedIndex == -1){
11468                 this.select(0);
11469             }else if(this.selectedIndex != 0){
11470                 this.select(this.selectedIndex-1);
11471             }
11472         }
11473     },
11474
11475     // private
11476     onKeyUp : function(e){
11477         if(this.editable !== false && !e.isSpecialKey()){
11478             this.lastKey = e.getKey();
11479             this.dqTask.delay(this.queryDelay);
11480         }
11481     },
11482
11483     // private
11484     validateBlur : function(){
11485         return !this.list || !this.list.isVisible();   
11486     },
11487
11488     // private
11489     initQuery : function(){
11490         this.doQuery(this.getRawValue());
11491     },
11492
11493     // private
11494     doForce : function(){
11495         if(this.inputEl().dom.value.length > 0){
11496             this.inputEl().dom.value =
11497                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11498              
11499         }
11500     },
11501
11502     /**
11503      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11504      * query allowing the query action to be canceled if needed.
11505      * @param {String} query The SQL query to execute
11506      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11507      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11508      * saved in the current store (defaults to false)
11509      */
11510     doQuery : function(q, forceAll){
11511         
11512         if(q === undefined || q === null){
11513             q = '';
11514         }
11515         var qe = {
11516             query: q,
11517             forceAll: forceAll,
11518             combo: this,
11519             cancel:false
11520         };
11521         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11522             return false;
11523         }
11524         q = qe.query;
11525         
11526         forceAll = qe.forceAll;
11527         if(forceAll === true || (q.length >= this.minChars)){
11528             
11529             this.hasQuery = true;
11530             
11531             if(this.lastQuery != q || this.alwaysQuery){
11532                 this.lastQuery = q;
11533                 if(this.mode == 'local'){
11534                     this.selectedIndex = -1;
11535                     if(forceAll){
11536                         this.store.clearFilter();
11537                     }else{
11538                         this.store.filter(this.displayField, q);
11539                     }
11540                     this.onLoad();
11541                 }else{
11542                     this.store.baseParams[this.queryParam] = q;
11543                     
11544                     var options = {params : this.getParams(q)};
11545                     
11546                     if(this.loadNext){
11547                         options.add = true;
11548                         options.params.start = this.page * this.pageSize;
11549                     }
11550                     
11551                     this.store.load(options);
11552                     /*
11553                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11554                      *  we should expand the list on onLoad
11555                      *  so command out it
11556                      */
11557 //                    this.expand();
11558                 }
11559             }else{
11560                 this.selectedIndex = -1;
11561                 this.onLoad();   
11562             }
11563         }
11564         
11565         this.loadNext = false;
11566     },
11567
11568     // private
11569     getParams : function(q){
11570         var p = {};
11571         //p[this.queryParam] = q;
11572         
11573         if(this.pageSize){
11574             p.start = 0;
11575             p.limit = this.pageSize;
11576         }
11577         return p;
11578     },
11579
11580     /**
11581      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11582      */
11583     collapse : function(){
11584         if(!this.isExpanded()){
11585             return;
11586         }
11587         
11588         this.list.hide();
11589         
11590         if(this.tickable){
11591             this.okBtn.hide();
11592             this.cancelBtn.hide();
11593             this.trigger.show();
11594         }
11595         
11596         Roo.get(document).un('mousedown', this.collapseIf, this);
11597         Roo.get(document).un('mousewheel', this.collapseIf, this);
11598         if (!this.editable) {
11599             Roo.get(document).un('keydown', this.listKeyPress, this);
11600         }
11601         this.fireEvent('collapse', this);
11602     },
11603
11604     // private
11605     collapseIf : function(e){
11606         var in_combo  = e.within(this.el);
11607         var in_list =  e.within(this.list);
11608         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11609         
11610         if (in_combo || in_list || is_list) {
11611             //e.stopPropagation();
11612             return;
11613         }
11614         
11615         if(this.tickable){
11616             this.onTickableFooterButtonClick(e, false, false);
11617         }
11618
11619         this.collapse();
11620         
11621     },
11622
11623     /**
11624      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11625      */
11626     expand : function(){
11627        
11628         if(this.isExpanded() || !this.hasFocus){
11629             return;
11630         }
11631         
11632         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11633         this.list.setWidth(lw);
11634         
11635         
11636          Roo.log('expand');
11637         
11638         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11639         this.list.setWidth(lw);
11640             
11641         this.list.show();
11642         
11643         this.restrictHeight();
11644         
11645         if(this.tickable){
11646             
11647             this.tickItems = Roo.apply([], this.item);
11648             
11649             this.okBtn.show();
11650             this.cancelBtn.show();
11651             this.trigger.hide();
11652             
11653         }
11654         
11655         Roo.get(document).on('mousedown', this.collapseIf, this);
11656         Roo.get(document).on('mousewheel', this.collapseIf, this);
11657         if (!this.editable) {
11658             Roo.get(document).on('keydown', this.listKeyPress, this);
11659         }
11660         
11661         this.fireEvent('expand', this);
11662     },
11663
11664     // private
11665     // Implements the default empty TriggerField.onTriggerClick function
11666     onTriggerClick : function(e)
11667     {
11668         Roo.log('trigger click');
11669         
11670         if(this.disabled || !this.triggerList){
11671             return;
11672         }
11673         
11674         this.page = 0;
11675         this.loadNext = false;
11676         
11677         if(this.isExpanded()){
11678             this.collapse();
11679             if (!this.blockFocus) {
11680                 this.inputEl().focus();
11681             }
11682             
11683         }else {
11684             this.hasFocus = true;
11685             if(this.triggerAction == 'all') {
11686                 this.doQuery(this.allQuery, true);
11687             } else {
11688                 this.doQuery(this.getRawValue());
11689             }
11690             if (!this.blockFocus) {
11691                 this.inputEl().focus();
11692             }
11693         }
11694     },
11695     
11696     onTickableTriggerClick : function(e)
11697     {
11698         if(this.disabled){
11699             return;
11700         }
11701         
11702         this.page = 0;
11703         this.loadNext = false;
11704         this.hasFocus = true;
11705         
11706         if(this.triggerAction == 'all') {
11707             this.doQuery(this.allQuery, true);
11708         } else {
11709             this.doQuery(this.getRawValue());
11710         }
11711     },
11712     
11713     onSearchFieldClick : function(e)
11714     {
11715         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11716             return;
11717         }
11718         
11719         this.page = 0;
11720         this.loadNext = false;
11721         this.hasFocus = true;
11722         
11723         if(this.triggerAction == 'all') {
11724             this.doQuery(this.allQuery, true);
11725         } else {
11726             this.doQuery(this.getRawValue());
11727         }
11728     },
11729     
11730     listKeyPress : function(e)
11731     {
11732         //Roo.log('listkeypress');
11733         // scroll to first matching element based on key pres..
11734         if (e.isSpecialKey()) {
11735             return false;
11736         }
11737         var k = String.fromCharCode(e.getKey()).toUpperCase();
11738         //Roo.log(k);
11739         var match  = false;
11740         var csel = this.view.getSelectedNodes();
11741         var cselitem = false;
11742         if (csel.length) {
11743             var ix = this.view.indexOf(csel[0]);
11744             cselitem  = this.store.getAt(ix);
11745             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11746                 cselitem = false;
11747             }
11748             
11749         }
11750         
11751         this.store.each(function(v) { 
11752             if (cselitem) {
11753                 // start at existing selection.
11754                 if (cselitem.id == v.id) {
11755                     cselitem = false;
11756                 }
11757                 return true;
11758             }
11759                 
11760             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11761                 match = this.store.indexOf(v);
11762                 return false;
11763             }
11764             return true;
11765         }, this);
11766         
11767         if (match === false) {
11768             return true; // no more action?
11769         }
11770         // scroll to?
11771         this.view.select(match);
11772         var sn = Roo.get(this.view.getSelectedNodes()[0])
11773         //sn.scrollIntoView(sn.dom.parentNode, false);
11774     },
11775     
11776     onViewScroll : function(e, t){
11777         
11778         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){
11779             return;
11780         }
11781         
11782         this.hasQuery = true;
11783         
11784         this.loading = this.list.select('.loading', true).first();
11785         
11786         if(this.loading === null){
11787             this.list.createChild({
11788                 tag: 'div',
11789                 cls: 'loading select2-more-results select2-active',
11790                 html: 'Loading more results...'
11791             })
11792             
11793             this.loading = this.list.select('.loading', true).first();
11794             
11795             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11796             
11797             this.loading.hide();
11798         }
11799         
11800         this.loading.show();
11801         
11802         var _combo = this;
11803         
11804         this.page++;
11805         this.loadNext = true;
11806         
11807         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11808         
11809         return;
11810     },
11811     
11812     addItem : function(o)
11813     {   
11814         var dv = ''; // display value
11815         
11816         if (this.displayField) {
11817             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11818         } else {
11819             // this is an error condition!!!
11820             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11821         }
11822         
11823         if(!dv.length){
11824             return;
11825         }
11826         
11827         var choice = this.choices.createChild({
11828             tag: 'li',
11829             cls: 'select2-search-choice',
11830             cn: [
11831                 {
11832                     tag: 'div',
11833                     html: dv
11834                 },
11835                 {
11836                     tag: 'a',
11837                     href: '#',
11838                     cls: 'select2-search-choice-close',
11839                     tabindex: '-1'
11840                 }
11841             ]
11842             
11843         }, this.searchField);
11844         
11845         var close = choice.select('a.select2-search-choice-close', true).first()
11846         
11847         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11848         
11849         this.item.push(o);
11850         
11851         this.lastData = o;
11852         
11853         this.syncValue();
11854         
11855         this.inputEl().dom.value = '';
11856         
11857     },
11858     
11859     onRemoveItem : function(e, _self, o)
11860     {
11861         e.preventDefault();
11862         var index = this.item.indexOf(o.data) * 1;
11863         
11864         if( index < 0){
11865             Roo.log('not this item?!');
11866             return;
11867         }
11868         
11869         this.item.splice(index, 1);
11870         o.item.remove();
11871         
11872         this.syncValue();
11873         
11874         this.fireEvent('remove', this, e);
11875         
11876     },
11877     
11878     syncValue : function()
11879     {
11880         if(!this.item.length){
11881             this.clearValue();
11882             return;
11883         }
11884             
11885         var value = [];
11886         var _this = this;
11887         Roo.each(this.item, function(i){
11888             if(_this.valueField){
11889                 value.push(i[_this.valueField]);
11890                 return;
11891             }
11892
11893             value.push(i);
11894         });
11895
11896         this.value = value.join(',');
11897
11898         if(this.hiddenField){
11899             this.hiddenField.dom.value = this.value;
11900         }
11901     },
11902     
11903     clearItem : function()
11904     {
11905         if(!this.multiple){
11906             return;
11907         }
11908         
11909         this.item = [];
11910         
11911         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11912            c.remove();
11913         });
11914         
11915         this.syncValue();
11916     },
11917     
11918     inputEl: function ()
11919     {
11920         if(this.tickable){
11921             return this.searchField;
11922         }
11923         return this.el.select('input.form-control',true).first();
11924     },
11925     
11926     
11927     onTickableFooterButtonClick : function(e, btn, el)
11928     {
11929         e.preventDefault();
11930         
11931         if(btn && btn.name == 'cancel'){
11932             this.tickItems = Roo.apply([], this.item);
11933             this.collapse();
11934             return;
11935         }
11936         
11937         this.clearItem();
11938         
11939         var _this = this;
11940         
11941         Roo.each(this.tickItems, function(o){
11942             _this.addItem(o);
11943         });
11944         
11945         this.collapse();
11946         
11947     }
11948     
11949     
11950
11951     /** 
11952     * @cfg {Boolean} grow 
11953     * @hide 
11954     */
11955     /** 
11956     * @cfg {Number} growMin 
11957     * @hide 
11958     */
11959     /** 
11960     * @cfg {Number} growMax 
11961     * @hide 
11962     */
11963     /**
11964      * @hide
11965      * @method autoSize
11966      */
11967 });
11968 /*
11969  * Based on:
11970  * Ext JS Library 1.1.1
11971  * Copyright(c) 2006-2007, Ext JS, LLC.
11972  *
11973  * Originally Released Under LGPL - original licence link has changed is not relivant.
11974  *
11975  * Fork - LGPL
11976  * <script type="text/javascript">
11977  */
11978
11979 /**
11980  * @class Roo.View
11981  * @extends Roo.util.Observable
11982  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11983  * This class also supports single and multi selection modes. <br>
11984  * Create a data model bound view:
11985  <pre><code>
11986  var store = new Roo.data.Store(...);
11987
11988  var view = new Roo.View({
11989     el : "my-element",
11990     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11991  
11992     singleSelect: true,
11993     selectedClass: "ydataview-selected",
11994     store: store
11995  });
11996
11997  // listen for node click?
11998  view.on("click", function(vw, index, node, e){
11999  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12000  });
12001
12002  // load XML data
12003  dataModel.load("foobar.xml");
12004  </code></pre>
12005  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12006  * <br><br>
12007  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12008  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12009  * 
12010  * Note: old style constructor is still suported (container, template, config)
12011  * 
12012  * @constructor
12013  * Create a new View
12014  * @param {Object} config The config object
12015  * 
12016  */
12017 Roo.View = function(config, depreciated_tpl, depreciated_config){
12018     
12019     this.parent = false;
12020     
12021     if (typeof(depreciated_tpl) == 'undefined') {
12022         // new way.. - universal constructor.
12023         Roo.apply(this, config);
12024         this.el  = Roo.get(this.el);
12025     } else {
12026         // old format..
12027         this.el  = Roo.get(config);
12028         this.tpl = depreciated_tpl;
12029         Roo.apply(this, depreciated_config);
12030     }
12031     this.wrapEl  = this.el.wrap().wrap();
12032     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12033     
12034     
12035     if(typeof(this.tpl) == "string"){
12036         this.tpl = new Roo.Template(this.tpl);
12037     } else {
12038         // support xtype ctors..
12039         this.tpl = new Roo.factory(this.tpl, Roo);
12040     }
12041     
12042     
12043     this.tpl.compile();
12044     
12045     /** @private */
12046     this.addEvents({
12047         /**
12048          * @event beforeclick
12049          * Fires before a click is processed. Returns false to cancel the default action.
12050          * @param {Roo.View} this
12051          * @param {Number} index The index of the target node
12052          * @param {HTMLElement} node The target node
12053          * @param {Roo.EventObject} e The raw event object
12054          */
12055             "beforeclick" : true,
12056         /**
12057          * @event click
12058          * Fires when a template node is clicked.
12059          * @param {Roo.View} this
12060          * @param {Number} index The index of the target node
12061          * @param {HTMLElement} node The target node
12062          * @param {Roo.EventObject} e The raw event object
12063          */
12064             "click" : true,
12065         /**
12066          * @event dblclick
12067          * Fires when a template node is double clicked.
12068          * @param {Roo.View} this
12069          * @param {Number} index The index of the target node
12070          * @param {HTMLElement} node The target node
12071          * @param {Roo.EventObject} e The raw event object
12072          */
12073             "dblclick" : true,
12074         /**
12075          * @event contextmenu
12076          * Fires when a template node is right clicked.
12077          * @param {Roo.View} this
12078          * @param {Number} index The index of the target node
12079          * @param {HTMLElement} node The target node
12080          * @param {Roo.EventObject} e The raw event object
12081          */
12082             "contextmenu" : true,
12083         /**
12084          * @event selectionchange
12085          * Fires when the selected nodes change.
12086          * @param {Roo.View} this
12087          * @param {Array} selections Array of the selected nodes
12088          */
12089             "selectionchange" : true,
12090     
12091         /**
12092          * @event beforeselect
12093          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12094          * @param {Roo.View} this
12095          * @param {HTMLElement} node The node to be selected
12096          * @param {Array} selections Array of currently selected nodes
12097          */
12098             "beforeselect" : true,
12099         /**
12100          * @event preparedata
12101          * Fires on every row to render, to allow you to change the data.
12102          * @param {Roo.View} this
12103          * @param {Object} data to be rendered (change this)
12104          */
12105           "preparedata" : true
12106           
12107           
12108         });
12109
12110
12111
12112     this.el.on({
12113         "click": this.onClick,
12114         "dblclick": this.onDblClick,
12115         "contextmenu": this.onContextMenu,
12116         scope:this
12117     });
12118
12119     this.selections = [];
12120     this.nodes = [];
12121     this.cmp = new Roo.CompositeElementLite([]);
12122     if(this.store){
12123         this.store = Roo.factory(this.store, Roo.data);
12124         this.setStore(this.store, true);
12125     }
12126     
12127     if ( this.footer && this.footer.xtype) {
12128            
12129          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12130         
12131         this.footer.dataSource = this.store
12132         this.footer.container = fctr;
12133         this.footer = Roo.factory(this.footer, Roo);
12134         fctr.insertFirst(this.el);
12135         
12136         // this is a bit insane - as the paging toolbar seems to detach the el..
12137 //        dom.parentNode.parentNode.parentNode
12138          // they get detached?
12139     }
12140     
12141     
12142     Roo.View.superclass.constructor.call(this);
12143     
12144     
12145 };
12146
12147 Roo.extend(Roo.View, Roo.util.Observable, {
12148     
12149      /**
12150      * @cfg {Roo.data.Store} store Data store to load data from.
12151      */
12152     store : false,
12153     
12154     /**
12155      * @cfg {String|Roo.Element} el The container element.
12156      */
12157     el : '',
12158     
12159     /**
12160      * @cfg {String|Roo.Template} tpl The template used by this View 
12161      */
12162     tpl : false,
12163     /**
12164      * @cfg {String} dataName the named area of the template to use as the data area
12165      *                          Works with domtemplates roo-name="name"
12166      */
12167     dataName: false,
12168     /**
12169      * @cfg {String} selectedClass The css class to add to selected nodes
12170      */
12171     selectedClass : "x-view-selected",
12172      /**
12173      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12174      */
12175     emptyText : "",
12176     
12177     /**
12178      * @cfg {String} text to display on mask (default Loading)
12179      */
12180     mask : false,
12181     /**
12182      * @cfg {Boolean} multiSelect Allow multiple selection
12183      */
12184     multiSelect : false,
12185     /**
12186      * @cfg {Boolean} singleSelect Allow single selection
12187      */
12188     singleSelect:  false,
12189     
12190     /**
12191      * @cfg {Boolean} toggleSelect - selecting 
12192      */
12193     toggleSelect : false,
12194     
12195     /**
12196      * @cfg {Boolean} tickable - selecting 
12197      */
12198     tickable : false,
12199     
12200     /**
12201      * Returns the element this view is bound to.
12202      * @return {Roo.Element}
12203      */
12204     getEl : function(){
12205         return this.wrapEl;
12206     },
12207     
12208     
12209
12210     /**
12211      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12212      */
12213     refresh : function(){
12214         Roo.log('refresh');
12215         var t = this.tpl;
12216         
12217         // if we are using something like 'domtemplate', then
12218         // the what gets used is:
12219         // t.applySubtemplate(NAME, data, wrapping data..)
12220         // the outer template then get' applied with
12221         //     the store 'extra data'
12222         // and the body get's added to the
12223         //      roo-name="data" node?
12224         //      <span class='roo-tpl-{name}'></span> ?????
12225         
12226         
12227         
12228         this.clearSelections();
12229         this.el.update("");
12230         var html = [];
12231         var records = this.store.getRange();
12232         if(records.length < 1) {
12233             
12234             // is this valid??  = should it render a template??
12235             
12236             this.el.update(this.emptyText);
12237             return;
12238         }
12239         var el = this.el;
12240         if (this.dataName) {
12241             this.el.update(t.apply(this.store.meta)); //????
12242             el = this.el.child('.roo-tpl-' + this.dataName);
12243         }
12244         
12245         for(var i = 0, len = records.length; i < len; i++){
12246             var data = this.prepareData(records[i].data, i, records[i]);
12247             this.fireEvent("preparedata", this, data, i, records[i]);
12248             
12249             var d = Roo.apply({}, data);
12250             
12251             if(this.tickable){
12252                 Roo.apply(d, {'roo-id' : Roo.id()});
12253                 
12254                 var _this = this;
12255             
12256                 Roo.each(this.parent.item, function(item){
12257                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12258                         return;
12259                     }
12260                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12261                 });
12262             }
12263             
12264             html[html.length] = Roo.util.Format.trim(
12265                 this.dataName ?
12266                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12267                     t.apply(d)
12268             );
12269         }
12270         
12271         
12272         
12273         el.update(html.join(""));
12274         this.nodes = el.dom.childNodes;
12275         this.updateIndexes(0);
12276     },
12277     
12278
12279     /**
12280      * Function to override to reformat the data that is sent to
12281      * the template for each node.
12282      * DEPRICATED - use the preparedata event handler.
12283      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12284      * a JSON object for an UpdateManager bound view).
12285      */
12286     prepareData : function(data, index, record)
12287     {
12288         this.fireEvent("preparedata", this, data, index, record);
12289         return data;
12290     },
12291
12292     onUpdate : function(ds, record){
12293          Roo.log('on update');   
12294         this.clearSelections();
12295         var index = this.store.indexOf(record);
12296         var n = this.nodes[index];
12297         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12298         n.parentNode.removeChild(n);
12299         this.updateIndexes(index, index);
12300     },
12301
12302     
12303     
12304 // --------- FIXME     
12305     onAdd : function(ds, records, index)
12306     {
12307         Roo.log(['on Add', ds, records, index] );        
12308         this.clearSelections();
12309         if(this.nodes.length == 0){
12310             this.refresh();
12311             return;
12312         }
12313         var n = this.nodes[index];
12314         for(var i = 0, len = records.length; i < len; i++){
12315             var d = this.prepareData(records[i].data, i, records[i]);
12316             if(n){
12317                 this.tpl.insertBefore(n, d);
12318             }else{
12319                 
12320                 this.tpl.append(this.el, d);
12321             }
12322         }
12323         this.updateIndexes(index);
12324     },
12325
12326     onRemove : function(ds, record, index){
12327         Roo.log('onRemove');
12328         this.clearSelections();
12329         var el = this.dataName  ?
12330             this.el.child('.roo-tpl-' + this.dataName) :
12331             this.el; 
12332         
12333         el.dom.removeChild(this.nodes[index]);
12334         this.updateIndexes(index);
12335     },
12336
12337     /**
12338      * Refresh an individual node.
12339      * @param {Number} index
12340      */
12341     refreshNode : function(index){
12342         this.onUpdate(this.store, this.store.getAt(index));
12343     },
12344
12345     updateIndexes : function(startIndex, endIndex){
12346         var ns = this.nodes;
12347         startIndex = startIndex || 0;
12348         endIndex = endIndex || ns.length - 1;
12349         for(var i = startIndex; i <= endIndex; i++){
12350             ns[i].nodeIndex = i;
12351         }
12352     },
12353
12354     /**
12355      * Changes the data store this view uses and refresh the view.
12356      * @param {Store} store
12357      */
12358     setStore : function(store, initial){
12359         if(!initial && this.store){
12360             this.store.un("datachanged", this.refresh);
12361             this.store.un("add", this.onAdd);
12362             this.store.un("remove", this.onRemove);
12363             this.store.un("update", this.onUpdate);
12364             this.store.un("clear", this.refresh);
12365             this.store.un("beforeload", this.onBeforeLoad);
12366             this.store.un("load", this.onLoad);
12367             this.store.un("loadexception", this.onLoad);
12368         }
12369         if(store){
12370           
12371             store.on("datachanged", this.refresh, this);
12372             store.on("add", this.onAdd, this);
12373             store.on("remove", this.onRemove, this);
12374             store.on("update", this.onUpdate, this);
12375             store.on("clear", this.refresh, this);
12376             store.on("beforeload", this.onBeforeLoad, this);
12377             store.on("load", this.onLoad, this);
12378             store.on("loadexception", this.onLoad, this);
12379         }
12380         
12381         if(store){
12382             this.refresh();
12383         }
12384     },
12385     /**
12386      * onbeforeLoad - masks the loading area.
12387      *
12388      */
12389     onBeforeLoad : function(store,opts)
12390     {
12391          Roo.log('onBeforeLoad');   
12392         if (!opts.add) {
12393             this.el.update("");
12394         }
12395         this.el.mask(this.mask ? this.mask : "Loading" ); 
12396     },
12397     onLoad : function ()
12398     {
12399         this.el.unmask();
12400     },
12401     
12402
12403     /**
12404      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12405      * @param {HTMLElement} node
12406      * @return {HTMLElement} The template node
12407      */
12408     findItemFromChild : function(node){
12409         var el = this.dataName  ?
12410             this.el.child('.roo-tpl-' + this.dataName,true) :
12411             this.el.dom; 
12412         
12413         if(!node || node.parentNode == el){
12414                     return node;
12415             }
12416             var p = node.parentNode;
12417             while(p && p != el){
12418             if(p.parentNode == el){
12419                 return p;
12420             }
12421             p = p.parentNode;
12422         }
12423             return null;
12424     },
12425
12426     /** @ignore */
12427     onClick : function(e){
12428         var item = this.findItemFromChild(e.getTarget());
12429         if(item){
12430             var index = this.indexOf(item);
12431             if(this.onItemClick(item, index, e) !== false){
12432                 this.fireEvent("click", this, index, item, e);
12433             }
12434         }else{
12435             this.clearSelections();
12436         }
12437     },
12438
12439     /** @ignore */
12440     onContextMenu : function(e){
12441         var item = this.findItemFromChild(e.getTarget());
12442         if(item){
12443             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12444         }
12445     },
12446
12447     /** @ignore */
12448     onDblClick : function(e){
12449         var item = this.findItemFromChild(e.getTarget());
12450         if(item){
12451             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12452         }
12453     },
12454
12455     onItemClick : function(item, index, e)
12456     {
12457         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12458             return false;
12459         }
12460         if (this.toggleSelect) {
12461             var m = this.isSelected(item) ? 'unselect' : 'select';
12462             Roo.log(m);
12463             var _t = this;
12464             _t[m](item, true, false);
12465             return true;
12466         }
12467         if(this.multiSelect || this.singleSelect){
12468             if(this.multiSelect && e.shiftKey && this.lastSelection){
12469                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12470             }else{
12471                 this.select(item, this.multiSelect && e.ctrlKey);
12472                 this.lastSelection = item;
12473             }
12474             
12475             if(!this.tickable){
12476                 e.preventDefault();
12477             }
12478             
12479         }
12480         return true;
12481     },
12482
12483     /**
12484      * Get the number of selected nodes.
12485      * @return {Number}
12486      */
12487     getSelectionCount : function(){
12488         return this.selections.length;
12489     },
12490
12491     /**
12492      * Get the currently selected nodes.
12493      * @return {Array} An array of HTMLElements
12494      */
12495     getSelectedNodes : function(){
12496         return this.selections;
12497     },
12498
12499     /**
12500      * Get the indexes of the selected nodes.
12501      * @return {Array}
12502      */
12503     getSelectedIndexes : function(){
12504         var indexes = [], s = this.selections;
12505         for(var i = 0, len = s.length; i < len; i++){
12506             indexes.push(s[i].nodeIndex);
12507         }
12508         return indexes;
12509     },
12510
12511     /**
12512      * Clear all selections
12513      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12514      */
12515     clearSelections : function(suppressEvent){
12516         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12517             this.cmp.elements = this.selections;
12518             this.cmp.removeClass(this.selectedClass);
12519             this.selections = [];
12520             if(!suppressEvent){
12521                 this.fireEvent("selectionchange", this, this.selections);
12522             }
12523         }
12524     },
12525
12526     /**
12527      * Returns true if the passed node is selected
12528      * @param {HTMLElement/Number} node The node or node index
12529      * @return {Boolean}
12530      */
12531     isSelected : function(node){
12532         var s = this.selections;
12533         if(s.length < 1){
12534             return false;
12535         }
12536         node = this.getNode(node);
12537         return s.indexOf(node) !== -1;
12538     },
12539
12540     /**
12541      * Selects nodes.
12542      * @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
12543      * @param {Boolean} keepExisting (optional) true to keep existing selections
12544      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12545      */
12546     select : function(nodeInfo, keepExisting, suppressEvent){
12547         if(nodeInfo instanceof Array){
12548             if(!keepExisting){
12549                 this.clearSelections(true);
12550             }
12551             for(var i = 0, len = nodeInfo.length; i < len; i++){
12552                 this.select(nodeInfo[i], true, true);
12553             }
12554             return;
12555         } 
12556         var node = this.getNode(nodeInfo);
12557         if(!node || this.isSelected(node)){
12558             return; // already selected.
12559         }
12560         if(!keepExisting){
12561             this.clearSelections(true);
12562         }
12563         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12564             Roo.fly(node).addClass(this.selectedClass);
12565             this.selections.push(node);
12566             if(!suppressEvent){
12567                 this.fireEvent("selectionchange", this, this.selections);
12568             }
12569         }
12570         
12571         
12572     },
12573       /**
12574      * Unselects nodes.
12575      * @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
12576      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12577      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12578      */
12579     unselect : function(nodeInfo, keepExisting, suppressEvent)
12580     {
12581         if(nodeInfo instanceof Array){
12582             Roo.each(this.selections, function(s) {
12583                 this.unselect(s, nodeInfo);
12584             }, this);
12585             return;
12586         }
12587         var node = this.getNode(nodeInfo);
12588         if(!node || !this.isSelected(node)){
12589             Roo.log("not selected");
12590             return; // not selected.
12591         }
12592         // fireevent???
12593         var ns = [];
12594         Roo.each(this.selections, function(s) {
12595             if (s == node ) {
12596                 Roo.fly(node).removeClass(this.selectedClass);
12597
12598                 return;
12599             }
12600             ns.push(s);
12601         },this);
12602         
12603         this.selections= ns;
12604         this.fireEvent("selectionchange", this, this.selections);
12605     },
12606
12607     /**
12608      * Gets a template node.
12609      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12610      * @return {HTMLElement} The node or null if it wasn't found
12611      */
12612     getNode : function(nodeInfo){
12613         if(typeof nodeInfo == "string"){
12614             return document.getElementById(nodeInfo);
12615         }else if(typeof nodeInfo == "number"){
12616             return this.nodes[nodeInfo];
12617         }
12618         return nodeInfo;
12619     },
12620
12621     /**
12622      * Gets a range template nodes.
12623      * @param {Number} startIndex
12624      * @param {Number} endIndex
12625      * @return {Array} An array of nodes
12626      */
12627     getNodes : function(start, end){
12628         var ns = this.nodes;
12629         start = start || 0;
12630         end = typeof end == "undefined" ? ns.length - 1 : end;
12631         var nodes = [];
12632         if(start <= end){
12633             for(var i = start; i <= end; i++){
12634                 nodes.push(ns[i]);
12635             }
12636         } else{
12637             for(var i = start; i >= end; i--){
12638                 nodes.push(ns[i]);
12639             }
12640         }
12641         return nodes;
12642     },
12643
12644     /**
12645      * Finds the index of the passed node
12646      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12647      * @return {Number} The index of the node or -1
12648      */
12649     indexOf : function(node){
12650         node = this.getNode(node);
12651         if(typeof node.nodeIndex == "number"){
12652             return node.nodeIndex;
12653         }
12654         var ns = this.nodes;
12655         for(var i = 0, len = ns.length; i < len; i++){
12656             if(ns[i] == node){
12657                 return i;
12658             }
12659         }
12660         return -1;
12661     }
12662 });
12663 /*
12664  * - LGPL
12665  *
12666  * based on jquery fullcalendar
12667  * 
12668  */
12669
12670 Roo.bootstrap = Roo.bootstrap || {};
12671 /**
12672  * @class Roo.bootstrap.Calendar
12673  * @extends Roo.bootstrap.Component
12674  * Bootstrap Calendar class
12675  * @cfg {Boolean} loadMask (true|false) default false
12676  * @cfg {Object} header generate the user specific header of the calendar, default false
12677
12678  * @constructor
12679  * Create a new Container
12680  * @param {Object} config The config object
12681  */
12682
12683
12684
12685 Roo.bootstrap.Calendar = function(config){
12686     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12687      this.addEvents({
12688         /**
12689              * @event select
12690              * Fires when a date is selected
12691              * @param {DatePicker} this
12692              * @param {Date} date The selected date
12693              */
12694         'select': true,
12695         /**
12696              * @event monthchange
12697              * Fires when the displayed month changes 
12698              * @param {DatePicker} this
12699              * @param {Date} date The selected month
12700              */
12701         'monthchange': true,
12702         /**
12703              * @event evententer
12704              * Fires when mouse over an event
12705              * @param {Calendar} this
12706              * @param {event} Event
12707              */
12708         'evententer': true,
12709         /**
12710              * @event eventleave
12711              * Fires when the mouse leaves an
12712              * @param {Calendar} this
12713              * @param {event}
12714              */
12715         'eventleave': true,
12716         /**
12717              * @event eventclick
12718              * Fires when the mouse click an
12719              * @param {Calendar} this
12720              * @param {event}
12721              */
12722         'eventclick': true
12723         
12724     });
12725
12726 };
12727
12728 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12729     
12730      /**
12731      * @cfg {Number} startDay
12732      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12733      */
12734     startDay : 0,
12735     
12736     loadMask : false,
12737     
12738     header : false,
12739       
12740     getAutoCreate : function(){
12741         
12742         
12743         var fc_button = function(name, corner, style, content ) {
12744             return Roo.apply({},{
12745                 tag : 'span',
12746                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12747                          (corner.length ?
12748                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12749                             ''
12750                         ),
12751                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12752                 unselectable: 'on'
12753             });
12754         };
12755         
12756         var header = {};
12757         
12758         if(!this.header){
12759             header = {
12760                 tag : 'table',
12761                 cls : 'fc-header',
12762                 style : 'width:100%',
12763                 cn : [
12764                     {
12765                         tag: 'tr',
12766                         cn : [
12767                             {
12768                                 tag : 'td',
12769                                 cls : 'fc-header-left',
12770                                 cn : [
12771                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12772                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12773                                     { tag: 'span', cls: 'fc-header-space' },
12774                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12775
12776
12777                                 ]
12778                             },
12779
12780                             {
12781                                 tag : 'td',
12782                                 cls : 'fc-header-center',
12783                                 cn : [
12784                                     {
12785                                         tag: 'span',
12786                                         cls: 'fc-header-title',
12787                                         cn : {
12788                                             tag: 'H2',
12789                                             html : 'month / year'
12790                                         }
12791                                     }
12792
12793                                 ]
12794                             },
12795                             {
12796                                 tag : 'td',
12797                                 cls : 'fc-header-right',
12798                                 cn : [
12799                               /*      fc_button('month', 'left', '', 'month' ),
12800                                     fc_button('week', '', '', 'week' ),
12801                                     fc_button('day', 'right', '', 'day' )
12802                                 */    
12803
12804                                 ]
12805                             }
12806
12807                         ]
12808                     }
12809                 ]
12810             };
12811         }
12812         
12813         header = this.header;
12814         
12815        
12816         var cal_heads = function() {
12817             var ret = [];
12818             // fixme - handle this.
12819             
12820             for (var i =0; i < Date.dayNames.length; i++) {
12821                 var d = Date.dayNames[i];
12822                 ret.push({
12823                     tag: 'th',
12824                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12825                     html : d.substring(0,3)
12826                 });
12827                 
12828             }
12829             ret[0].cls += ' fc-first';
12830             ret[6].cls += ' fc-last';
12831             return ret;
12832         };
12833         var cal_cell = function(n) {
12834             return  {
12835                 tag: 'td',
12836                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12837                 cn : [
12838                     {
12839                         cn : [
12840                             {
12841                                 cls: 'fc-day-number',
12842                                 html: 'D'
12843                             },
12844                             {
12845                                 cls: 'fc-day-content',
12846                              
12847                                 cn : [
12848                                      {
12849                                         style: 'position: relative;' // height: 17px;
12850                                     }
12851                                 ]
12852                             }
12853                             
12854                             
12855                         ]
12856                     }
12857                 ]
12858                 
12859             }
12860         };
12861         var cal_rows = function() {
12862             
12863             var ret = []
12864             for (var r = 0; r < 6; r++) {
12865                 var row= {
12866                     tag : 'tr',
12867                     cls : 'fc-week',
12868                     cn : []
12869                 };
12870                 
12871                 for (var i =0; i < Date.dayNames.length; i++) {
12872                     var d = Date.dayNames[i];
12873                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12874
12875                 }
12876                 row.cn[0].cls+=' fc-first';
12877                 row.cn[0].cn[0].style = 'min-height:90px';
12878                 row.cn[6].cls+=' fc-last';
12879                 ret.push(row);
12880                 
12881             }
12882             ret[0].cls += ' fc-first';
12883             ret[4].cls += ' fc-prev-last';
12884             ret[5].cls += ' fc-last';
12885             return ret;
12886             
12887         };
12888         
12889         var cal_table = {
12890             tag: 'table',
12891             cls: 'fc-border-separate',
12892             style : 'width:100%',
12893             cellspacing  : 0,
12894             cn : [
12895                 { 
12896                     tag: 'thead',
12897                     cn : [
12898                         { 
12899                             tag: 'tr',
12900                             cls : 'fc-first fc-last',
12901                             cn : cal_heads()
12902                         }
12903                     ]
12904                 },
12905                 { 
12906                     tag: 'tbody',
12907                     cn : cal_rows()
12908                 }
12909                   
12910             ]
12911         };
12912          
12913          var cfg = {
12914             cls : 'fc fc-ltr',
12915             cn : [
12916                 header,
12917                 {
12918                     cls : 'fc-content',
12919                     style : "position: relative;",
12920                     cn : [
12921                         {
12922                             cls : 'fc-view fc-view-month fc-grid',
12923                             style : 'position: relative',
12924                             unselectable : 'on',
12925                             cn : [
12926                                 {
12927                                     cls : 'fc-event-container',
12928                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12929                                 },
12930                                 cal_table
12931                             ]
12932                         }
12933                     ]
12934     
12935                 }
12936            ] 
12937             
12938         };
12939         
12940          
12941         
12942         return cfg;
12943     },
12944     
12945     
12946     initEvents : function()
12947     {
12948         if(!this.store){
12949             throw "can not find store for calendar";
12950         }
12951         
12952         var mark = {
12953             tag: "div",
12954             cls:"x-dlg-mask",
12955             style: "text-align:center",
12956             cn: [
12957                 {
12958                     tag: "div",
12959                     style: "background-color:white;width:50%;margin:250 auto",
12960                     cn: [
12961                         {
12962                             tag: "img",
12963                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12964                         },
12965                         {
12966                             tag: "span",
12967                             html: "Loading"
12968                         }
12969                         
12970                     ]
12971                 }
12972             ]
12973         }
12974         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12975         
12976         var size = this.el.select('.fc-content', true).first().getSize();
12977         this.maskEl.setSize(size.width, size.height);
12978         this.maskEl.enableDisplayMode("block");
12979         if(!this.loadMask){
12980             this.maskEl.hide();
12981         }
12982         
12983         this.store = Roo.factory(this.store, Roo.data);
12984         this.store.on('load', this.onLoad, this);
12985         this.store.on('beforeload', this.onBeforeLoad, this);
12986         
12987         this.resize();
12988         
12989         this.cells = this.el.select('.fc-day',true);
12990         //Roo.log(this.cells);
12991         this.textNodes = this.el.query('.fc-day-number');
12992         this.cells.addClassOnOver('fc-state-hover');
12993         
12994         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12995         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12996         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12997         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12998         
12999         this.on('monthchange', this.onMonthChange, this);
13000         
13001         this.update(new Date().clearTime());
13002     },
13003     
13004     resize : function() {
13005         var sz  = this.el.getSize();
13006         
13007         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13008         this.el.select('.fc-day-content div',true).setHeight(34);
13009     },
13010     
13011     
13012     // private
13013     showPrevMonth : function(e){
13014         this.update(this.activeDate.add("mo", -1));
13015     },
13016     showToday : function(e){
13017         this.update(new Date().clearTime());
13018     },
13019     // private
13020     showNextMonth : function(e){
13021         this.update(this.activeDate.add("mo", 1));
13022     },
13023
13024     // private
13025     showPrevYear : function(){
13026         this.update(this.activeDate.add("y", -1));
13027     },
13028
13029     // private
13030     showNextYear : function(){
13031         this.update(this.activeDate.add("y", 1));
13032     },
13033
13034     
13035    // private
13036     update : function(date)
13037     {
13038         var vd = this.activeDate;
13039         this.activeDate = date;
13040 //        if(vd && this.el){
13041 //            var t = date.getTime();
13042 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13043 //                Roo.log('using add remove');
13044 //                
13045 //                this.fireEvent('monthchange', this, date);
13046 //                
13047 //                this.cells.removeClass("fc-state-highlight");
13048 //                this.cells.each(function(c){
13049 //                   if(c.dateValue == t){
13050 //                       c.addClass("fc-state-highlight");
13051 //                       setTimeout(function(){
13052 //                            try{c.dom.firstChild.focus();}catch(e){}
13053 //                       }, 50);
13054 //                       return false;
13055 //                   }
13056 //                   return true;
13057 //                });
13058 //                return;
13059 //            }
13060 //        }
13061         
13062         var days = date.getDaysInMonth();
13063         
13064         var firstOfMonth = date.getFirstDateOfMonth();
13065         var startingPos = firstOfMonth.getDay()-this.startDay;
13066         
13067         if(startingPos < this.startDay){
13068             startingPos += 7;
13069         }
13070         
13071         var pm = date.add(Date.MONTH, -1);
13072         var prevStart = pm.getDaysInMonth()-startingPos;
13073 //        
13074         this.cells = this.el.select('.fc-day',true);
13075         this.textNodes = this.el.query('.fc-day-number');
13076         this.cells.addClassOnOver('fc-state-hover');
13077         
13078         var cells = this.cells.elements;
13079         var textEls = this.textNodes;
13080         
13081         Roo.each(cells, function(cell){
13082             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13083         });
13084         
13085         days += startingPos;
13086
13087         // convert everything to numbers so it's fast
13088         var day = 86400000;
13089         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13090         //Roo.log(d);
13091         //Roo.log(pm);
13092         //Roo.log(prevStart);
13093         
13094         var today = new Date().clearTime().getTime();
13095         var sel = date.clearTime().getTime();
13096         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13097         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13098         var ddMatch = this.disabledDatesRE;
13099         var ddText = this.disabledDatesText;
13100         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13101         var ddaysText = this.disabledDaysText;
13102         var format = this.format;
13103         
13104         var setCellClass = function(cal, cell){
13105             cell.row = 0;
13106             cell.events = [];
13107             cell.more = [];
13108             //Roo.log('set Cell Class');
13109             cell.title = "";
13110             var t = d.getTime();
13111             
13112             //Roo.log(d);
13113             
13114             cell.dateValue = t;
13115             if(t == today){
13116                 cell.className += " fc-today";
13117                 cell.className += " fc-state-highlight";
13118                 cell.title = cal.todayText;
13119             }
13120             if(t == sel){
13121                 // disable highlight in other month..
13122                 //cell.className += " fc-state-highlight";
13123                 
13124             }
13125             // disabling
13126             if(t < min) {
13127                 cell.className = " fc-state-disabled";
13128                 cell.title = cal.minText;
13129                 return;
13130             }
13131             if(t > max) {
13132                 cell.className = " fc-state-disabled";
13133                 cell.title = cal.maxText;
13134                 return;
13135             }
13136             if(ddays){
13137                 if(ddays.indexOf(d.getDay()) != -1){
13138                     cell.title = ddaysText;
13139                     cell.className = " fc-state-disabled";
13140                 }
13141             }
13142             if(ddMatch && format){
13143                 var fvalue = d.dateFormat(format);
13144                 if(ddMatch.test(fvalue)){
13145                     cell.title = ddText.replace("%0", fvalue);
13146                     cell.className = " fc-state-disabled";
13147                 }
13148             }
13149             
13150             if (!cell.initialClassName) {
13151                 cell.initialClassName = cell.dom.className;
13152             }
13153             
13154             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13155         };
13156
13157         var i = 0;
13158         
13159         for(; i < startingPos; i++) {
13160             textEls[i].innerHTML = (++prevStart);
13161             d.setDate(d.getDate()+1);
13162             
13163             cells[i].className = "fc-past fc-other-month";
13164             setCellClass(this, cells[i]);
13165         }
13166         
13167         var intDay = 0;
13168         
13169         for(; i < days; i++){
13170             intDay = i - startingPos + 1;
13171             textEls[i].innerHTML = (intDay);
13172             d.setDate(d.getDate()+1);
13173             
13174             cells[i].className = ''; // "x-date-active";
13175             setCellClass(this, cells[i]);
13176         }
13177         var extraDays = 0;
13178         
13179         for(; i < 42; i++) {
13180             textEls[i].innerHTML = (++extraDays);
13181             d.setDate(d.getDate()+1);
13182             
13183             cells[i].className = "fc-future fc-other-month";
13184             setCellClass(this, cells[i]);
13185         }
13186         
13187         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13188         
13189         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13190         
13191         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13192         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13193         
13194         if(totalRows != 6){
13195             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13196             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13197         }
13198         
13199         this.fireEvent('monthchange', this, date);
13200         
13201         
13202         /*
13203         if(!this.internalRender){
13204             var main = this.el.dom.firstChild;
13205             var w = main.offsetWidth;
13206             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13207             Roo.fly(main).setWidth(w);
13208             this.internalRender = true;
13209             // opera does not respect the auto grow header center column
13210             // then, after it gets a width opera refuses to recalculate
13211             // without a second pass
13212             if(Roo.isOpera && !this.secondPass){
13213                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13214                 this.secondPass = true;
13215                 this.update.defer(10, this, [date]);
13216             }
13217         }
13218         */
13219         
13220     },
13221     
13222     findCell : function(dt) {
13223         dt = dt.clearTime().getTime();
13224         var ret = false;
13225         this.cells.each(function(c){
13226             //Roo.log("check " +c.dateValue + '?=' + dt);
13227             if(c.dateValue == dt){
13228                 ret = c;
13229                 return false;
13230             }
13231             return true;
13232         });
13233         
13234         return ret;
13235     },
13236     
13237     findCells : function(ev) {
13238         var s = ev.start.clone().clearTime().getTime();
13239        // Roo.log(s);
13240         var e= ev.end.clone().clearTime().getTime();
13241        // Roo.log(e);
13242         var ret = [];
13243         this.cells.each(function(c){
13244              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13245             
13246             if(c.dateValue > e){
13247                 return ;
13248             }
13249             if(c.dateValue < s){
13250                 return ;
13251             }
13252             ret.push(c);
13253         });
13254         
13255         return ret;    
13256     },
13257     
13258 //    findBestRow: function(cells)
13259 //    {
13260 //        var ret = 0;
13261 //        
13262 //        for (var i =0 ; i < cells.length;i++) {
13263 //            ret  = Math.max(cells[i].rows || 0,ret);
13264 //        }
13265 //        return ret;
13266 //        
13267 //    },
13268     
13269     
13270     addItem : function(ev)
13271     {
13272         // look for vertical location slot in
13273         var cells = this.findCells(ev);
13274         
13275 //        ev.row = this.findBestRow(cells);
13276         
13277         // work out the location.
13278         
13279         var crow = false;
13280         var rows = [];
13281         for(var i =0; i < cells.length; i++) {
13282             
13283             cells[i].row = cells[0].row;
13284             
13285             if(i == 0){
13286                 cells[i].row = cells[i].row + 1;
13287             }
13288             
13289             if (!crow) {
13290                 crow = {
13291                     start : cells[i],
13292                     end :  cells[i]
13293                 };
13294                 continue;
13295             }
13296             if (crow.start.getY() == cells[i].getY()) {
13297                 // on same row.
13298                 crow.end = cells[i];
13299                 continue;
13300             }
13301             // different row.
13302             rows.push(crow);
13303             crow = {
13304                 start: cells[i],
13305                 end : cells[i]
13306             };
13307             
13308         }
13309         
13310         rows.push(crow);
13311         ev.els = [];
13312         ev.rows = rows;
13313         ev.cells = cells;
13314         
13315         cells[0].events.push(ev);
13316         
13317         this.calevents.push(ev);
13318     },
13319     
13320     clearEvents: function() {
13321         
13322         if(!this.calevents){
13323             return;
13324         }
13325         
13326         Roo.each(this.cells.elements, function(c){
13327             c.row = 0;
13328             c.events = [];
13329             c.more = [];
13330         });
13331         
13332         Roo.each(this.calevents, function(e) {
13333             Roo.each(e.els, function(el) {
13334                 el.un('mouseenter' ,this.onEventEnter, this);
13335                 el.un('mouseleave' ,this.onEventLeave, this);
13336                 el.remove();
13337             },this);
13338         },this);
13339         
13340         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13341             e.remove();
13342         });
13343         
13344     },
13345     
13346     renderEvents: function()
13347     {   
13348         var _this = this;
13349         
13350         this.cells.each(function(c) {
13351             
13352             if(c.row < 5){
13353                 return;
13354             }
13355             
13356             var ev = c.events;
13357             
13358             var r = 4;
13359             if(c.row != c.events.length){
13360                 r = 4 - (4 - (c.row - c.events.length));
13361             }
13362             
13363             c.events = ev.slice(0, r);
13364             c.more = ev.slice(r);
13365             
13366             if(c.more.length && c.more.length == 1){
13367                 c.events.push(c.more.pop());
13368             }
13369             
13370             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13371             
13372         });
13373             
13374         this.cells.each(function(c) {
13375             
13376             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13377             
13378             
13379             for (var e = 0; e < c.events.length; e++){
13380                 var ev = c.events[e];
13381                 var rows = ev.rows;
13382                 
13383                 for(var i = 0; i < rows.length; i++) {
13384                 
13385                     // how many rows should it span..
13386
13387                     var  cfg = {
13388                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13389                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13390
13391                         unselectable : "on",
13392                         cn : [
13393                             {
13394                                 cls: 'fc-event-inner',
13395                                 cn : [
13396     //                                {
13397     //                                  tag:'span',
13398     //                                  cls: 'fc-event-time',
13399     //                                  html : cells.length > 1 ? '' : ev.time
13400     //                                },
13401                                     {
13402                                       tag:'span',
13403                                       cls: 'fc-event-title',
13404                                       html : String.format('{0}', ev.title)
13405                                     }
13406
13407
13408                                 ]
13409                             },
13410                             {
13411                                 cls: 'ui-resizable-handle ui-resizable-e',
13412                                 html : '&nbsp;&nbsp;&nbsp'
13413                             }
13414
13415                         ]
13416                     };
13417
13418                     if (i == 0) {
13419                         cfg.cls += ' fc-event-start';
13420                     }
13421                     if ((i+1) == rows.length) {
13422                         cfg.cls += ' fc-event-end';
13423                     }
13424
13425                     var ctr = _this.el.select('.fc-event-container',true).first();
13426                     var cg = ctr.createChild(cfg);
13427
13428                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13429                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13430
13431                     var r = (c.more.length) ? 1 : 0;
13432                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13433                     cg.setWidth(ebox.right - sbox.x -2);
13434
13435                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13436                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13437                     cg.on('click', _this.onEventClick, _this, ev);
13438
13439                     ev.els.push(cg);
13440                     
13441                 }
13442                 
13443             }
13444             
13445             
13446             if(c.more.length){
13447                 var  cfg = {
13448                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13449                     style : 'position: absolute',
13450                     unselectable : "on",
13451                     cn : [
13452                         {
13453                             cls: 'fc-event-inner',
13454                             cn : [
13455                                 {
13456                                   tag:'span',
13457                                   cls: 'fc-event-title',
13458                                   html : 'More'
13459                                 }
13460
13461
13462                             ]
13463                         },
13464                         {
13465                             cls: 'ui-resizable-handle ui-resizable-e',
13466                             html : '&nbsp;&nbsp;&nbsp'
13467                         }
13468
13469                     ]
13470                 };
13471
13472                 var ctr = _this.el.select('.fc-event-container',true).first();
13473                 var cg = ctr.createChild(cfg);
13474
13475                 var sbox = c.select('.fc-day-content',true).first().getBox();
13476                 var ebox = c.select('.fc-day-content',true).first().getBox();
13477                 //Roo.log(cg);
13478                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13479                 cg.setWidth(ebox.right - sbox.x -2);
13480
13481                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13482                 
13483             }
13484             
13485         });
13486         
13487         
13488         
13489     },
13490     
13491     onEventEnter: function (e, el,event,d) {
13492         this.fireEvent('evententer', this, el, event);
13493     },
13494     
13495     onEventLeave: function (e, el,event,d) {
13496         this.fireEvent('eventleave', this, el, event);
13497     },
13498     
13499     onEventClick: function (e, el,event,d) {
13500         this.fireEvent('eventclick', this, el, event);
13501     },
13502     
13503     onMonthChange: function () {
13504         this.store.load();
13505     },
13506     
13507     onMoreEventClick: function(e, el, more)
13508     {
13509         var _this = this;
13510         
13511         this.calpopover.placement = 'right';
13512         this.calpopover.setTitle('More');
13513         
13514         this.calpopover.setContent('');
13515         
13516         var ctr = this.calpopover.el.select('.popover-content', true).first();
13517         
13518         Roo.each(more, function(m){
13519             var cfg = {
13520                 cls : 'fc-event-hori fc-event-draggable',
13521                 html : m.title
13522             }
13523             var cg = ctr.createChild(cfg);
13524             
13525             cg.on('click', _this.onEventClick, _this, m);
13526         });
13527         
13528         this.calpopover.show(el);
13529         
13530         
13531     },
13532     
13533     onLoad: function () 
13534     {   
13535         this.calevents = [];
13536         var cal = this;
13537         
13538         if(this.store.getCount() > 0){
13539             this.store.data.each(function(d){
13540                cal.addItem({
13541                     id : d.data.id,
13542                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13543                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13544                     time : d.data.start_time,
13545                     title : d.data.title,
13546                     description : d.data.description,
13547                     venue : d.data.venue
13548                 });
13549             });
13550         }
13551         
13552         this.renderEvents();
13553         
13554         if(this.calevents.length && this.loadMask){
13555             this.maskEl.hide();
13556         }
13557     },
13558     
13559     onBeforeLoad: function()
13560     {
13561         this.clearEvents();
13562         if(this.loadMask){
13563             this.maskEl.show();
13564         }
13565     }
13566 });
13567
13568  
13569  /*
13570  * - LGPL
13571  *
13572  * element
13573  * 
13574  */
13575
13576 /**
13577  * @class Roo.bootstrap.Popover
13578  * @extends Roo.bootstrap.Component
13579  * Bootstrap Popover class
13580  * @cfg {String} html contents of the popover   (or false to use children..)
13581  * @cfg {String} title of popover (or false to hide)
13582  * @cfg {String} placement how it is placed
13583  * @cfg {String} trigger click || hover (or false to trigger manually)
13584  * @cfg {String} over what (parent or false to trigger manually.)
13585  * 
13586  * @constructor
13587  * Create a new Popover
13588  * @param {Object} config The config object
13589  */
13590
13591 Roo.bootstrap.Popover = function(config){
13592     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13593 };
13594
13595 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13596     
13597     title: 'Fill in a title',
13598     html: false,
13599     
13600     placement : 'right',
13601     trigger : 'hover', // hover
13602     
13603     over: 'parent',
13604     
13605     can_build_overlaid : false,
13606     
13607     getChildContainer : function()
13608     {
13609         return this.el.select('.popover-content',true).first();
13610     },
13611     
13612     getAutoCreate : function(){
13613          Roo.log('make popover?');
13614         var cfg = {
13615            cls : 'popover roo-dynamic',
13616            style: 'display:block',
13617            cn : [
13618                 {
13619                     cls : 'arrow'
13620                 },
13621                 {
13622                     cls : 'popover-inner',
13623                     cn : [
13624                         {
13625                             tag: 'h3',
13626                             cls: 'popover-title',
13627                             html : this.title
13628                         },
13629                         {
13630                             cls : 'popover-content',
13631                             html : this.html
13632                         }
13633                     ]
13634                     
13635                 }
13636            ]
13637         };
13638         
13639         return cfg;
13640     },
13641     setTitle: function(str)
13642     {
13643         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13644     },
13645     setContent: function(str)
13646     {
13647         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13648     },
13649     // as it get's added to the bottom of the page.
13650     onRender : function(ct, position)
13651     {
13652         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13653         if(!this.el){
13654             var cfg = Roo.apply({},  this.getAutoCreate());
13655             cfg.id = Roo.id();
13656             
13657             if (this.cls) {
13658                 cfg.cls += ' ' + this.cls;
13659             }
13660             if (this.style) {
13661                 cfg.style = this.style;
13662             }
13663             Roo.log("adding to ")
13664             this.el = Roo.get(document.body).createChild(cfg, position);
13665             Roo.log(this.el);
13666         }
13667         this.initEvents();
13668     },
13669     
13670     initEvents : function()
13671     {
13672         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13673         this.el.enableDisplayMode('block');
13674         this.el.hide();
13675         if (this.over === false) {
13676             return; 
13677         }
13678         if (this.triggers === false) {
13679             return;
13680         }
13681         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13682         var triggers = this.trigger ? this.trigger.split(' ') : [];
13683         Roo.each(triggers, function(trigger) {
13684         
13685             if (trigger == 'click') {
13686                 on_el.on('click', this.toggle, this);
13687             } else if (trigger != 'manual') {
13688                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13689                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13690       
13691                 on_el.on(eventIn  ,this.enter, this);
13692                 on_el.on(eventOut, this.leave, this);
13693             }
13694         }, this);
13695         
13696     },
13697     
13698     
13699     // private
13700     timeout : null,
13701     hoverState : null,
13702     
13703     toggle : function () {
13704         this.hoverState == 'in' ? this.leave() : this.enter();
13705     },
13706     
13707     enter : function () {
13708        
13709     
13710         clearTimeout(this.timeout);
13711     
13712         this.hoverState = 'in'
13713     
13714         if (!this.delay || !this.delay.show) {
13715             this.show();
13716             return 
13717         }
13718         var _t = this;
13719         this.timeout = setTimeout(function () {
13720             if (_t.hoverState == 'in') {
13721                 _t.show();
13722             }
13723         }, this.delay.show)
13724     },
13725     leave : function() {
13726         clearTimeout(this.timeout);
13727     
13728         this.hoverState = 'out'
13729     
13730         if (!this.delay || !this.delay.hide) {
13731             this.hide();
13732             return 
13733         }
13734         var _t = this;
13735         this.timeout = setTimeout(function () {
13736             if (_t.hoverState == 'out') {
13737                 _t.hide();
13738             }
13739         }, this.delay.hide)
13740     },
13741     
13742     show : function (on_el)
13743     {
13744         if (!on_el) {
13745             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13746         }
13747         // set content.
13748         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13749         if (this.html !== false) {
13750             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13751         }
13752         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13753         if (!this.title.length) {
13754             this.el.select('.popover-title',true).hide();
13755         }
13756         
13757         var placement = typeof this.placement == 'function' ?
13758             this.placement.call(this, this.el, on_el) :
13759             this.placement;
13760             
13761         var autoToken = /\s?auto?\s?/i;
13762         var autoPlace = autoToken.test(placement);
13763         if (autoPlace) {
13764             placement = placement.replace(autoToken, '') || 'top';
13765         }
13766         
13767         //this.el.detach()
13768         //this.el.setXY([0,0]);
13769         this.el.show();
13770         this.el.dom.style.display='block';
13771         this.el.addClass(placement);
13772         
13773         //this.el.appendTo(on_el);
13774         
13775         var p = this.getPosition();
13776         var box = this.el.getBox();
13777         
13778         if (autoPlace) {
13779             // fixme..
13780         }
13781         var align = Roo.bootstrap.Popover.alignment[placement]
13782         this.el.alignTo(on_el, align[0],align[1]);
13783         //var arrow = this.el.select('.arrow',true).first();
13784         //arrow.set(align[2], 
13785         
13786         this.el.addClass('in');
13787         this.hoverState = null;
13788         
13789         if (this.el.hasClass('fade')) {
13790             // fade it?
13791         }
13792         
13793     },
13794     hide : function()
13795     {
13796         this.el.setXY([0,0]);
13797         this.el.removeClass('in');
13798         this.el.hide();
13799         
13800     }
13801     
13802 });
13803
13804 Roo.bootstrap.Popover.alignment = {
13805     'left' : ['r-l', [-10,0], 'right'],
13806     'right' : ['l-r', [10,0], 'left'],
13807     'bottom' : ['t-b', [0,10], 'top'],
13808     'top' : [ 'b-t', [0,-10], 'bottom']
13809 };
13810
13811  /*
13812  * - LGPL
13813  *
13814  * Progress
13815  * 
13816  */
13817
13818 /**
13819  * @class Roo.bootstrap.Progress
13820  * @extends Roo.bootstrap.Component
13821  * Bootstrap Progress class
13822  * @cfg {Boolean} striped striped of the progress bar
13823  * @cfg {Boolean} active animated of the progress bar
13824  * 
13825  * 
13826  * @constructor
13827  * Create a new Progress
13828  * @param {Object} config The config object
13829  */
13830
13831 Roo.bootstrap.Progress = function(config){
13832     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13833 };
13834
13835 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13836     
13837     striped : false,
13838     active: false,
13839     
13840     getAutoCreate : function(){
13841         var cfg = {
13842             tag: 'div',
13843             cls: 'progress'
13844         };
13845         
13846         
13847         if(this.striped){
13848             cfg.cls += ' progress-striped';
13849         }
13850       
13851         if(this.active){
13852             cfg.cls += ' active';
13853         }
13854         
13855         
13856         return cfg;
13857     }
13858    
13859 });
13860
13861  
13862
13863  /*
13864  * - LGPL
13865  *
13866  * ProgressBar
13867  * 
13868  */
13869
13870 /**
13871  * @class Roo.bootstrap.ProgressBar
13872  * @extends Roo.bootstrap.Component
13873  * Bootstrap ProgressBar class
13874  * @cfg {Number} aria_valuenow aria-value now
13875  * @cfg {Number} aria_valuemin aria-value min
13876  * @cfg {Number} aria_valuemax aria-value max
13877  * @cfg {String} label label for the progress bar
13878  * @cfg {String} panel (success | info | warning | danger )
13879  * @cfg {String} role role of the progress bar
13880  * @cfg {String} sr_only text
13881  * 
13882  * 
13883  * @constructor
13884  * Create a new ProgressBar
13885  * @param {Object} config The config object
13886  */
13887
13888 Roo.bootstrap.ProgressBar = function(config){
13889     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13890 };
13891
13892 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13893     
13894     aria_valuenow : 0,
13895     aria_valuemin : 0,
13896     aria_valuemax : 100,
13897     label : false,
13898     panel : false,
13899     role : false,
13900     sr_only: false,
13901     
13902     getAutoCreate : function()
13903     {
13904         
13905         var cfg = {
13906             tag: 'div',
13907             cls: 'progress-bar',
13908             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13909         };
13910         
13911         if(this.sr_only){
13912             cfg.cn = {
13913                 tag: 'span',
13914                 cls: 'sr-only',
13915                 html: this.sr_only
13916             }
13917         }
13918         
13919         if(this.role){
13920             cfg.role = this.role;
13921         }
13922         
13923         if(this.aria_valuenow){
13924             cfg['aria-valuenow'] = this.aria_valuenow;
13925         }
13926         
13927         if(this.aria_valuemin){
13928             cfg['aria-valuemin'] = this.aria_valuemin;
13929         }
13930         
13931         if(this.aria_valuemax){
13932             cfg['aria-valuemax'] = this.aria_valuemax;
13933         }
13934         
13935         if(this.label && !this.sr_only){
13936             cfg.html = this.label;
13937         }
13938         
13939         if(this.panel){
13940             cfg.cls += ' progress-bar-' + this.panel;
13941         }
13942         
13943         return cfg;
13944     },
13945     
13946     update : function(aria_valuenow)
13947     {
13948         this.aria_valuenow = aria_valuenow;
13949         
13950         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13951     }
13952    
13953 });
13954
13955  
13956
13957  /*
13958  * - LGPL
13959  *
13960  * column
13961  * 
13962  */
13963
13964 /**
13965  * @class Roo.bootstrap.TabGroup
13966  * @extends Roo.bootstrap.Column
13967  * Bootstrap Column class
13968  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13969  * @cfg {Boolean} carousel true to make the group behave like a carousel
13970  * 
13971  * @constructor
13972  * Create a new TabGroup
13973  * @param {Object} config The config object
13974  */
13975
13976 Roo.bootstrap.TabGroup = function(config){
13977     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13978     if (!this.navId) {
13979         this.navId = Roo.id();
13980     }
13981     this.tabs = [];
13982     Roo.bootstrap.TabGroup.register(this);
13983     
13984 };
13985
13986 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13987     
13988     carousel : false,
13989     transition : false,
13990      
13991     getAutoCreate : function()
13992     {
13993         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13994         
13995         cfg.cls += ' tab-content';
13996         
13997         if (this.carousel) {
13998             cfg.cls += ' carousel slide';
13999             cfg.cn = [{
14000                cls : 'carousel-inner'
14001             }]
14002         }
14003         
14004         
14005         return cfg;
14006     },
14007     getChildContainer : function()
14008     {
14009         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14010     },
14011     
14012     /**
14013     * register a Navigation item
14014     * @param {Roo.bootstrap.NavItem} the navitem to add
14015     */
14016     register : function(item)
14017     {
14018         this.tabs.push( item);
14019         item.navId = this.navId; // not really needed..
14020     
14021     },
14022     
14023     getActivePanel : function()
14024     {
14025         var r = false;
14026         Roo.each(this.tabs, function(t) {
14027             if (t.active) {
14028                 r = t;
14029                 return false;
14030             }
14031             return null;
14032         });
14033         return r;
14034         
14035     },
14036     getPanelByName : function(n)
14037     {
14038         var r = false;
14039         Roo.each(this.tabs, function(t) {
14040             if (t.tabId == n) {
14041                 r = t;
14042                 return false;
14043             }
14044             return null;
14045         });
14046         return r;
14047     },
14048     indexOfPanel : function(p)
14049     {
14050         var r = false;
14051         Roo.each(this.tabs, function(t,i) {
14052             if (t.tabId == p.tabId) {
14053                 r = i;
14054                 return false;
14055             }
14056             return null;
14057         });
14058         return r;
14059     },
14060     /**
14061      * show a specific panel
14062      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14063      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14064      */
14065     showPanel : function (pan)
14066     {
14067         
14068         if (typeof(pan) == 'number') {
14069             pan = this.tabs[pan];
14070         }
14071         if (typeof(pan) == 'string') {
14072             pan = this.getPanelByName(pan);
14073         }
14074         if (pan.tabId == this.getActivePanel().tabId) {
14075             return true;
14076         }
14077         var cur = this.getActivePanel();
14078         
14079         if (false === cur.fireEvent('beforedeactivate')) {
14080             return false;
14081         }
14082         
14083         if (this.carousel) {
14084             this.transition = true;
14085             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14086             var lr = dir == 'next' ? 'left' : 'right';
14087             pan.el.addClass(dir); // or prev
14088             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14089             cur.el.addClass(lr); // or right
14090             pan.el.addClass(lr);
14091             
14092             var _this = this;
14093             cur.el.on('transitionend', function() {
14094                 Roo.log("trans end?");
14095                 
14096                 pan.el.removeClass([lr,dir]);
14097                 pan.setActive(true);
14098                 
14099                 cur.el.removeClass([lr]);
14100                 cur.setActive(false);
14101                 
14102                 _this.transition = false;
14103                 
14104             }, this, { single:  true } );
14105             return true;
14106         }
14107         
14108         cur.setActive(false);
14109         pan.setActive(true);
14110         return true;
14111         
14112     },
14113     showPanelNext : function()
14114     {
14115         var i = this.indexOfPanel(this.getActivePanel());
14116         if (i > this.tabs.length) {
14117             return;
14118         }
14119         this.showPanel(this.tabs[i+1]);
14120     },
14121     showPanelPrev : function()
14122     {
14123         var i = this.indexOfPanel(this.getActivePanel());
14124         if (i  < 1) {
14125             return;
14126         }
14127         this.showPanel(this.tabs[i-1]);
14128     }
14129     
14130     
14131   
14132 });
14133
14134  
14135
14136  
14137  
14138 Roo.apply(Roo.bootstrap.TabGroup, {
14139     
14140     groups: {},
14141      /**
14142     * register a Navigation Group
14143     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14144     */
14145     register : function(navgrp)
14146     {
14147         this.groups[navgrp.navId] = navgrp;
14148         
14149     },
14150     /**
14151     * fetch a Navigation Group based on the navigation ID
14152     * if one does not exist , it will get created.
14153     * @param {string} the navgroup to add
14154     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14155     */
14156     get: function(navId) {
14157         if (typeof(this.groups[navId]) == 'undefined') {
14158             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14159         }
14160         return this.groups[navId] ;
14161     }
14162     
14163     
14164     
14165 });
14166
14167  /*
14168  * - LGPL
14169  *
14170  * TabPanel
14171  * 
14172  */
14173
14174 /**
14175  * @class Roo.bootstrap.TabPanel
14176  * @extends Roo.bootstrap.Component
14177  * Bootstrap TabPanel class
14178  * @cfg {Boolean} active panel active
14179  * @cfg {String} html panel content
14180  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14181  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14182  * 
14183  * 
14184  * @constructor
14185  * Create a new TabPanel
14186  * @param {Object} config The config object
14187  */
14188
14189 Roo.bootstrap.TabPanel = function(config){
14190     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14191     this.addEvents({
14192         /**
14193              * @event changed
14194              * Fires when the active status changes
14195              * @param {Roo.bootstrap.TabPanel} this
14196              * @param {Boolean} state the new state
14197             
14198          */
14199         'changed': true,
14200         /**
14201              * @event beforedeactivate
14202              * Fires before a tab is de-activated - can be used to do validation on a form.
14203              * @param {Roo.bootstrap.TabPanel} this
14204              * @return {Boolean} false if there is an error
14205             
14206          */
14207         'beforedeactivate': true
14208      });
14209     
14210     this.tabId = this.tabId || Roo.id();
14211   
14212 };
14213
14214 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14215     
14216     active: false,
14217     html: false,
14218     tabId: false,
14219     navId : false,
14220     
14221     getAutoCreate : function(){
14222         var cfg = {
14223             tag: 'div',
14224             // item is needed for carousel - not sure if it has any effect otherwise
14225             cls: 'tab-pane item',
14226             html: this.html || ''
14227         };
14228         
14229         if(this.active){
14230             cfg.cls += ' active';
14231         }
14232         
14233         if(this.tabId){
14234             cfg.tabId = this.tabId;
14235         }
14236         
14237         
14238         return cfg;
14239     },
14240     
14241     initEvents:  function()
14242     {
14243         Roo.log('-------- init events on tab panel ---------');
14244         
14245         var p = this.parent();
14246         this.navId = this.navId || p.navId;
14247         
14248         if (typeof(this.navId) != 'undefined') {
14249             // not really needed.. but just in case.. parent should be a NavGroup.
14250             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14251             Roo.log(['register', tg, this]);
14252             tg.register(this);
14253         }
14254     },
14255     
14256     
14257     onRender : function(ct, position)
14258     {
14259        // Roo.log("Call onRender: " + this.xtype);
14260         
14261         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14262         
14263         
14264         
14265         
14266         
14267     },
14268     
14269     setActive: function(state)
14270     {
14271         Roo.log("panel - set active " + this.tabId + "=" + state);
14272         
14273         this.active = state;
14274         if (!state) {
14275             this.el.removeClass('active');
14276             
14277         } else  if (!this.el.hasClass('active')) {
14278             this.el.addClass('active');
14279         }
14280         this.fireEvent('changed', this, state);
14281     }
14282     
14283     
14284 });
14285  
14286
14287  
14288
14289  /*
14290  * - LGPL
14291  *
14292  * DateField
14293  * 
14294  */
14295
14296 /**
14297  * @class Roo.bootstrap.DateField
14298  * @extends Roo.bootstrap.Input
14299  * Bootstrap DateField class
14300  * @cfg {Number} weekStart default 0
14301  * @cfg {Number} weekStart default 0
14302  * @cfg {Number} viewMode default empty, (months|years)
14303  * @cfg {Number} minViewMode default empty, (months|years)
14304  * @cfg {Number} startDate default -Infinity
14305  * @cfg {Number} endDate default Infinity
14306  * @cfg {Boolean} todayHighlight default false
14307  * @cfg {Boolean} todayBtn default false
14308  * @cfg {Boolean} calendarWeeks default false
14309  * @cfg {Object} daysOfWeekDisabled default empty
14310  * 
14311  * @cfg {Boolean} keyboardNavigation default true
14312  * @cfg {String} language default en
14313  * 
14314  * @constructor
14315  * Create a new DateField
14316  * @param {Object} config The config object
14317  */
14318
14319 Roo.bootstrap.DateField = function(config){
14320     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14321      this.addEvents({
14322             /**
14323              * @event show
14324              * Fires when this field show.
14325              * @param {Roo.bootstrap.DateField} this
14326              * @param {Mixed} date The date value
14327              */
14328             show : true,
14329             /**
14330              * @event show
14331              * Fires when this field hide.
14332              * @param {Roo.bootstrap.DateField} this
14333              * @param {Mixed} date The date value
14334              */
14335             hide : true,
14336             /**
14337              * @event select
14338              * Fires when select a date.
14339              * @param {Roo.bootstrap.DateField} this
14340              * @param {Mixed} date The date value
14341              */
14342             select : true
14343         });
14344 };
14345
14346 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14347     
14348     /**
14349      * @cfg {String} format
14350      * The default date format string which can be overriden for localization support.  The format must be
14351      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14352      */
14353     format : "m/d/y",
14354     /**
14355      * @cfg {String} altFormats
14356      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14357      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14358      */
14359     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14360     
14361     weekStart : 0,
14362     
14363     viewMode : '',
14364     
14365     minViewMode : '',
14366     
14367     todayHighlight : false,
14368     
14369     todayBtn: false,
14370     
14371     language: 'en',
14372     
14373     keyboardNavigation: true,
14374     
14375     calendarWeeks: false,
14376     
14377     startDate: -Infinity,
14378     
14379     endDate: Infinity,
14380     
14381     daysOfWeekDisabled: [],
14382     
14383     _events: [],
14384     
14385     UTCDate: function()
14386     {
14387         return new Date(Date.UTC.apply(Date, arguments));
14388     },
14389     
14390     UTCToday: function()
14391     {
14392         var today = new Date();
14393         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14394     },
14395     
14396     getDate: function() {
14397             var d = this.getUTCDate();
14398             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14399     },
14400     
14401     getUTCDate: function() {
14402             return this.date;
14403     },
14404     
14405     setDate: function(d) {
14406             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14407     },
14408     
14409     setUTCDate: function(d) {
14410             this.date = d;
14411             this.setValue(this.formatDate(this.date));
14412     },
14413         
14414     onRender: function(ct, position)
14415     {
14416         
14417         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14418         
14419         this.language = this.language || 'en';
14420         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14421         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14422         
14423         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14424         this.format = this.format || 'm/d/y';
14425         this.isInline = false;
14426         this.isInput = true;
14427         this.component = this.el.select('.add-on', true).first() || false;
14428         this.component = (this.component && this.component.length === 0) ? false : this.component;
14429         this.hasInput = this.component && this.inputEL().length;
14430         
14431         if (typeof(this.minViewMode === 'string')) {
14432             switch (this.minViewMode) {
14433                 case 'months':
14434                     this.minViewMode = 1;
14435                     break;
14436                 case 'years':
14437                     this.minViewMode = 2;
14438                     break;
14439                 default:
14440                     this.minViewMode = 0;
14441                     break;
14442             }
14443         }
14444         
14445         if (typeof(this.viewMode === 'string')) {
14446             switch (this.viewMode) {
14447                 case 'months':
14448                     this.viewMode = 1;
14449                     break;
14450                 case 'years':
14451                     this.viewMode = 2;
14452                     break;
14453                 default:
14454                     this.viewMode = 0;
14455                     break;
14456             }
14457         }
14458                 
14459         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14460         
14461 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14462         
14463         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14464         
14465         this.picker().on('mousedown', this.onMousedown, this);
14466         this.picker().on('click', this.onClick, this);
14467         
14468         this.picker().addClass('datepicker-dropdown');
14469         
14470         this.startViewMode = this.viewMode;
14471         
14472         
14473         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14474             if(!this.calendarWeeks){
14475                 v.remove();
14476                 return;
14477             };
14478             
14479             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14480             v.attr('colspan', function(i, val){
14481                 return parseInt(val) + 1;
14482             });
14483         })
14484                         
14485         
14486         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14487         
14488         this.setStartDate(this.startDate);
14489         this.setEndDate(this.endDate);
14490         
14491         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14492         
14493         this.fillDow();
14494         this.fillMonths();
14495         this.update();
14496         this.showMode();
14497         
14498         if(this.isInline) {
14499             this.show();
14500         }
14501     },
14502     
14503     picker : function()
14504     {
14505         return this.pickerEl;
14506 //        return this.el.select('.datepicker', true).first();
14507     },
14508     
14509     fillDow: function()
14510     {
14511         var dowCnt = this.weekStart;
14512         
14513         var dow = {
14514             tag: 'tr',
14515             cn: [
14516                 
14517             ]
14518         };
14519         
14520         if(this.calendarWeeks){
14521             dow.cn.push({
14522                 tag: 'th',
14523                 cls: 'cw',
14524                 html: '&nbsp;'
14525             })
14526         }
14527         
14528         while (dowCnt < this.weekStart + 7) {
14529             dow.cn.push({
14530                 tag: 'th',
14531                 cls: 'dow',
14532                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14533             });
14534         }
14535         
14536         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14537     },
14538     
14539     fillMonths: function()
14540     {    
14541         var i = 0
14542         var months = this.picker().select('>.datepicker-months td', true).first();
14543         
14544         months.dom.innerHTML = '';
14545         
14546         while (i < 12) {
14547             var month = {
14548                 tag: 'span',
14549                 cls: 'month',
14550                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14551             }
14552             
14553             months.createChild(month);
14554         }
14555         
14556     },
14557     
14558     update: function()
14559     {
14560         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;
14561         
14562         if (this.date < this.startDate) {
14563             this.viewDate = new Date(this.startDate);
14564         } else if (this.date > this.endDate) {
14565             this.viewDate = new Date(this.endDate);
14566         } else {
14567             this.viewDate = new Date(this.date);
14568         }
14569         
14570         this.fill();
14571     },
14572     
14573     fill: function() 
14574     {
14575         var d = new Date(this.viewDate),
14576                 year = d.getUTCFullYear(),
14577                 month = d.getUTCMonth(),
14578                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14579                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14580                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14581                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14582                 currentDate = this.date && this.date.valueOf(),
14583                 today = this.UTCToday();
14584         
14585         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14586         
14587 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14588         
14589 //        this.picker.select('>tfoot th.today').
14590 //                                              .text(dates[this.language].today)
14591 //                                              .toggle(this.todayBtn !== false);
14592     
14593         this.updateNavArrows();
14594         this.fillMonths();
14595                                                 
14596         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14597         
14598         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14599          
14600         prevMonth.setUTCDate(day);
14601         
14602         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14603         
14604         var nextMonth = new Date(prevMonth);
14605         
14606         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14607         
14608         nextMonth = nextMonth.valueOf();
14609         
14610         var fillMonths = false;
14611         
14612         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14613         
14614         while(prevMonth.valueOf() < nextMonth) {
14615             var clsName = '';
14616             
14617             if (prevMonth.getUTCDay() === this.weekStart) {
14618                 if(fillMonths){
14619                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14620                 }
14621                     
14622                 fillMonths = {
14623                     tag: 'tr',
14624                     cn: []
14625                 };
14626                 
14627                 if(this.calendarWeeks){
14628                     // ISO 8601: First week contains first thursday.
14629                     // ISO also states week starts on Monday, but we can be more abstract here.
14630                     var
14631                     // Start of current week: based on weekstart/current date
14632                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14633                     // Thursday of this week
14634                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14635                     // First Thursday of year, year from thursday
14636                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14637                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14638                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14639                     
14640                     fillMonths.cn.push({
14641                         tag: 'td',
14642                         cls: 'cw',
14643                         html: calWeek
14644                     });
14645                 }
14646             }
14647             
14648             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14649                 clsName += ' old';
14650             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14651                 clsName += ' new';
14652             }
14653             if (this.todayHighlight &&
14654                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14655                 prevMonth.getUTCMonth() == today.getMonth() &&
14656                 prevMonth.getUTCDate() == today.getDate()) {
14657                 clsName += ' today';
14658             }
14659             
14660             if (currentDate && prevMonth.valueOf() === currentDate) {
14661                 clsName += ' active';
14662             }
14663             
14664             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14665                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14666                     clsName += ' disabled';
14667             }
14668             
14669             fillMonths.cn.push({
14670                 tag: 'td',
14671                 cls: 'day ' + clsName,
14672                 html: prevMonth.getDate()
14673             })
14674             
14675             prevMonth.setDate(prevMonth.getDate()+1);
14676         }
14677           
14678         var currentYear = this.date && this.date.getUTCFullYear();
14679         var currentMonth = this.date && this.date.getUTCMonth();
14680         
14681         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14682         
14683         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14684             v.removeClass('active');
14685             
14686             if(currentYear === year && k === currentMonth){
14687                 v.addClass('active');
14688             }
14689             
14690             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14691                 v.addClass('disabled');
14692             }
14693             
14694         });
14695         
14696         
14697         year = parseInt(year/10, 10) * 10;
14698         
14699         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14700         
14701         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14702         
14703         year -= 1;
14704         for (var i = -1; i < 11; i++) {
14705             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14706                 tag: 'span',
14707                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14708                 html: year
14709             })
14710             
14711             year += 1;
14712         }
14713     },
14714     
14715     showMode: function(dir) 
14716     {
14717         if (dir) {
14718             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14719         }
14720         Roo.each(this.picker().select('>div',true).elements, function(v){
14721             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14722             v.hide();
14723         });
14724         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14725     },
14726     
14727     place: function()
14728     {
14729         if(this.isInline) return;
14730         
14731         this.picker().removeClass(['bottom', 'top']);
14732         
14733         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14734             /*
14735              * place to the top of element!
14736              *
14737              */
14738             
14739             this.picker().addClass('top');
14740             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14741             
14742             return;
14743         }
14744         
14745         this.picker().addClass('bottom');
14746         
14747         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14748     },
14749     
14750     parseDate : function(value)
14751     {
14752         if(!value || value instanceof Date){
14753             return value;
14754         }
14755         var v = Date.parseDate(value, this.format);
14756         if (!v && this.useIso) {
14757             v = Date.parseDate(value, 'Y-m-d');
14758         }
14759         if(!v && this.altFormats){
14760             if(!this.altFormatsArray){
14761                 this.altFormatsArray = this.altFormats.split("|");
14762             }
14763             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14764                 v = Date.parseDate(value, this.altFormatsArray[i]);
14765             }
14766         }
14767         return v;
14768     },
14769     
14770     formatDate : function(date, fmt)
14771     {
14772         return (!date || !(date instanceof Date)) ?
14773         date : date.dateFormat(fmt || this.format);
14774     },
14775     
14776     onFocus : function()
14777     {
14778         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14779         this.show();
14780     },
14781     
14782     onBlur : function()
14783     {
14784         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14785         
14786         var d = this.inputEl().getValue();
14787         
14788         this.setValue(d);
14789                 
14790         this.hide();
14791     },
14792     
14793     show : function()
14794     {
14795         this.picker().show();
14796         this.update();
14797         this.place();
14798         
14799         this.fireEvent('show', this, this.date);
14800     },
14801     
14802     hide : function()
14803     {
14804         if(this.isInline) return;
14805         this.picker().hide();
14806         this.viewMode = this.startViewMode;
14807         this.showMode();
14808         
14809         this.fireEvent('hide', this, this.date);
14810         
14811     },
14812     
14813     onMousedown: function(e)
14814     {
14815         e.stopPropagation();
14816         e.preventDefault();
14817     },
14818     
14819     keyup: function(e)
14820     {
14821         Roo.bootstrap.DateField.superclass.keyup.call(this);
14822         this.update();
14823     },
14824
14825     setValue: function(v)
14826     {
14827         var d = new Date(v).clearTime();
14828         
14829         if(isNaN(d.getTime())){
14830             this.date = this.viewDate = '';
14831             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14832             return;
14833         }
14834         
14835         v = this.formatDate(d);
14836         
14837         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14838         
14839         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14840      
14841         this.update();
14842
14843         this.fireEvent('select', this, this.date);
14844         
14845     },
14846     
14847     getValue: function()
14848     {
14849         return this.formatDate(this.date);
14850     },
14851     
14852     fireKey: function(e)
14853     {
14854         if (!this.picker().isVisible()){
14855             if (e.keyCode == 27) // allow escape to hide and re-show picker
14856                 this.show();
14857             return;
14858         }
14859         
14860         var dateChanged = false,
14861         dir, day, month,
14862         newDate, newViewDate;
14863         
14864         switch(e.keyCode){
14865             case 27: // escape
14866                 this.hide();
14867                 e.preventDefault();
14868                 break;
14869             case 37: // left
14870             case 39: // right
14871                 if (!this.keyboardNavigation) break;
14872                 dir = e.keyCode == 37 ? -1 : 1;
14873                 
14874                 if (e.ctrlKey){
14875                     newDate = this.moveYear(this.date, dir);
14876                     newViewDate = this.moveYear(this.viewDate, dir);
14877                 } else if (e.shiftKey){
14878                     newDate = this.moveMonth(this.date, dir);
14879                     newViewDate = this.moveMonth(this.viewDate, dir);
14880                 } else {
14881                     newDate = new Date(this.date);
14882                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14883                     newViewDate = new Date(this.viewDate);
14884                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14885                 }
14886                 if (this.dateWithinRange(newDate)){
14887                     this.date = newDate;
14888                     this.viewDate = newViewDate;
14889                     this.setValue(this.formatDate(this.date));
14890 //                    this.update();
14891                     e.preventDefault();
14892                     dateChanged = true;
14893                 }
14894                 break;
14895             case 38: // up
14896             case 40: // down
14897                 if (!this.keyboardNavigation) break;
14898                 dir = e.keyCode == 38 ? -1 : 1;
14899                 if (e.ctrlKey){
14900                     newDate = this.moveYear(this.date, dir);
14901                     newViewDate = this.moveYear(this.viewDate, dir);
14902                 } else if (e.shiftKey){
14903                     newDate = this.moveMonth(this.date, dir);
14904                     newViewDate = this.moveMonth(this.viewDate, dir);
14905                 } else {
14906                     newDate = new Date(this.date);
14907                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14908                     newViewDate = new Date(this.viewDate);
14909                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14910                 }
14911                 if (this.dateWithinRange(newDate)){
14912                     this.date = newDate;
14913                     this.viewDate = newViewDate;
14914                     this.setValue(this.formatDate(this.date));
14915 //                    this.update();
14916                     e.preventDefault();
14917                     dateChanged = true;
14918                 }
14919                 break;
14920             case 13: // enter
14921                 this.setValue(this.formatDate(this.date));
14922                 this.hide();
14923                 e.preventDefault();
14924                 break;
14925             case 9: // tab
14926                 this.setValue(this.formatDate(this.date));
14927                 this.hide();
14928                 break;
14929             case 16: // shift
14930             case 17: // ctrl
14931             case 18: // alt
14932                 break;
14933             default :
14934                 this.hide();
14935                 
14936         }
14937     },
14938     
14939     
14940     onClick: function(e) 
14941     {
14942         e.stopPropagation();
14943         e.preventDefault();
14944         
14945         var target = e.getTarget();
14946         
14947         if(target.nodeName.toLowerCase() === 'i'){
14948             target = Roo.get(target).dom.parentNode;
14949         }
14950         
14951         var nodeName = target.nodeName;
14952         var className = target.className;
14953         var html = target.innerHTML;
14954         
14955         switch(nodeName.toLowerCase()) {
14956             case 'th':
14957                 switch(className) {
14958                     case 'switch':
14959                         this.showMode(1);
14960                         break;
14961                     case 'prev':
14962                     case 'next':
14963                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14964                         switch(this.viewMode){
14965                                 case 0:
14966                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14967                                         break;
14968                                 case 1:
14969                                 case 2:
14970                                         this.viewDate = this.moveYear(this.viewDate, dir);
14971                                         break;
14972                         }
14973                         this.fill();
14974                         break;
14975                     case 'today':
14976                         var date = new Date();
14977                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14978 //                        this.fill()
14979                         this.setValue(this.formatDate(this.date));
14980                         
14981                         this.hide();
14982                         break;
14983                 }
14984                 break;
14985             case 'span':
14986                 if (className.indexOf('disabled') === -1) {
14987                     this.viewDate.setUTCDate(1);
14988                     if (className.indexOf('month') !== -1) {
14989                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14990                     } else {
14991                         var year = parseInt(html, 10) || 0;
14992                         this.viewDate.setUTCFullYear(year);
14993                         
14994                     }
14995                     this.showMode(-1);
14996                     this.fill();
14997                 }
14998                 break;
14999                 
15000             case 'td':
15001                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15002                     var day = parseInt(html, 10) || 1;
15003                     var year = this.viewDate.getUTCFullYear(),
15004                         month = this.viewDate.getUTCMonth();
15005
15006                     if (className.indexOf('old') !== -1) {
15007                         if(month === 0 ){
15008                             month = 11;
15009                             year -= 1;
15010                         }else{
15011                             month -= 1;
15012                         }
15013                     } else if (className.indexOf('new') !== -1) {
15014                         if (month == 11) {
15015                             month = 0;
15016                             year += 1;
15017                         } else {
15018                             month += 1;
15019                         }
15020                     }
15021                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15022                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15023 //                    this.fill();
15024                     this.setValue(this.formatDate(this.date));
15025                     this.hide();
15026                 }
15027                 break;
15028         }
15029     },
15030     
15031     setStartDate: function(startDate)
15032     {
15033         this.startDate = startDate || -Infinity;
15034         if (this.startDate !== -Infinity) {
15035             this.startDate = this.parseDate(this.startDate);
15036         }
15037         this.update();
15038         this.updateNavArrows();
15039     },
15040
15041     setEndDate: function(endDate)
15042     {
15043         this.endDate = endDate || Infinity;
15044         if (this.endDate !== Infinity) {
15045             this.endDate = this.parseDate(this.endDate);
15046         }
15047         this.update();
15048         this.updateNavArrows();
15049     },
15050     
15051     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15052     {
15053         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15054         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15055             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15056         }
15057         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15058             return parseInt(d, 10);
15059         });
15060         this.update();
15061         this.updateNavArrows();
15062     },
15063     
15064     updateNavArrows: function() 
15065     {
15066         var d = new Date(this.viewDate),
15067         year = d.getUTCFullYear(),
15068         month = d.getUTCMonth();
15069         
15070         Roo.each(this.picker().select('.prev', true).elements, function(v){
15071             v.show();
15072             switch (this.viewMode) {
15073                 case 0:
15074
15075                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15076                         v.hide();
15077                     }
15078                     break;
15079                 case 1:
15080                 case 2:
15081                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15082                         v.hide();
15083                     }
15084                     break;
15085             }
15086         });
15087         
15088         Roo.each(this.picker().select('.next', true).elements, function(v){
15089             v.show();
15090             switch (this.viewMode) {
15091                 case 0:
15092
15093                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15094                         v.hide();
15095                     }
15096                     break;
15097                 case 1:
15098                 case 2:
15099                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15100                         v.hide();
15101                     }
15102                     break;
15103             }
15104         })
15105     },
15106     
15107     moveMonth: function(date, dir)
15108     {
15109         if (!dir) return date;
15110         var new_date = new Date(date.valueOf()),
15111         day = new_date.getUTCDate(),
15112         month = new_date.getUTCMonth(),
15113         mag = Math.abs(dir),
15114         new_month, test;
15115         dir = dir > 0 ? 1 : -1;
15116         if (mag == 1){
15117             test = dir == -1
15118             // If going back one month, make sure month is not current month
15119             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15120             ? function(){
15121                 return new_date.getUTCMonth() == month;
15122             }
15123             // If going forward one month, make sure month is as expected
15124             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15125             : function(){
15126                 return new_date.getUTCMonth() != new_month;
15127             };
15128             new_month = month + dir;
15129             new_date.setUTCMonth(new_month);
15130             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15131             if (new_month < 0 || new_month > 11)
15132                 new_month = (new_month + 12) % 12;
15133         } else {
15134             // For magnitudes >1, move one month at a time...
15135             for (var i=0; i<mag; i++)
15136                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15137                 new_date = this.moveMonth(new_date, dir);
15138             // ...then reset the day, keeping it in the new month
15139             new_month = new_date.getUTCMonth();
15140             new_date.setUTCDate(day);
15141             test = function(){
15142                 return new_month != new_date.getUTCMonth();
15143             };
15144         }
15145         // Common date-resetting loop -- if date is beyond end of month, make it
15146         // end of month
15147         while (test()){
15148             new_date.setUTCDate(--day);
15149             new_date.setUTCMonth(new_month);
15150         }
15151         return new_date;
15152     },
15153
15154     moveYear: function(date, dir)
15155     {
15156         return this.moveMonth(date, dir*12);
15157     },
15158
15159     dateWithinRange: function(date)
15160     {
15161         return date >= this.startDate && date <= this.endDate;
15162     },
15163
15164     
15165     remove: function() 
15166     {
15167         this.picker().remove();
15168     }
15169    
15170 });
15171
15172 Roo.apply(Roo.bootstrap.DateField,  {
15173     
15174     head : {
15175         tag: 'thead',
15176         cn: [
15177         {
15178             tag: 'tr',
15179             cn: [
15180             {
15181                 tag: 'th',
15182                 cls: 'prev',
15183                 html: '<i class="fa fa-arrow-left"/>'
15184             },
15185             {
15186                 tag: 'th',
15187                 cls: 'switch',
15188                 colspan: '5'
15189             },
15190             {
15191                 tag: 'th',
15192                 cls: 'next',
15193                 html: '<i class="fa fa-arrow-right"/>'
15194             }
15195
15196             ]
15197         }
15198         ]
15199     },
15200     
15201     content : {
15202         tag: 'tbody',
15203         cn: [
15204         {
15205             tag: 'tr',
15206             cn: [
15207             {
15208                 tag: 'td',
15209                 colspan: '7'
15210             }
15211             ]
15212         }
15213         ]
15214     },
15215     
15216     footer : {
15217         tag: 'tfoot',
15218         cn: [
15219         {
15220             tag: 'tr',
15221             cn: [
15222             {
15223                 tag: 'th',
15224                 colspan: '7',
15225                 cls: 'today'
15226             }
15227                     
15228             ]
15229         }
15230         ]
15231     },
15232     
15233     dates:{
15234         en: {
15235             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15236             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15237             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15238             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15239             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15240             today: "Today"
15241         }
15242     },
15243     
15244     modes: [
15245     {
15246         clsName: 'days',
15247         navFnc: 'Month',
15248         navStep: 1
15249     },
15250     {
15251         clsName: 'months',
15252         navFnc: 'FullYear',
15253         navStep: 1
15254     },
15255     {
15256         clsName: 'years',
15257         navFnc: 'FullYear',
15258         navStep: 10
15259     }]
15260 });
15261
15262 Roo.apply(Roo.bootstrap.DateField,  {
15263   
15264     template : {
15265         tag: 'div',
15266         cls: 'datepicker dropdown-menu',
15267         cn: [
15268         {
15269             tag: 'div',
15270             cls: 'datepicker-days',
15271             cn: [
15272             {
15273                 tag: 'table',
15274                 cls: 'table-condensed',
15275                 cn:[
15276                 Roo.bootstrap.DateField.head,
15277                 {
15278                     tag: 'tbody'
15279                 },
15280                 Roo.bootstrap.DateField.footer
15281                 ]
15282             }
15283             ]
15284         },
15285         {
15286             tag: 'div',
15287             cls: 'datepicker-months',
15288             cn: [
15289             {
15290                 tag: 'table',
15291                 cls: 'table-condensed',
15292                 cn:[
15293                 Roo.bootstrap.DateField.head,
15294                 Roo.bootstrap.DateField.content,
15295                 Roo.bootstrap.DateField.footer
15296                 ]
15297             }
15298             ]
15299         },
15300         {
15301             tag: 'div',
15302             cls: 'datepicker-years',
15303             cn: [
15304             {
15305                 tag: 'table',
15306                 cls: 'table-condensed',
15307                 cn:[
15308                 Roo.bootstrap.DateField.head,
15309                 Roo.bootstrap.DateField.content,
15310                 Roo.bootstrap.DateField.footer
15311                 ]
15312             }
15313             ]
15314         }
15315         ]
15316     }
15317 });
15318
15319  
15320
15321  /*
15322  * - LGPL
15323  *
15324  * TimeField
15325  * 
15326  */
15327
15328 /**
15329  * @class Roo.bootstrap.TimeField
15330  * @extends Roo.bootstrap.Input
15331  * Bootstrap DateField class
15332  * 
15333  * 
15334  * @constructor
15335  * Create a new TimeField
15336  * @param {Object} config The config object
15337  */
15338
15339 Roo.bootstrap.TimeField = function(config){
15340     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15341     this.addEvents({
15342             /**
15343              * @event show
15344              * Fires when this field show.
15345              * @param {Roo.bootstrap.DateField} this
15346              * @param {Mixed} date The date value
15347              */
15348             show : true,
15349             /**
15350              * @event show
15351              * Fires when this field hide.
15352              * @param {Roo.bootstrap.DateField} this
15353              * @param {Mixed} date The date value
15354              */
15355             hide : true,
15356             /**
15357              * @event select
15358              * Fires when select a date.
15359              * @param {Roo.bootstrap.DateField} this
15360              * @param {Mixed} date The date value
15361              */
15362             select : true
15363         });
15364 };
15365
15366 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15367     
15368     /**
15369      * @cfg {String} format
15370      * The default time format string which can be overriden for localization support.  The format must be
15371      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15372      */
15373     format : "H:i",
15374        
15375     onRender: function(ct, position)
15376     {
15377         
15378         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15379                 
15380         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15381         
15382         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15383         
15384         this.pop = this.picker().select('>.datepicker-time',true).first();
15385         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15386         
15387         this.picker().on('mousedown', this.onMousedown, this);
15388         this.picker().on('click', this.onClick, this);
15389         
15390         this.picker().addClass('datepicker-dropdown');
15391     
15392         this.fillTime();
15393         this.update();
15394             
15395         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15396         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15397         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15398         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15399         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15400         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15401
15402     },
15403     
15404     fireKey: function(e){
15405         if (!this.picker().isVisible()){
15406             if (e.keyCode == 27) // allow escape to hide and re-show picker
15407                 this.show();
15408             return;
15409         }
15410
15411         e.preventDefault();
15412         
15413         switch(e.keyCode){
15414             case 27: // escape
15415                 this.hide();
15416                 break;
15417             case 37: // left
15418             case 39: // right
15419                 this.onTogglePeriod();
15420                 break;
15421             case 38: // up
15422                 this.onIncrementMinutes();
15423                 break;
15424             case 40: // down
15425                 this.onDecrementMinutes();
15426                 break;
15427             case 13: // enter
15428             case 9: // tab
15429                 this.setTime();
15430                 break;
15431         }
15432     },
15433     
15434     onClick: function(e) {
15435         e.stopPropagation();
15436         e.preventDefault();
15437     },
15438     
15439     picker : function()
15440     {
15441         return this.el.select('.datepicker', true).first();
15442     },
15443     
15444     fillTime: function()
15445     {    
15446         var time = this.pop.select('tbody', true).first();
15447         
15448         time.dom.innerHTML = '';
15449         
15450         time.createChild({
15451             tag: 'tr',
15452             cn: [
15453                 {
15454                     tag: 'td',
15455                     cn: [
15456                         {
15457                             tag: 'a',
15458                             href: '#',
15459                             cls: 'btn',
15460                             cn: [
15461                                 {
15462                                     tag: 'span',
15463                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15464                                 }
15465                             ]
15466                         } 
15467                     ]
15468                 },
15469                 {
15470                     tag: 'td',
15471                     cls: 'separator'
15472                 },
15473                 {
15474                     tag: 'td',
15475                     cn: [
15476                         {
15477                             tag: 'a',
15478                             href: '#',
15479                             cls: 'btn',
15480                             cn: [
15481                                 {
15482                                     tag: 'span',
15483                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15484                                 }
15485                             ]
15486                         }
15487                     ]
15488                 },
15489                 {
15490                     tag: 'td',
15491                     cls: 'separator'
15492                 }
15493             ]
15494         });
15495         
15496         time.createChild({
15497             tag: 'tr',
15498             cn: [
15499                 {
15500                     tag: 'td',
15501                     cn: [
15502                         {
15503                             tag: 'span',
15504                             cls: 'timepicker-hour',
15505                             html: '00'
15506                         }  
15507                     ]
15508                 },
15509                 {
15510                     tag: 'td',
15511                     cls: 'separator',
15512                     html: ':'
15513                 },
15514                 {
15515                     tag: 'td',
15516                     cn: [
15517                         {
15518                             tag: 'span',
15519                             cls: 'timepicker-minute',
15520                             html: '00'
15521                         }  
15522                     ]
15523                 },
15524                 {
15525                     tag: 'td',
15526                     cls: 'separator'
15527                 },
15528                 {
15529                     tag: 'td',
15530                     cn: [
15531                         {
15532                             tag: 'button',
15533                             type: 'button',
15534                             cls: 'btn btn-primary period',
15535                             html: 'AM'
15536                             
15537                         }
15538                     ]
15539                 }
15540             ]
15541         });
15542         
15543         time.createChild({
15544             tag: 'tr',
15545             cn: [
15546                 {
15547                     tag: 'td',
15548                     cn: [
15549                         {
15550                             tag: 'a',
15551                             href: '#',
15552                             cls: 'btn',
15553                             cn: [
15554                                 {
15555                                     tag: 'span',
15556                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15557                                 }
15558                             ]
15559                         }
15560                     ]
15561                 },
15562                 {
15563                     tag: 'td',
15564                     cls: 'separator'
15565                 },
15566                 {
15567                     tag: 'td',
15568                     cn: [
15569                         {
15570                             tag: 'a',
15571                             href: '#',
15572                             cls: 'btn',
15573                             cn: [
15574                                 {
15575                                     tag: 'span',
15576                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15577                                 }
15578                             ]
15579                         }
15580                     ]
15581                 },
15582                 {
15583                     tag: 'td',
15584                     cls: 'separator'
15585                 }
15586             ]
15587         });
15588         
15589     },
15590     
15591     update: function()
15592     {
15593         
15594         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15595         
15596         this.fill();
15597     },
15598     
15599     fill: function() 
15600     {
15601         var hours = this.time.getHours();
15602         var minutes = this.time.getMinutes();
15603         var period = 'AM';
15604         
15605         if(hours > 11){
15606             period = 'PM';
15607         }
15608         
15609         if(hours == 0){
15610             hours = 12;
15611         }
15612         
15613         
15614         if(hours > 12){
15615             hours = hours - 12;
15616         }
15617         
15618         if(hours < 10){
15619             hours = '0' + hours;
15620         }
15621         
15622         if(minutes < 10){
15623             minutes = '0' + minutes;
15624         }
15625         
15626         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15627         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15628         this.pop.select('button', true).first().dom.innerHTML = period;
15629         
15630     },
15631     
15632     place: function()
15633     {   
15634         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15635         
15636         var cls = ['bottom'];
15637         
15638         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15639             cls.pop();
15640             cls.push('top');
15641         }
15642         
15643         cls.push('right');
15644         
15645         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15646             cls.pop();
15647             cls.push('left');
15648         }
15649         
15650         this.picker().addClass(cls.join('-'));
15651         
15652         var _this = this;
15653         
15654         Roo.each(cls, function(c){
15655             if(c == 'bottom'){
15656                 _this.picker().setTop(_this.inputEl().getHeight());
15657                 return;
15658             }
15659             if(c == 'top'){
15660                 _this.picker().setTop(0 - _this.picker().getHeight());
15661                 return;
15662             }
15663             
15664             if(c == 'left'){
15665                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15666                 return;
15667             }
15668             if(c == 'right'){
15669                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15670                 return;
15671             }
15672         });
15673         
15674     },
15675   
15676     onFocus : function()
15677     {
15678         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15679         this.show();
15680     },
15681     
15682     onBlur : function()
15683     {
15684         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15685         this.hide();
15686     },
15687     
15688     show : function()
15689     {
15690         this.picker().show();
15691         this.pop.show();
15692         this.update();
15693         this.place();
15694         
15695         this.fireEvent('show', this, this.date);
15696     },
15697     
15698     hide : function()
15699     {
15700         this.picker().hide();
15701         this.pop.hide();
15702         
15703         this.fireEvent('hide', this, this.date);
15704     },
15705     
15706     setTime : function()
15707     {
15708         this.hide();
15709         this.setValue(this.time.format(this.format));
15710         
15711         this.fireEvent('select', this, this.date);
15712         
15713         
15714     },
15715     
15716     onMousedown: function(e){
15717         e.stopPropagation();
15718         e.preventDefault();
15719     },
15720     
15721     onIncrementHours: function()
15722     {
15723         Roo.log('onIncrementHours');
15724         this.time = this.time.add(Date.HOUR, 1);
15725         this.update();
15726         
15727     },
15728     
15729     onDecrementHours: function()
15730     {
15731         Roo.log('onDecrementHours');
15732         this.time = this.time.add(Date.HOUR, -1);
15733         this.update();
15734     },
15735     
15736     onIncrementMinutes: function()
15737     {
15738         Roo.log('onIncrementMinutes');
15739         this.time = this.time.add(Date.MINUTE, 1);
15740         this.update();
15741     },
15742     
15743     onDecrementMinutes: function()
15744     {
15745         Roo.log('onDecrementMinutes');
15746         this.time = this.time.add(Date.MINUTE, -1);
15747         this.update();
15748     },
15749     
15750     onTogglePeriod: function()
15751     {
15752         Roo.log('onTogglePeriod');
15753         this.time = this.time.add(Date.HOUR, 12);
15754         this.update();
15755     }
15756     
15757    
15758 });
15759
15760 Roo.apply(Roo.bootstrap.TimeField,  {
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: '',
15787                     cn: [
15788                         {
15789                             tag: 'button',
15790                             cls: 'btn btn-info ok',
15791                             html: 'OK'
15792                         }
15793                     ]
15794                 }
15795
15796                 ]
15797             }
15798         ]
15799     }
15800 });
15801
15802 Roo.apply(Roo.bootstrap.TimeField,  {
15803   
15804     template : {
15805         tag: 'div',
15806         cls: 'datepicker dropdown-menu',
15807         cn: [
15808             {
15809                 tag: 'div',
15810                 cls: 'datepicker-time',
15811                 cn: [
15812                 {
15813                     tag: 'table',
15814                     cls: 'table-condensed',
15815                     cn:[
15816                     Roo.bootstrap.TimeField.content,
15817                     Roo.bootstrap.TimeField.footer
15818                     ]
15819                 }
15820                 ]
15821             }
15822         ]
15823     }
15824 });
15825
15826  
15827
15828  /*
15829  * - LGPL
15830  *
15831  * CheckBox
15832  * 
15833  */
15834
15835 /**
15836  * @class Roo.bootstrap.CheckBox
15837  * @extends Roo.bootstrap.Input
15838  * Bootstrap CheckBox class
15839  * 
15840  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15841  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15842  * @cfg {String} boxLabel The text that appears beside the checkbox
15843  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15844  * @cfg {Boolean} checked initnal the element
15845  * 
15846  * 
15847  * @constructor
15848  * Create a new CheckBox
15849  * @param {Object} config The config object
15850  */
15851
15852 Roo.bootstrap.CheckBox = function(config){
15853     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15854    
15855         this.addEvents({
15856             /**
15857             * @event check
15858             * Fires when the element is checked or unchecked.
15859             * @param {Roo.bootstrap.CheckBox} this This input
15860             * @param {Boolean} checked The new checked value
15861             */
15862            check : true
15863         });
15864 };
15865
15866 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15867     
15868     inputType: 'checkbox',
15869     inputValue: 1,
15870     valueOff: 0,
15871     boxLabel: false,
15872     checked: false,
15873     weight : false,
15874     
15875     getAutoCreate : function()
15876     {
15877         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15878         
15879         var id = Roo.id();
15880         
15881         var cfg = {};
15882         
15883         cfg.cls = 'form-group checkbox' //input-group
15884         
15885         
15886         
15887         
15888         var input =  {
15889             tag: 'input',
15890             id : id,
15891             type : this.inputType,
15892             value : (!this.checked) ? this.valueOff : this.inputValue,
15893             cls : 'roo-checkbox', //'form-box',
15894             placeholder : this.placeholder || ''
15895             
15896         };
15897         
15898         if (this.weight) { // Validity check?
15899             cfg.cls += " checkbox-" + this.weight;
15900         }
15901         
15902         if (this.disabled) {
15903             input.disabled=true;
15904         }
15905         
15906         if(this.checked){
15907             input.checked = this.checked;
15908         }
15909         
15910         if (this.name) {
15911             input.name = this.name;
15912         }
15913         
15914         if (this.size) {
15915             input.cls += ' input-' + this.size;
15916         }
15917         
15918         var settings=this;
15919         ['xs','sm','md','lg'].map(function(size){
15920             if (settings[size]) {
15921                 cfg.cls += ' col-' + size + '-' + settings[size];
15922             }
15923         });
15924         
15925        
15926         
15927         var inputblock = input;
15928         
15929         
15930         
15931         
15932         if (this.before || this.after) {
15933             
15934             inputblock = {
15935                 cls : 'input-group',
15936                 cn :  [] 
15937             };
15938             if (this.before) {
15939                 inputblock.cn.push({
15940                     tag :'span',
15941                     cls : 'input-group-addon',
15942                     html : this.before
15943                 });
15944             }
15945             inputblock.cn.push(input);
15946             if (this.after) {
15947                 inputblock.cn.push({
15948                     tag :'span',
15949                     cls : 'input-group-addon',
15950                     html : this.after
15951                 });
15952             }
15953             
15954         };
15955         
15956         if (align ==='left' && this.fieldLabel.length) {
15957                 Roo.log("left and has label");
15958                 cfg.cn = [
15959                     
15960                     {
15961                         tag: 'label',
15962                         'for' :  id,
15963                         cls : 'control-label col-md-' + this.labelWidth,
15964                         html : this.fieldLabel
15965                         
15966                     },
15967                     {
15968                         cls : "col-md-" + (12 - this.labelWidth), 
15969                         cn: [
15970                             inputblock
15971                         ]
15972                     }
15973                     
15974                 ];
15975         } else if ( this.fieldLabel.length) {
15976                 Roo.log(" label");
15977                 cfg.cn = [
15978                    
15979                     {
15980                         tag: this.boxLabel ? 'span' : 'label',
15981                         'for': id,
15982                         cls: 'control-label box-input-label',
15983                         //cls : 'input-group-addon',
15984                         html : this.fieldLabel
15985                         
15986                     },
15987                     
15988                     inputblock
15989                     
15990                 ];
15991
15992         } else {
15993             
15994                 Roo.log(" no label && no align");
15995                 cfg.cn = [  inputblock ] ;
15996                 
15997                 
15998         };
15999          if(this.boxLabel){
16000             cfg.cn.push( {
16001                 tag: 'label',
16002                 'for': id,
16003                 cls: 'box-label',
16004                 html: this.boxLabel
16005                 
16006             });
16007         }
16008         
16009         
16010        
16011         return cfg;
16012         
16013     },
16014     
16015     /**
16016      * return the real input element.
16017      */
16018     inputEl: function ()
16019     {
16020         return this.el.select('input.roo-checkbox',true).first();
16021     },
16022     
16023     label: function()
16024     {
16025         return this.el.select('label.control-label',true).first();
16026     },
16027     
16028     initEvents : function()
16029     {
16030 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16031         
16032         this.inputEl().on('click', this.onClick,  this);
16033         
16034     },
16035     
16036     onClick : function()
16037     {   
16038         this.setChecked(!this.checked);
16039     },
16040     
16041     setChecked : function(state,suppressEvent)
16042     {
16043         this.checked = state;
16044         
16045         this.inputEl().dom.checked = state;
16046         
16047         if(suppressEvent !== true){
16048             this.fireEvent('check', this, state);
16049         }
16050         
16051         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16052         
16053     },
16054     
16055     setValue : function(v,suppressEvent)
16056     {
16057         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16058     }
16059     
16060 });
16061
16062  
16063 /*
16064  * - LGPL
16065  *
16066  * Radio
16067  * 
16068  */
16069
16070 /**
16071  * @class Roo.bootstrap.Radio
16072  * @extends Roo.bootstrap.CheckBox
16073  * Bootstrap Radio class
16074
16075  * @constructor
16076  * Create a new Radio
16077  * @param {Object} config The config object
16078  */
16079
16080 Roo.bootstrap.Radio = function(config){
16081     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16082    
16083 };
16084
16085 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16086     
16087     inputType: 'radio',
16088     inputValue: '',
16089     valueOff: '',
16090     
16091     getAutoCreate : function()
16092     {
16093         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16094         
16095         var id = Roo.id();
16096         
16097         var cfg = {};
16098         
16099         cfg.cls = 'form-group radio' //input-group
16100         
16101         var input =  {
16102             tag: 'input',
16103             id : id,
16104             type : this.inputType,
16105             value : (!this.checked) ? this.valueOff : this.inputValue,
16106             cls : 'roo-radio',
16107             placeholder : this.placeholder || ''
16108             
16109         };
16110           if (this.weight) { // Validity check?
16111             cfg.cls += " radio-" + this.weight;
16112         }
16113         if (this.disabled) {
16114             input.disabled=true;
16115         }
16116         
16117         if(this.checked){
16118             input.checked = this.checked;
16119         }
16120         
16121         if (this.name) {
16122             input.name = this.name;
16123         }
16124         
16125         if (this.size) {
16126             input.cls += ' input-' + this.size;
16127         }
16128         
16129         var settings=this;
16130         ['xs','sm','md','lg'].map(function(size){
16131             if (settings[size]) {
16132                 cfg.cls += ' col-' + size + '-' + settings[size];
16133             }
16134         });
16135         
16136         var inputblock = input;
16137         
16138         if (this.before || this.after) {
16139             
16140             inputblock = {
16141                 cls : 'input-group',
16142                 cn :  [] 
16143             };
16144             if (this.before) {
16145                 inputblock.cn.push({
16146                     tag :'span',
16147                     cls : 'input-group-addon',
16148                     html : this.before
16149                 });
16150             }
16151             inputblock.cn.push(input);
16152             if (this.after) {
16153                 inputblock.cn.push({
16154                     tag :'span',
16155                     cls : 'input-group-addon',
16156                     html : this.after
16157                 });
16158             }
16159             
16160         };
16161         
16162         if (align ==='left' && this.fieldLabel.length) {
16163                 Roo.log("left and has label");
16164                 cfg.cn = [
16165                     
16166                     {
16167                         tag: 'label',
16168                         'for' :  id,
16169                         cls : 'control-label col-md-' + this.labelWidth,
16170                         html : this.fieldLabel
16171                         
16172                     },
16173                     {
16174                         cls : "col-md-" + (12 - this.labelWidth), 
16175                         cn: [
16176                             inputblock
16177                         ]
16178                     }
16179                     
16180                 ];
16181         } else if ( this.fieldLabel.length) {
16182                 Roo.log(" label");
16183                  cfg.cn = [
16184                    
16185                     {
16186                         tag: 'label',
16187                         'for': id,
16188                         cls: 'control-label box-input-label',
16189                         //cls : 'input-group-addon',
16190                         html : this.fieldLabel
16191                         
16192                     },
16193                     
16194                     inputblock
16195                     
16196                 ];
16197
16198         } else {
16199             
16200                    Roo.log(" no label && no align");
16201                 cfg.cn = [
16202                     
16203                         inputblock
16204                     
16205                 ];
16206                 
16207                 
16208         };
16209         
16210         if(this.boxLabel){
16211             cfg.cn.push({
16212                 tag: 'label',
16213                 'for': id,
16214                 cls: 'box-label',
16215                 html: this.boxLabel
16216             })
16217         }
16218         
16219         return cfg;
16220         
16221     },
16222     inputEl: function ()
16223     {
16224         return this.el.select('input.roo-radio',true).first();
16225     },
16226     onClick : function()
16227     {   
16228         this.setChecked(true);
16229     },
16230     
16231     setChecked : function(state,suppressEvent)
16232     {
16233         if(state){
16234             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16235                 v.dom.checked = false;
16236             });
16237         }
16238         
16239         this.checked = state;
16240         this.inputEl().dom.checked = state;
16241         
16242         if(suppressEvent !== true){
16243             this.fireEvent('check', this, state);
16244         }
16245         
16246         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16247         
16248     },
16249     
16250     getGroupValue : function()
16251     {
16252         var value = ''
16253         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16254             if(v.dom.checked == true){
16255                 value = v.dom.value;
16256             }
16257         });
16258         
16259         return value;
16260     },
16261     
16262     /**
16263      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16264      * @return {Mixed} value The field value
16265      */
16266     getValue : function(){
16267         return this.getGroupValue();
16268     }
16269     
16270 });
16271
16272  
16273 //<script type="text/javascript">
16274
16275 /*
16276  * Based  Ext JS Library 1.1.1
16277  * Copyright(c) 2006-2007, Ext JS, LLC.
16278  * LGPL
16279  *
16280  */
16281  
16282 /**
16283  * @class Roo.HtmlEditorCore
16284  * @extends Roo.Component
16285  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16286  *
16287  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16288  */
16289
16290 Roo.HtmlEditorCore = function(config){
16291     
16292     
16293     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16294     this.addEvents({
16295         /**
16296          * @event initialize
16297          * Fires when the editor is fully initialized (including the iframe)
16298          * @param {Roo.HtmlEditorCore} this
16299          */
16300         initialize: true,
16301         /**
16302          * @event activate
16303          * Fires when the editor is first receives the focus. Any insertion must wait
16304          * until after this event.
16305          * @param {Roo.HtmlEditorCore} this
16306          */
16307         activate: true,
16308          /**
16309          * @event beforesync
16310          * Fires before the textarea is updated with content from the editor iframe. Return false
16311          * to cancel the sync.
16312          * @param {Roo.HtmlEditorCore} this
16313          * @param {String} html
16314          */
16315         beforesync: true,
16316          /**
16317          * @event beforepush
16318          * Fires before the iframe editor is updated with content from the textarea. Return false
16319          * to cancel the push.
16320          * @param {Roo.HtmlEditorCore} this
16321          * @param {String} html
16322          */
16323         beforepush: true,
16324          /**
16325          * @event sync
16326          * Fires when the textarea is updated with content from the editor iframe.
16327          * @param {Roo.HtmlEditorCore} this
16328          * @param {String} html
16329          */
16330         sync: true,
16331          /**
16332          * @event push
16333          * Fires when the iframe editor is updated with content from the textarea.
16334          * @param {Roo.HtmlEditorCore} this
16335          * @param {String} html
16336          */
16337         push: true,
16338         
16339         /**
16340          * @event editorevent
16341          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16342          * @param {Roo.HtmlEditorCore} this
16343          */
16344         editorevent: true
16345     });
16346      
16347 };
16348
16349
16350 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16351
16352
16353      /**
16354      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16355      */
16356     
16357     owner : false,
16358     
16359      /**
16360      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16361      *                        Roo.resizable.
16362      */
16363     resizable : false,
16364      /**
16365      * @cfg {Number} height (in pixels)
16366      */   
16367     height: 300,
16368    /**
16369      * @cfg {Number} width (in pixels)
16370      */   
16371     width: 500,
16372     
16373     /**
16374      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16375      * 
16376      */
16377     stylesheets: false,
16378     
16379     // id of frame..
16380     frameId: false,
16381     
16382     // private properties
16383     validationEvent : false,
16384     deferHeight: true,
16385     initialized : false,
16386     activated : false,
16387     sourceEditMode : false,
16388     onFocus : Roo.emptyFn,
16389     iframePad:3,
16390     hideMode:'offsets',
16391     
16392     clearUp: true,
16393     
16394      
16395     
16396
16397     /**
16398      * Protected method that will not generally be called directly. It
16399      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16400      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16401      */
16402     getDocMarkup : function(){
16403         // body styles..
16404         var st = '';
16405         Roo.log(this.stylesheets);
16406         
16407         // inherit styels from page...?? 
16408         if (this.stylesheets === false) {
16409             
16410             Roo.get(document.head).select('style').each(function(node) {
16411                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16412             });
16413             
16414             Roo.get(document.head).select('link').each(function(node) { 
16415                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16416             });
16417             
16418         } else if (!this.stylesheets.length) {
16419                 // simple..
16420                 st = '<style type="text/css">' +
16421                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16422                    '</style>';
16423         } else {
16424             Roo.each(this.stylesheets, function(s) {
16425                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16426             });
16427             
16428         }
16429         
16430         st +=  '<style type="text/css">' +
16431             'IMG { cursor: pointer } ' +
16432         '</style>';
16433
16434         
16435         return '<html><head>' + st  +
16436             //<style type="text/css">' +
16437             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16438             //'</style>' +
16439             ' </head><body class="roo-htmleditor-body"></body></html>';
16440     },
16441
16442     // private
16443     onRender : function(ct, position)
16444     {
16445         var _t = this;
16446         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16447         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16448         
16449         
16450         this.el.dom.style.border = '0 none';
16451         this.el.dom.setAttribute('tabIndex', -1);
16452         this.el.addClass('x-hidden hide');
16453         
16454         
16455         
16456         if(Roo.isIE){ // fix IE 1px bogus margin
16457             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16458         }
16459        
16460         
16461         this.frameId = Roo.id();
16462         
16463          
16464         
16465         var iframe = this.owner.wrap.createChild({
16466             tag: 'iframe',
16467             cls: 'form-control', // bootstrap..
16468             id: this.frameId,
16469             name: this.frameId,
16470             frameBorder : 'no',
16471             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16472         }, this.el
16473         );
16474         
16475         
16476         this.iframe = iframe.dom;
16477
16478          this.assignDocWin();
16479         
16480         this.doc.designMode = 'on';
16481        
16482         this.doc.open();
16483         this.doc.write(this.getDocMarkup());
16484         this.doc.close();
16485
16486         
16487         var task = { // must defer to wait for browser to be ready
16488             run : function(){
16489                 //console.log("run task?" + this.doc.readyState);
16490                 this.assignDocWin();
16491                 if(this.doc.body || this.doc.readyState == 'complete'){
16492                     try {
16493                         this.doc.designMode="on";
16494                     } catch (e) {
16495                         return;
16496                     }
16497                     Roo.TaskMgr.stop(task);
16498                     this.initEditor.defer(10, this);
16499                 }
16500             },
16501             interval : 10,
16502             duration: 10000,
16503             scope: this
16504         };
16505         Roo.TaskMgr.start(task);
16506
16507         
16508          
16509     },
16510
16511     // private
16512     onResize : function(w, h)
16513     {
16514          Roo.log('resize: ' +w + ',' + h );
16515         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16516         if(!this.iframe){
16517             return;
16518         }
16519         if(typeof w == 'number'){
16520             
16521             this.iframe.style.width = w + 'px';
16522         }
16523         if(typeof h == 'number'){
16524             
16525             this.iframe.style.height = h + 'px';
16526             if(this.doc){
16527                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16528             }
16529         }
16530         
16531     },
16532
16533     /**
16534      * Toggles the editor between standard and source edit mode.
16535      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16536      */
16537     toggleSourceEdit : function(sourceEditMode){
16538         
16539         this.sourceEditMode = sourceEditMode === true;
16540         
16541         if(this.sourceEditMode){
16542  
16543             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16544             
16545         }else{
16546             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16547             //this.iframe.className = '';
16548             this.deferFocus();
16549         }
16550         //this.setSize(this.owner.wrap.getSize());
16551         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16552     },
16553
16554     
16555   
16556
16557     /**
16558      * Protected method that will not generally be called directly. If you need/want
16559      * custom HTML cleanup, this is the method you should override.
16560      * @param {String} html The HTML to be cleaned
16561      * return {String} The cleaned HTML
16562      */
16563     cleanHtml : function(html){
16564         html = String(html);
16565         if(html.length > 5){
16566             if(Roo.isSafari){ // strip safari nonsense
16567                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16568             }
16569         }
16570         if(html == '&nbsp;'){
16571             html = '';
16572         }
16573         return html;
16574     },
16575
16576     /**
16577      * HTML Editor -> Textarea
16578      * Protected method that will not generally be called directly. Syncs the contents
16579      * of the editor iframe with the textarea.
16580      */
16581     syncValue : function(){
16582         if(this.initialized){
16583             var bd = (this.doc.body || this.doc.documentElement);
16584             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16585             var html = bd.innerHTML;
16586             if(Roo.isSafari){
16587                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16588                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16589                 if(m && m[1]){
16590                     html = '<div style="'+m[0]+'">' + html + '</div>';
16591                 }
16592             }
16593             html = this.cleanHtml(html);
16594             // fix up the special chars.. normaly like back quotes in word...
16595             // however we do not want to do this with chinese..
16596             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16597                 var cc = b.charCodeAt();
16598                 if (
16599                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16600                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16601                     (cc >= 0xf900 && cc < 0xfb00 )
16602                 ) {
16603                         return b;
16604                 }
16605                 return "&#"+cc+";" 
16606             });
16607             if(this.owner.fireEvent('beforesync', this, html) !== false){
16608                 this.el.dom.value = html;
16609                 this.owner.fireEvent('sync', this, html);
16610             }
16611         }
16612     },
16613
16614     /**
16615      * Protected method that will not generally be called directly. Pushes the value of the textarea
16616      * into the iframe editor.
16617      */
16618     pushValue : function(){
16619         if(this.initialized){
16620             var v = this.el.dom.value.trim();
16621             
16622 //            if(v.length < 1){
16623 //                v = '&#160;';
16624 //            }
16625             
16626             if(this.owner.fireEvent('beforepush', this, v) !== false){
16627                 var d = (this.doc.body || this.doc.documentElement);
16628                 d.innerHTML = v;
16629                 this.cleanUpPaste();
16630                 this.el.dom.value = d.innerHTML;
16631                 this.owner.fireEvent('push', this, v);
16632             }
16633         }
16634     },
16635
16636     // private
16637     deferFocus : function(){
16638         this.focus.defer(10, this);
16639     },
16640
16641     // doc'ed in Field
16642     focus : function(){
16643         if(this.win && !this.sourceEditMode){
16644             this.win.focus();
16645         }else{
16646             this.el.focus();
16647         }
16648     },
16649     
16650     assignDocWin: function()
16651     {
16652         var iframe = this.iframe;
16653         
16654          if(Roo.isIE){
16655             this.doc = iframe.contentWindow.document;
16656             this.win = iframe.contentWindow;
16657         } else {
16658 //            if (!Roo.get(this.frameId)) {
16659 //                return;
16660 //            }
16661 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16662 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16663             
16664             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16665                 return;
16666             }
16667             
16668             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16669             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16670         }
16671     },
16672     
16673     // private
16674     initEditor : function(){
16675         //console.log("INIT EDITOR");
16676         this.assignDocWin();
16677         
16678         
16679         
16680         this.doc.designMode="on";
16681         this.doc.open();
16682         this.doc.write(this.getDocMarkup());
16683         this.doc.close();
16684         
16685         var dbody = (this.doc.body || this.doc.documentElement);
16686         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16687         // this copies styles from the containing element into thsi one..
16688         // not sure why we need all of this..
16689         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16690         
16691         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16692         //ss['background-attachment'] = 'fixed'; // w3c
16693         dbody.bgProperties = 'fixed'; // ie
16694         //Roo.DomHelper.applyStyles(dbody, ss);
16695         Roo.EventManager.on(this.doc, {
16696             //'mousedown': this.onEditorEvent,
16697             'mouseup': this.onEditorEvent,
16698             'dblclick': this.onEditorEvent,
16699             'click': this.onEditorEvent,
16700             'keyup': this.onEditorEvent,
16701             buffer:100,
16702             scope: this
16703         });
16704         if(Roo.isGecko){
16705             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16706         }
16707         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16708             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16709         }
16710         this.initialized = true;
16711
16712         this.owner.fireEvent('initialize', this);
16713         this.pushValue();
16714     },
16715
16716     // private
16717     onDestroy : function(){
16718         
16719         
16720         
16721         if(this.rendered){
16722             
16723             //for (var i =0; i < this.toolbars.length;i++) {
16724             //    // fixme - ask toolbars for heights?
16725             //    this.toolbars[i].onDestroy();
16726            // }
16727             
16728             //this.wrap.dom.innerHTML = '';
16729             //this.wrap.remove();
16730         }
16731     },
16732
16733     // private
16734     onFirstFocus : function(){
16735         
16736         this.assignDocWin();
16737         
16738         
16739         this.activated = true;
16740          
16741     
16742         if(Roo.isGecko){ // prevent silly gecko errors
16743             this.win.focus();
16744             var s = this.win.getSelection();
16745             if(!s.focusNode || s.focusNode.nodeType != 3){
16746                 var r = s.getRangeAt(0);
16747                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16748                 r.collapse(true);
16749                 this.deferFocus();
16750             }
16751             try{
16752                 this.execCmd('useCSS', true);
16753                 this.execCmd('styleWithCSS', false);
16754             }catch(e){}
16755         }
16756         this.owner.fireEvent('activate', this);
16757     },
16758
16759     // private
16760     adjustFont: function(btn){
16761         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16762         //if(Roo.isSafari){ // safari
16763         //    adjust *= 2;
16764        // }
16765         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16766         if(Roo.isSafari){ // safari
16767             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16768             v =  (v < 10) ? 10 : v;
16769             v =  (v > 48) ? 48 : v;
16770             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16771             
16772         }
16773         
16774         
16775         v = Math.max(1, v+adjust);
16776         
16777         this.execCmd('FontSize', v  );
16778     },
16779
16780     onEditorEvent : function(e){
16781         this.owner.fireEvent('editorevent', this, e);
16782       //  this.updateToolbar();
16783         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16784     },
16785
16786     insertTag : function(tg)
16787     {
16788         // could be a bit smarter... -> wrap the current selected tRoo..
16789         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16790             
16791             range = this.createRange(this.getSelection());
16792             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16793             wrappingNode.appendChild(range.extractContents());
16794             range.insertNode(wrappingNode);
16795
16796             return;
16797             
16798             
16799             
16800         }
16801         this.execCmd("formatblock",   tg);
16802         
16803     },
16804     
16805     insertText : function(txt)
16806     {
16807         
16808         
16809         var range = this.createRange();
16810         range.deleteContents();
16811                //alert(Sender.getAttribute('label'));
16812                
16813         range.insertNode(this.doc.createTextNode(txt));
16814     } ,
16815     
16816      
16817
16818     /**
16819      * Executes a Midas editor command on the editor document and performs necessary focus and
16820      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16821      * @param {String} cmd The Midas command
16822      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16823      */
16824     relayCmd : function(cmd, value){
16825         this.win.focus();
16826         this.execCmd(cmd, value);
16827         this.owner.fireEvent('editorevent', this);
16828         //this.updateToolbar();
16829         this.owner.deferFocus();
16830     },
16831
16832     /**
16833      * Executes a Midas editor command directly on the editor document.
16834      * For visual commands, you should use {@link #relayCmd} instead.
16835      * <b>This should only be called after the editor is initialized.</b>
16836      * @param {String} cmd The Midas command
16837      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16838      */
16839     execCmd : function(cmd, value){
16840         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16841         this.syncValue();
16842     },
16843  
16844  
16845    
16846     /**
16847      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16848      * to insert tRoo.
16849      * @param {String} text | dom node.. 
16850      */
16851     insertAtCursor : function(text)
16852     {
16853         
16854         
16855         
16856         if(!this.activated){
16857             return;
16858         }
16859         /*
16860         if(Roo.isIE){
16861             this.win.focus();
16862             var r = this.doc.selection.createRange();
16863             if(r){
16864                 r.collapse(true);
16865                 r.pasteHTML(text);
16866                 this.syncValue();
16867                 this.deferFocus();
16868             
16869             }
16870             return;
16871         }
16872         */
16873         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16874             this.win.focus();
16875             
16876             
16877             // from jquery ui (MIT licenced)
16878             var range, node;
16879             var win = this.win;
16880             
16881             if (win.getSelection && win.getSelection().getRangeAt) {
16882                 range = win.getSelection().getRangeAt(0);
16883                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16884                 range.insertNode(node);
16885             } else if (win.document.selection && win.document.selection.createRange) {
16886                 // no firefox support
16887                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16888                 win.document.selection.createRange().pasteHTML(txt);
16889             } else {
16890                 // no firefox support
16891                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16892                 this.execCmd('InsertHTML', txt);
16893             } 
16894             
16895             this.syncValue();
16896             
16897             this.deferFocus();
16898         }
16899     },
16900  // private
16901     mozKeyPress : function(e){
16902         if(e.ctrlKey){
16903             var c = e.getCharCode(), cmd;
16904           
16905             if(c > 0){
16906                 c = String.fromCharCode(c).toLowerCase();
16907                 switch(c){
16908                     case 'b':
16909                         cmd = 'bold';
16910                         break;
16911                     case 'i':
16912                         cmd = 'italic';
16913                         break;
16914                     
16915                     case 'u':
16916                         cmd = 'underline';
16917                         break;
16918                     
16919                     case 'v':
16920                         this.cleanUpPaste.defer(100, this);
16921                         return;
16922                         
16923                 }
16924                 if(cmd){
16925                     this.win.focus();
16926                     this.execCmd(cmd);
16927                     this.deferFocus();
16928                     e.preventDefault();
16929                 }
16930                 
16931             }
16932         }
16933     },
16934
16935     // private
16936     fixKeys : function(){ // load time branching for fastest keydown performance
16937         if(Roo.isIE){
16938             return function(e){
16939                 var k = e.getKey(), r;
16940                 if(k == e.TAB){
16941                     e.stopEvent();
16942                     r = this.doc.selection.createRange();
16943                     if(r){
16944                         r.collapse(true);
16945                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16946                         this.deferFocus();
16947                     }
16948                     return;
16949                 }
16950                 
16951                 if(k == e.ENTER){
16952                     r = this.doc.selection.createRange();
16953                     if(r){
16954                         var target = r.parentElement();
16955                         if(!target || target.tagName.toLowerCase() != 'li'){
16956                             e.stopEvent();
16957                             r.pasteHTML('<br />');
16958                             r.collapse(false);
16959                             r.select();
16960                         }
16961                     }
16962                 }
16963                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16964                     this.cleanUpPaste.defer(100, this);
16965                     return;
16966                 }
16967                 
16968                 
16969             };
16970         }else if(Roo.isOpera){
16971             return function(e){
16972                 var k = e.getKey();
16973                 if(k == e.TAB){
16974                     e.stopEvent();
16975                     this.win.focus();
16976                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16977                     this.deferFocus();
16978                 }
16979                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16980                     this.cleanUpPaste.defer(100, this);
16981                     return;
16982                 }
16983                 
16984             };
16985         }else if(Roo.isSafari){
16986             return function(e){
16987                 var k = e.getKey();
16988                 
16989                 if(k == e.TAB){
16990                     e.stopEvent();
16991                     this.execCmd('InsertText','\t');
16992                     this.deferFocus();
16993                     return;
16994                 }
16995                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16996                     this.cleanUpPaste.defer(100, this);
16997                     return;
16998                 }
16999                 
17000              };
17001         }
17002     }(),
17003     
17004     getAllAncestors: function()
17005     {
17006         var p = this.getSelectedNode();
17007         var a = [];
17008         if (!p) {
17009             a.push(p); // push blank onto stack..
17010             p = this.getParentElement();
17011         }
17012         
17013         
17014         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17015             a.push(p);
17016             p = p.parentNode;
17017         }
17018         a.push(this.doc.body);
17019         return a;
17020     },
17021     lastSel : false,
17022     lastSelNode : false,
17023     
17024     
17025     getSelection : function() 
17026     {
17027         this.assignDocWin();
17028         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17029     },
17030     
17031     getSelectedNode: function() 
17032     {
17033         // this may only work on Gecko!!!
17034         
17035         // should we cache this!!!!
17036         
17037         
17038         
17039          
17040         var range = this.createRange(this.getSelection()).cloneRange();
17041         
17042         if (Roo.isIE) {
17043             var parent = range.parentElement();
17044             while (true) {
17045                 var testRange = range.duplicate();
17046                 testRange.moveToElementText(parent);
17047                 if (testRange.inRange(range)) {
17048                     break;
17049                 }
17050                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17051                     break;
17052                 }
17053                 parent = parent.parentElement;
17054             }
17055             return parent;
17056         }
17057         
17058         // is ancestor a text element.
17059         var ac =  range.commonAncestorContainer;
17060         if (ac.nodeType == 3) {
17061             ac = ac.parentNode;
17062         }
17063         
17064         var ar = ac.childNodes;
17065          
17066         var nodes = [];
17067         var other_nodes = [];
17068         var has_other_nodes = false;
17069         for (var i=0;i<ar.length;i++) {
17070             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17071                 continue;
17072             }
17073             // fullly contained node.
17074             
17075             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17076                 nodes.push(ar[i]);
17077                 continue;
17078             }
17079             
17080             // probably selected..
17081             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17082                 other_nodes.push(ar[i]);
17083                 continue;
17084             }
17085             // outer..
17086             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17087                 continue;
17088             }
17089             
17090             
17091             has_other_nodes = true;
17092         }
17093         if (!nodes.length && other_nodes.length) {
17094             nodes= other_nodes;
17095         }
17096         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17097             return false;
17098         }
17099         
17100         return nodes[0];
17101     },
17102     createRange: function(sel)
17103     {
17104         // this has strange effects when using with 
17105         // top toolbar - not sure if it's a great idea.
17106         //this.editor.contentWindow.focus();
17107         if (typeof sel != "undefined") {
17108             try {
17109                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17110             } catch(e) {
17111                 return this.doc.createRange();
17112             }
17113         } else {
17114             return this.doc.createRange();
17115         }
17116     },
17117     getParentElement: function()
17118     {
17119         
17120         this.assignDocWin();
17121         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17122         
17123         var range = this.createRange(sel);
17124          
17125         try {
17126             var p = range.commonAncestorContainer;
17127             while (p.nodeType == 3) { // text node
17128                 p = p.parentNode;
17129             }
17130             return p;
17131         } catch (e) {
17132             return null;
17133         }
17134     
17135     },
17136     /***
17137      *
17138      * Range intersection.. the hard stuff...
17139      *  '-1' = before
17140      *  '0' = hits..
17141      *  '1' = after.
17142      *         [ -- selected range --- ]
17143      *   [fail]                        [fail]
17144      *
17145      *    basically..
17146      *      if end is before start or  hits it. fail.
17147      *      if start is after end or hits it fail.
17148      *
17149      *   if either hits (but other is outside. - then it's not 
17150      *   
17151      *    
17152      **/
17153     
17154     
17155     // @see http://www.thismuchiknow.co.uk/?p=64.
17156     rangeIntersectsNode : function(range, node)
17157     {
17158         var nodeRange = node.ownerDocument.createRange();
17159         try {
17160             nodeRange.selectNode(node);
17161         } catch (e) {
17162             nodeRange.selectNodeContents(node);
17163         }
17164     
17165         var rangeStartRange = range.cloneRange();
17166         rangeStartRange.collapse(true);
17167     
17168         var rangeEndRange = range.cloneRange();
17169         rangeEndRange.collapse(false);
17170     
17171         var nodeStartRange = nodeRange.cloneRange();
17172         nodeStartRange.collapse(true);
17173     
17174         var nodeEndRange = nodeRange.cloneRange();
17175         nodeEndRange.collapse(false);
17176     
17177         return rangeStartRange.compareBoundaryPoints(
17178                  Range.START_TO_START, nodeEndRange) == -1 &&
17179                rangeEndRange.compareBoundaryPoints(
17180                  Range.START_TO_START, nodeStartRange) == 1;
17181         
17182          
17183     },
17184     rangeCompareNode : function(range, node)
17185     {
17186         var nodeRange = node.ownerDocument.createRange();
17187         try {
17188             nodeRange.selectNode(node);
17189         } catch (e) {
17190             nodeRange.selectNodeContents(node);
17191         }
17192         
17193         
17194         range.collapse(true);
17195     
17196         nodeRange.collapse(true);
17197      
17198         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17199         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17200          
17201         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17202         
17203         var nodeIsBefore   =  ss == 1;
17204         var nodeIsAfter    = ee == -1;
17205         
17206         if (nodeIsBefore && nodeIsAfter)
17207             return 0; // outer
17208         if (!nodeIsBefore && nodeIsAfter)
17209             return 1; //right trailed.
17210         
17211         if (nodeIsBefore && !nodeIsAfter)
17212             return 2;  // left trailed.
17213         // fully contined.
17214         return 3;
17215     },
17216
17217     // private? - in a new class?
17218     cleanUpPaste :  function()
17219     {
17220         // cleans up the whole document..
17221         Roo.log('cleanuppaste');
17222         
17223         this.cleanUpChildren(this.doc.body);
17224         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17225         if (clean != this.doc.body.innerHTML) {
17226             this.doc.body.innerHTML = clean;
17227         }
17228         
17229     },
17230     
17231     cleanWordChars : function(input) {// change the chars to hex code
17232         var he = Roo.HtmlEditorCore;
17233         
17234         var output = input;
17235         Roo.each(he.swapCodes, function(sw) { 
17236             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17237             
17238             output = output.replace(swapper, sw[1]);
17239         });
17240         
17241         return output;
17242     },
17243     
17244     
17245     cleanUpChildren : function (n)
17246     {
17247         if (!n.childNodes.length) {
17248             return;
17249         }
17250         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17251            this.cleanUpChild(n.childNodes[i]);
17252         }
17253     },
17254     
17255     
17256         
17257     
17258     cleanUpChild : function (node)
17259     {
17260         var ed = this;
17261         //console.log(node);
17262         if (node.nodeName == "#text") {
17263             // clean up silly Windows -- stuff?
17264             return; 
17265         }
17266         if (node.nodeName == "#comment") {
17267             node.parentNode.removeChild(node);
17268             // clean up silly Windows -- stuff?
17269             return; 
17270         }
17271         
17272         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17273             // remove node.
17274             node.parentNode.removeChild(node);
17275             return;
17276             
17277         }
17278         
17279         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17280         
17281         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17282         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17283         
17284         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17285         //    remove_keep_children = true;
17286         //}
17287         
17288         if (remove_keep_children) {
17289             this.cleanUpChildren(node);
17290             // inserts everything just before this node...
17291             while (node.childNodes.length) {
17292                 var cn = node.childNodes[0];
17293                 node.removeChild(cn);
17294                 node.parentNode.insertBefore(cn, node);
17295             }
17296             node.parentNode.removeChild(node);
17297             return;
17298         }
17299         
17300         if (!node.attributes || !node.attributes.length) {
17301             this.cleanUpChildren(node);
17302             return;
17303         }
17304         
17305         function cleanAttr(n,v)
17306         {
17307             
17308             if (v.match(/^\./) || v.match(/^\//)) {
17309                 return;
17310             }
17311             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17312                 return;
17313             }
17314             if (v.match(/^#/)) {
17315                 return;
17316             }
17317 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17318             node.removeAttribute(n);
17319             
17320         }
17321         
17322         function cleanStyle(n,v)
17323         {
17324             if (v.match(/expression/)) { //XSS?? should we even bother..
17325                 node.removeAttribute(n);
17326                 return;
17327             }
17328             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17329             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17330             
17331             
17332             var parts = v.split(/;/);
17333             var clean = [];
17334             
17335             Roo.each(parts, function(p) {
17336                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17337                 if (!p.length) {
17338                     return true;
17339                 }
17340                 var l = p.split(':').shift().replace(/\s+/g,'');
17341                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17342                 
17343                 if ( cblack.indexOf(l) > -1) {
17344 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17345                     //node.removeAttribute(n);
17346                     return true;
17347                 }
17348                 //Roo.log()
17349                 // only allow 'c whitelisted system attributes'
17350                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17351 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17352                     //node.removeAttribute(n);
17353                     return true;
17354                 }
17355                 
17356                 
17357                  
17358                 
17359                 clean.push(p);
17360                 return true;
17361             });
17362             if (clean.length) { 
17363                 node.setAttribute(n, clean.join(';'));
17364             } else {
17365                 node.removeAttribute(n);
17366             }
17367             
17368         }
17369         
17370         
17371         for (var i = node.attributes.length-1; i > -1 ; i--) {
17372             var a = node.attributes[i];
17373             //console.log(a);
17374             
17375             if (a.name.toLowerCase().substr(0,2)=='on')  {
17376                 node.removeAttribute(a.name);
17377                 continue;
17378             }
17379             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17380                 node.removeAttribute(a.name);
17381                 continue;
17382             }
17383             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17384                 cleanAttr(a.name,a.value); // fixme..
17385                 continue;
17386             }
17387             if (a.name == 'style') {
17388                 cleanStyle(a.name,a.value);
17389                 continue;
17390             }
17391             /// clean up MS crap..
17392             // tecnically this should be a list of valid class'es..
17393             
17394             
17395             if (a.name == 'class') {
17396                 if (a.value.match(/^Mso/)) {
17397                     node.className = '';
17398                 }
17399                 
17400                 if (a.value.match(/body/)) {
17401                     node.className = '';
17402                 }
17403                 continue;
17404             }
17405             
17406             // style cleanup!?
17407             // class cleanup?
17408             
17409         }
17410         
17411         
17412         this.cleanUpChildren(node);
17413         
17414         
17415     },
17416     /**
17417      * Clean up MS wordisms...
17418      */
17419     cleanWord : function(node)
17420     {
17421         var _t = this;
17422         var cleanWordChildren = function()
17423         {
17424             if (!node.childNodes.length) {
17425                 return;
17426             }
17427             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17428                _t.cleanWord(node.childNodes[i]);
17429             }
17430         }
17431         
17432         
17433         if (!node) {
17434             this.cleanWord(this.doc.body);
17435             return;
17436         }
17437         if (node.nodeName == "#text") {
17438             // clean up silly Windows -- stuff?
17439             return; 
17440         }
17441         if (node.nodeName == "#comment") {
17442             node.parentNode.removeChild(node);
17443             // clean up silly Windows -- stuff?
17444             return; 
17445         }
17446         
17447         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17448             node.parentNode.removeChild(node);
17449             return;
17450         }
17451         
17452         // remove - but keep children..
17453         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17454             while (node.childNodes.length) {
17455                 var cn = node.childNodes[0];
17456                 node.removeChild(cn);
17457                 node.parentNode.insertBefore(cn, node);
17458             }
17459             node.parentNode.removeChild(node);
17460             cleanWordChildren();
17461             return;
17462         }
17463         // clean styles
17464         if (node.className.length) {
17465             
17466             var cn = node.className.split(/\W+/);
17467             var cna = [];
17468             Roo.each(cn, function(cls) {
17469                 if (cls.match(/Mso[a-zA-Z]+/)) {
17470                     return;
17471                 }
17472                 cna.push(cls);
17473             });
17474             node.className = cna.length ? cna.join(' ') : '';
17475             if (!cna.length) {
17476                 node.removeAttribute("class");
17477             }
17478         }
17479         
17480         if (node.hasAttribute("lang")) {
17481             node.removeAttribute("lang");
17482         }
17483         
17484         if (node.hasAttribute("style")) {
17485             
17486             var styles = node.getAttribute("style").split(";");
17487             var nstyle = [];
17488             Roo.each(styles, function(s) {
17489                 if (!s.match(/:/)) {
17490                     return;
17491                 }
17492                 var kv = s.split(":");
17493                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17494                     return;
17495                 }
17496                 // what ever is left... we allow.
17497                 nstyle.push(s);
17498             });
17499             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17500             if (!nstyle.length) {
17501                 node.removeAttribute('style');
17502             }
17503         }
17504         
17505         cleanWordChildren();
17506         
17507         
17508     },
17509     domToHTML : function(currentElement, depth, nopadtext) {
17510         
17511             depth = depth || 0;
17512             nopadtext = nopadtext || false;
17513         
17514             if (!currentElement) {
17515                 return this.domToHTML(this.doc.body);
17516             }
17517             
17518             //Roo.log(currentElement);
17519             var j;
17520             var allText = false;
17521             var nodeName = currentElement.nodeName;
17522             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17523             
17524             if  (nodeName == '#text') {
17525                 return currentElement.nodeValue;
17526             }
17527             
17528             
17529             var ret = '';
17530             if (nodeName != 'BODY') {
17531                  
17532                 var i = 0;
17533                 // Prints the node tagName, such as <A>, <IMG>, etc
17534                 if (tagName) {
17535                     var attr = [];
17536                     for(i = 0; i < currentElement.attributes.length;i++) {
17537                         // quoting?
17538                         var aname = currentElement.attributes.item(i).name;
17539                         if (!currentElement.attributes.item(i).value.length) {
17540                             continue;
17541                         }
17542                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17543                     }
17544                     
17545                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17546                 } 
17547                 else {
17548                     
17549                     // eack
17550                 }
17551             } else {
17552                 tagName = false;
17553             }
17554             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17555                 return ret;
17556             }
17557             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17558                 nopadtext = true;
17559             }
17560             
17561             
17562             // Traverse the tree
17563             i = 0;
17564             var currentElementChild = currentElement.childNodes.item(i);
17565             var allText = true;
17566             var innerHTML  = '';
17567             lastnode = '';
17568             while (currentElementChild) {
17569                 // Formatting code (indent the tree so it looks nice on the screen)
17570                 var nopad = nopadtext;
17571                 if (lastnode == 'SPAN') {
17572                     nopad  = true;
17573                 }
17574                 // text
17575                 if  (currentElementChild.nodeName == '#text') {
17576                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17577                     if (!nopad && toadd.length > 80) {
17578                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17579                     }
17580                     innerHTML  += toadd;
17581                     
17582                     i++;
17583                     currentElementChild = currentElement.childNodes.item(i);
17584                     lastNode = '';
17585                     continue;
17586                 }
17587                 allText = false;
17588                 
17589                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17590                     
17591                 // Recursively traverse the tree structure of the child node
17592                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17593                 lastnode = currentElementChild.nodeName;
17594                 i++;
17595                 currentElementChild=currentElement.childNodes.item(i);
17596             }
17597             
17598             ret += innerHTML;
17599             
17600             if (!allText) {
17601                     // The remaining code is mostly for formatting the tree
17602                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17603             }
17604             
17605             
17606             if (tagName) {
17607                 ret+= "</"+tagName+">";
17608             }
17609             return ret;
17610             
17611         }
17612     
17613     // hide stuff that is not compatible
17614     /**
17615      * @event blur
17616      * @hide
17617      */
17618     /**
17619      * @event change
17620      * @hide
17621      */
17622     /**
17623      * @event focus
17624      * @hide
17625      */
17626     /**
17627      * @event specialkey
17628      * @hide
17629      */
17630     /**
17631      * @cfg {String} fieldClass @hide
17632      */
17633     /**
17634      * @cfg {String} focusClass @hide
17635      */
17636     /**
17637      * @cfg {String} autoCreate @hide
17638      */
17639     /**
17640      * @cfg {String} inputType @hide
17641      */
17642     /**
17643      * @cfg {String} invalidClass @hide
17644      */
17645     /**
17646      * @cfg {String} invalidText @hide
17647      */
17648     /**
17649      * @cfg {String} msgFx @hide
17650      */
17651     /**
17652      * @cfg {String} validateOnBlur @hide
17653      */
17654 });
17655
17656 Roo.HtmlEditorCore.white = [
17657         'area', 'br', 'img', 'input', 'hr', 'wbr',
17658         
17659        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17660        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17661        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17662        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17663        'table',   'ul',         'xmp', 
17664        
17665        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17666       'thead',   'tr', 
17667      
17668       'dir', 'menu', 'ol', 'ul', 'dl',
17669        
17670       'embed',  'object'
17671 ];
17672
17673
17674 Roo.HtmlEditorCore.black = [
17675     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17676         'applet', // 
17677         'base',   'basefont', 'bgsound', 'blink',  'body', 
17678         'frame',  'frameset', 'head',    'html',   'ilayer', 
17679         'iframe', 'layer',  'link',     'meta',    'object',   
17680         'script', 'style' ,'title',  'xml' // clean later..
17681 ];
17682 Roo.HtmlEditorCore.clean = [
17683     'script', 'style', 'title', 'xml'
17684 ];
17685 Roo.HtmlEditorCore.remove = [
17686     'font'
17687 ];
17688 // attributes..
17689
17690 Roo.HtmlEditorCore.ablack = [
17691     'on'
17692 ];
17693     
17694 Roo.HtmlEditorCore.aclean = [ 
17695     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17696 ];
17697
17698 // protocols..
17699 Roo.HtmlEditorCore.pwhite= [
17700         'http',  'https',  'mailto'
17701 ];
17702
17703 // white listed style attributes.
17704 Roo.HtmlEditorCore.cwhite= [
17705       //  'text-align', /// default is to allow most things..
17706       
17707          
17708 //        'font-size'//??
17709 ];
17710
17711 // black listed style attributes.
17712 Roo.HtmlEditorCore.cblack= [
17713       //  'font-size' -- this can be set by the project 
17714 ];
17715
17716
17717 Roo.HtmlEditorCore.swapCodes   =[ 
17718     [    8211, "--" ], 
17719     [    8212, "--" ], 
17720     [    8216,  "'" ],  
17721     [    8217, "'" ],  
17722     [    8220, '"' ],  
17723     [    8221, '"' ],  
17724     [    8226, "*" ],  
17725     [    8230, "..." ]
17726 ]; 
17727
17728     /*
17729  * - LGPL
17730  *
17731  * HtmlEditor
17732  * 
17733  */
17734
17735 /**
17736  * @class Roo.bootstrap.HtmlEditor
17737  * @extends Roo.bootstrap.TextArea
17738  * Bootstrap HtmlEditor class
17739
17740  * @constructor
17741  * Create a new HtmlEditor
17742  * @param {Object} config The config object
17743  */
17744
17745 Roo.bootstrap.HtmlEditor = function(config){
17746     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17747     if (!this.toolbars) {
17748         this.toolbars = [];
17749     }
17750     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17751     this.addEvents({
17752             /**
17753              * @event initialize
17754              * Fires when the editor is fully initialized (including the iframe)
17755              * @param {HtmlEditor} this
17756              */
17757             initialize: true,
17758             /**
17759              * @event activate
17760              * Fires when the editor is first receives the focus. Any insertion must wait
17761              * until after this event.
17762              * @param {HtmlEditor} this
17763              */
17764             activate: true,
17765              /**
17766              * @event beforesync
17767              * Fires before the textarea is updated with content from the editor iframe. Return false
17768              * to cancel the sync.
17769              * @param {HtmlEditor} this
17770              * @param {String} html
17771              */
17772             beforesync: true,
17773              /**
17774              * @event beforepush
17775              * Fires before the iframe editor is updated with content from the textarea. Return false
17776              * to cancel the push.
17777              * @param {HtmlEditor} this
17778              * @param {String} html
17779              */
17780             beforepush: true,
17781              /**
17782              * @event sync
17783              * Fires when the textarea is updated with content from the editor iframe.
17784              * @param {HtmlEditor} this
17785              * @param {String} html
17786              */
17787             sync: true,
17788              /**
17789              * @event push
17790              * Fires when the iframe editor is updated with content from the textarea.
17791              * @param {HtmlEditor} this
17792              * @param {String} html
17793              */
17794             push: true,
17795              /**
17796              * @event editmodechange
17797              * Fires when the editor switches edit modes
17798              * @param {HtmlEditor} this
17799              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17800              */
17801             editmodechange: true,
17802             /**
17803              * @event editorevent
17804              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17805              * @param {HtmlEditor} this
17806              */
17807             editorevent: true,
17808             /**
17809              * @event firstfocus
17810              * Fires when on first focus - needed by toolbars..
17811              * @param {HtmlEditor} this
17812              */
17813             firstfocus: true,
17814             /**
17815              * @event autosave
17816              * Auto save the htmlEditor value as a file into Events
17817              * @param {HtmlEditor} this
17818              */
17819             autosave: true,
17820             /**
17821              * @event savedpreview
17822              * preview the saved version of htmlEditor
17823              * @param {HtmlEditor} this
17824              */
17825             savedpreview: true
17826         });
17827 };
17828
17829
17830 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17831     
17832     
17833       /**
17834      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17835      */
17836     toolbars : false,
17837    
17838      /**
17839      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17840      *                        Roo.resizable.
17841      */
17842     resizable : false,
17843      /**
17844      * @cfg {Number} height (in pixels)
17845      */   
17846     height: 300,
17847    /**
17848      * @cfg {Number} width (in pixels)
17849      */   
17850     width: false,
17851     
17852     /**
17853      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17854      * 
17855      */
17856     stylesheets: false,
17857     
17858     // id of frame..
17859     frameId: false,
17860     
17861     // private properties
17862     validationEvent : false,
17863     deferHeight: true,
17864     initialized : false,
17865     activated : false,
17866     
17867     onFocus : Roo.emptyFn,
17868     iframePad:3,
17869     hideMode:'offsets',
17870     
17871     
17872     tbContainer : false,
17873     
17874     toolbarContainer :function() {
17875         return this.wrap.select('.x-html-editor-tb',true).first();
17876     },
17877
17878     /**
17879      * Protected method that will not generally be called directly. It
17880      * is called when the editor creates its toolbar. Override this method if you need to
17881      * add custom toolbar buttons.
17882      * @param {HtmlEditor} editor
17883      */
17884     createToolbar : function(){
17885         
17886         Roo.log("create toolbars");
17887         
17888         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17889         this.toolbars[0].render(this.toolbarContainer());
17890         
17891         return;
17892         
17893 //        if (!editor.toolbars || !editor.toolbars.length) {
17894 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17895 //        }
17896 //        
17897 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17898 //            editor.toolbars[i] = Roo.factory(
17899 //                    typeof(editor.toolbars[i]) == 'string' ?
17900 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17901 //                Roo.bootstrap.HtmlEditor);
17902 //            editor.toolbars[i].init(editor);
17903 //        }
17904     },
17905
17906      
17907     // private
17908     onRender : function(ct, position)
17909     {
17910        // Roo.log("Call onRender: " + this.xtype);
17911         var _t = this;
17912         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17913       
17914         this.wrap = this.inputEl().wrap({
17915             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17916         });
17917         
17918         this.editorcore.onRender(ct, position);
17919          
17920         if (this.resizable) {
17921             this.resizeEl = new Roo.Resizable(this.wrap, {
17922                 pinned : true,
17923                 wrap: true,
17924                 dynamic : true,
17925                 minHeight : this.height,
17926                 height: this.height,
17927                 handles : this.resizable,
17928                 width: this.width,
17929                 listeners : {
17930                     resize : function(r, w, h) {
17931                         _t.onResize(w,h); // -something
17932                     }
17933                 }
17934             });
17935             
17936         }
17937         this.createToolbar(this);
17938        
17939         
17940         if(!this.width && this.resizable){
17941             this.setSize(this.wrap.getSize());
17942         }
17943         if (this.resizeEl) {
17944             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17945             // should trigger onReize..
17946         }
17947         
17948     },
17949
17950     // private
17951     onResize : function(w, h)
17952     {
17953         Roo.log('resize: ' +w + ',' + h );
17954         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17955         var ew = false;
17956         var eh = false;
17957         
17958         if(this.inputEl() ){
17959             if(typeof w == 'number'){
17960                 var aw = w - this.wrap.getFrameWidth('lr');
17961                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17962                 ew = aw;
17963             }
17964             if(typeof h == 'number'){
17965                  var tbh = -11;  // fixme it needs to tool bar size!
17966                 for (var i =0; i < this.toolbars.length;i++) {
17967                     // fixme - ask toolbars for heights?
17968                     tbh += this.toolbars[i].el.getHeight();
17969                     //if (this.toolbars[i].footer) {
17970                     //    tbh += this.toolbars[i].footer.el.getHeight();
17971                     //}
17972                 }
17973               
17974                 
17975                 
17976                 
17977                 
17978                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17979                 ah -= 5; // knock a few pixes off for look..
17980                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17981                 var eh = ah;
17982             }
17983         }
17984         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17985         this.editorcore.onResize(ew,eh);
17986         
17987     },
17988
17989     /**
17990      * Toggles the editor between standard and source edit mode.
17991      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17992      */
17993     toggleSourceEdit : function(sourceEditMode)
17994     {
17995         this.editorcore.toggleSourceEdit(sourceEditMode);
17996         
17997         if(this.editorcore.sourceEditMode){
17998             Roo.log('editor - showing textarea');
17999             
18000 //            Roo.log('in');
18001 //            Roo.log(this.syncValue());
18002             this.syncValue();
18003             this.inputEl().removeClass(['hide', 'x-hidden']);
18004             this.inputEl().dom.removeAttribute('tabIndex');
18005             this.inputEl().focus();
18006         }else{
18007             Roo.log('editor - hiding textarea');
18008 //            Roo.log('out')
18009 //            Roo.log(this.pushValue()); 
18010             this.pushValue();
18011             
18012             this.inputEl().addClass(['hide', 'x-hidden']);
18013             this.inputEl().dom.setAttribute('tabIndex', -1);
18014             //this.deferFocus();
18015         }
18016          
18017         if(this.resizable){
18018             this.setSize(this.wrap.getSize());
18019         }
18020         
18021         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18022     },
18023  
18024     // private (for BoxComponent)
18025     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18026
18027     // private (for BoxComponent)
18028     getResizeEl : function(){
18029         return this.wrap;
18030     },
18031
18032     // private (for BoxComponent)
18033     getPositionEl : function(){
18034         return this.wrap;
18035     },
18036
18037     // private
18038     initEvents : function(){
18039         this.originalValue = this.getValue();
18040     },
18041
18042 //    /**
18043 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18044 //     * @method
18045 //     */
18046 //    markInvalid : Roo.emptyFn,
18047 //    /**
18048 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18049 //     * @method
18050 //     */
18051 //    clearInvalid : Roo.emptyFn,
18052
18053     setValue : function(v){
18054         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18055         this.editorcore.pushValue();
18056     },
18057
18058      
18059     // private
18060     deferFocus : function(){
18061         this.focus.defer(10, this);
18062     },
18063
18064     // doc'ed in Field
18065     focus : function(){
18066         this.editorcore.focus();
18067         
18068     },
18069       
18070
18071     // private
18072     onDestroy : function(){
18073         
18074         
18075         
18076         if(this.rendered){
18077             
18078             for (var i =0; i < this.toolbars.length;i++) {
18079                 // fixme - ask toolbars for heights?
18080                 this.toolbars[i].onDestroy();
18081             }
18082             
18083             this.wrap.dom.innerHTML = '';
18084             this.wrap.remove();
18085         }
18086     },
18087
18088     // private
18089     onFirstFocus : function(){
18090         //Roo.log("onFirstFocus");
18091         this.editorcore.onFirstFocus();
18092          for (var i =0; i < this.toolbars.length;i++) {
18093             this.toolbars[i].onFirstFocus();
18094         }
18095         
18096     },
18097     
18098     // private
18099     syncValue : function()
18100     {   
18101         this.editorcore.syncValue();
18102     },
18103     
18104     pushValue : function()
18105     {   
18106         this.editorcore.pushValue();
18107     }
18108      
18109     
18110     // hide stuff that is not compatible
18111     /**
18112      * @event blur
18113      * @hide
18114      */
18115     /**
18116      * @event change
18117      * @hide
18118      */
18119     /**
18120      * @event focus
18121      * @hide
18122      */
18123     /**
18124      * @event specialkey
18125      * @hide
18126      */
18127     /**
18128      * @cfg {String} fieldClass @hide
18129      */
18130     /**
18131      * @cfg {String} focusClass @hide
18132      */
18133     /**
18134      * @cfg {String} autoCreate @hide
18135      */
18136     /**
18137      * @cfg {String} inputType @hide
18138      */
18139     /**
18140      * @cfg {String} invalidClass @hide
18141      */
18142     /**
18143      * @cfg {String} invalidText @hide
18144      */
18145     /**
18146      * @cfg {String} msgFx @hide
18147      */
18148     /**
18149      * @cfg {String} validateOnBlur @hide
18150      */
18151 });
18152  
18153     
18154    
18155    
18156    
18157       
18158 Roo.namespace('Roo.bootstrap.htmleditor');
18159 /**
18160  * @class Roo.bootstrap.HtmlEditorToolbar1
18161  * Basic Toolbar
18162  * 
18163  * Usage:
18164  *
18165  new Roo.bootstrap.HtmlEditor({
18166     ....
18167     toolbars : [
18168         new Roo.bootstrap.HtmlEditorToolbar1({
18169             disable : { fonts: 1 , format: 1, ..., ... , ...],
18170             btns : [ .... ]
18171         })
18172     }
18173      
18174  * 
18175  * @cfg {Object} disable List of elements to disable..
18176  * @cfg {Array} btns List of additional buttons.
18177  * 
18178  * 
18179  * NEEDS Extra CSS? 
18180  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18181  */
18182  
18183 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18184 {
18185     
18186     Roo.apply(this, config);
18187     
18188     // default disabled, based on 'good practice'..
18189     this.disable = this.disable || {};
18190     Roo.applyIf(this.disable, {
18191         fontSize : true,
18192         colors : true,
18193         specialElements : true
18194     });
18195     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18196     
18197     this.editor = config.editor;
18198     this.editorcore = config.editor.editorcore;
18199     
18200     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18201     
18202     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18203     // dont call parent... till later.
18204 }
18205 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18206      
18207     bar : true,
18208     
18209     editor : false,
18210     editorcore : false,
18211     
18212     
18213     formats : [
18214         "p" ,  
18215         "h1","h2","h3","h4","h5","h6", 
18216         "pre", "code", 
18217         "abbr", "acronym", "address", "cite", "samp", "var",
18218         'div','span'
18219     ],
18220     
18221     onRender : function(ct, position)
18222     {
18223        // Roo.log("Call onRender: " + this.xtype);
18224         
18225        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18226        Roo.log(this.el);
18227        this.el.dom.style.marginBottom = '0';
18228        var _this = this;
18229        var editorcore = this.editorcore;
18230        var editor= this.editor;
18231        
18232        var children = [];
18233        var btn = function(id,cmd , toggle, handler){
18234        
18235             var  event = toggle ? 'toggle' : 'click';
18236        
18237             var a = {
18238                 size : 'sm',
18239                 xtype: 'Button',
18240                 xns: Roo.bootstrap,
18241                 glyphicon : id,
18242                 cmd : id || cmd,
18243                 enableToggle:toggle !== false,
18244                 //html : 'submit'
18245                 pressed : toggle ? false : null,
18246                 listeners : {}
18247             }
18248             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18249                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18250             }
18251             children.push(a);
18252             return a;
18253        }
18254         
18255         var style = {
18256                 xtype: 'Button',
18257                 size : 'sm',
18258                 xns: Roo.bootstrap,
18259                 glyphicon : 'font',
18260                 //html : 'submit'
18261                 menu : {
18262                     xtype: 'Menu',
18263                     xns: Roo.bootstrap,
18264                     items:  []
18265                 }
18266         };
18267         Roo.each(this.formats, function(f) {
18268             style.menu.items.push({
18269                 xtype :'MenuItem',
18270                 xns: Roo.bootstrap,
18271                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18272                 tagname : f,
18273                 listeners : {
18274                     click : function()
18275                     {
18276                         editorcore.insertTag(this.tagname);
18277                         editor.focus();
18278                     }
18279                 }
18280                 
18281             });
18282         });
18283          children.push(style);   
18284             
18285             
18286         btn('bold',false,true);
18287         btn('italic',false,true);
18288         btn('align-left', 'justifyleft',true);
18289         btn('align-center', 'justifycenter',true);
18290         btn('align-right' , 'justifyright',true);
18291         btn('link', false, false, function(btn) {
18292             //Roo.log("create link?");
18293             var url = prompt(this.createLinkText, this.defaultLinkValue);
18294             if(url && url != 'http:/'+'/'){
18295                 this.editorcore.relayCmd('createlink', url);
18296             }
18297         }),
18298         btn('list','insertunorderedlist',true);
18299         btn('pencil', false,true, function(btn){
18300                 Roo.log(this);
18301                 
18302                 this.toggleSourceEdit(btn.pressed);
18303         });
18304         /*
18305         var cog = {
18306                 xtype: 'Button',
18307                 size : 'sm',
18308                 xns: Roo.bootstrap,
18309                 glyphicon : 'cog',
18310                 //html : 'submit'
18311                 menu : {
18312                     xtype: 'Menu',
18313                     xns: Roo.bootstrap,
18314                     items:  []
18315                 }
18316         };
18317         
18318         cog.menu.items.push({
18319             xtype :'MenuItem',
18320             xns: Roo.bootstrap,
18321             html : Clean styles,
18322             tagname : f,
18323             listeners : {
18324                 click : function()
18325                 {
18326                     editorcore.insertTag(this.tagname);
18327                     editor.focus();
18328                 }
18329             }
18330             
18331         });
18332        */
18333         
18334          
18335        this.xtype = 'NavSimplebar';
18336         
18337         for(var i=0;i< children.length;i++) {
18338             
18339             this.buttons.add(this.addxtypeChild(children[i]));
18340             
18341         }
18342         
18343         editor.on('editorevent', this.updateToolbar, this);
18344     },
18345     onBtnClick : function(id)
18346     {
18347        this.editorcore.relayCmd(id);
18348        this.editorcore.focus();
18349     },
18350     
18351     /**
18352      * Protected method that will not generally be called directly. It triggers
18353      * a toolbar update by reading the markup state of the current selection in the editor.
18354      */
18355     updateToolbar: function(){
18356
18357         if(!this.editorcore.activated){
18358             this.editor.onFirstFocus(); // is this neeed?
18359             return;
18360         }
18361
18362         var btns = this.buttons; 
18363         var doc = this.editorcore.doc;
18364         btns.get('bold').setActive(doc.queryCommandState('bold'));
18365         btns.get('italic').setActive(doc.queryCommandState('italic'));
18366         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18367         
18368         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18369         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18370         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18371         
18372         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18373         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18374          /*
18375         
18376         var ans = this.editorcore.getAllAncestors();
18377         if (this.formatCombo) {
18378             
18379             
18380             var store = this.formatCombo.store;
18381             this.formatCombo.setValue("");
18382             for (var i =0; i < ans.length;i++) {
18383                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18384                     // select it..
18385                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18386                     break;
18387                 }
18388             }
18389         }
18390         
18391         
18392         
18393         // hides menus... - so this cant be on a menu...
18394         Roo.bootstrap.MenuMgr.hideAll();
18395         */
18396         Roo.bootstrap.MenuMgr.hideAll();
18397         //this.editorsyncValue();
18398     },
18399     onFirstFocus: function() {
18400         this.buttons.each(function(item){
18401            item.enable();
18402         });
18403     },
18404     toggleSourceEdit : function(sourceEditMode){
18405         
18406           
18407         if(sourceEditMode){
18408             Roo.log("disabling buttons");
18409            this.buttons.each( function(item){
18410                 if(item.cmd != 'pencil'){
18411                     item.disable();
18412                 }
18413             });
18414           
18415         }else{
18416             Roo.log("enabling buttons");
18417             if(this.editorcore.initialized){
18418                 this.buttons.each( function(item){
18419                     item.enable();
18420                 });
18421             }
18422             
18423         }
18424         Roo.log("calling toggole on editor");
18425         // tell the editor that it's been pressed..
18426         this.editor.toggleSourceEdit(sourceEditMode);
18427        
18428     }
18429 });
18430
18431
18432
18433
18434
18435 /**
18436  * @class Roo.bootstrap.Table.AbstractSelectionModel
18437  * @extends Roo.util.Observable
18438  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18439  * implemented by descendant classes.  This class should not be directly instantiated.
18440  * @constructor
18441  */
18442 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18443     this.locked = false;
18444     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18445 };
18446
18447
18448 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18449     /** @ignore Called by the grid automatically. Do not call directly. */
18450     init : function(grid){
18451         this.grid = grid;
18452         this.initEvents();
18453     },
18454
18455     /**
18456      * Locks the selections.
18457      */
18458     lock : function(){
18459         this.locked = true;
18460     },
18461
18462     /**
18463      * Unlocks the selections.
18464      */
18465     unlock : function(){
18466         this.locked = false;
18467     },
18468
18469     /**
18470      * Returns true if the selections are locked.
18471      * @return {Boolean}
18472      */
18473     isLocked : function(){
18474         return this.locked;
18475     }
18476 });
18477 /**
18478  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18479  * @class Roo.bootstrap.Table.RowSelectionModel
18480  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18481  * It supports multiple selections and keyboard selection/navigation. 
18482  * @constructor
18483  * @param {Object} config
18484  */
18485
18486 Roo.bootstrap.Table.RowSelectionModel = function(config){
18487     Roo.apply(this, config);
18488     this.selections = new Roo.util.MixedCollection(false, function(o){
18489         return o.id;
18490     });
18491
18492     this.last = false;
18493     this.lastActive = false;
18494
18495     this.addEvents({
18496         /**
18497              * @event selectionchange
18498              * Fires when the selection changes
18499              * @param {SelectionModel} this
18500              */
18501             "selectionchange" : true,
18502         /**
18503              * @event afterselectionchange
18504              * Fires after the selection changes (eg. by key press or clicking)
18505              * @param {SelectionModel} this
18506              */
18507             "afterselectionchange" : true,
18508         /**
18509              * @event beforerowselect
18510              * Fires when a row is selected being selected, return false to cancel.
18511              * @param {SelectionModel} this
18512              * @param {Number} rowIndex The selected index
18513              * @param {Boolean} keepExisting False if other selections will be cleared
18514              */
18515             "beforerowselect" : true,
18516         /**
18517              * @event rowselect
18518              * Fires when a row is selected.
18519              * @param {SelectionModel} this
18520              * @param {Number} rowIndex The selected index
18521              * @param {Roo.data.Record} r The record
18522              */
18523             "rowselect" : true,
18524         /**
18525              * @event rowdeselect
18526              * Fires when a row is deselected.
18527              * @param {SelectionModel} this
18528              * @param {Number} rowIndex The selected index
18529              */
18530         "rowdeselect" : true
18531     });
18532     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18533     this.locked = false;
18534 };
18535
18536 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18537     /**
18538      * @cfg {Boolean} singleSelect
18539      * True to allow selection of only one row at a time (defaults to false)
18540      */
18541     singleSelect : false,
18542
18543     // private
18544     initEvents : function(){
18545
18546         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18547             this.grid.on("mousedown", this.handleMouseDown, this);
18548         }else{ // allow click to work like normal
18549             this.grid.on("rowclick", this.handleDragableRowClick, this);
18550         }
18551
18552         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18553             "up" : function(e){
18554                 if(!e.shiftKey){
18555                     this.selectPrevious(e.shiftKey);
18556                 }else if(this.last !== false && this.lastActive !== false){
18557                     var last = this.last;
18558                     this.selectRange(this.last,  this.lastActive-1);
18559                     this.grid.getView().focusRow(this.lastActive);
18560                     if(last !== false){
18561                         this.last = last;
18562                     }
18563                 }else{
18564                     this.selectFirstRow();
18565                 }
18566                 this.fireEvent("afterselectionchange", this);
18567             },
18568             "down" : function(e){
18569                 if(!e.shiftKey){
18570                     this.selectNext(e.shiftKey);
18571                 }else if(this.last !== false && this.lastActive !== false){
18572                     var last = this.last;
18573                     this.selectRange(this.last,  this.lastActive+1);
18574                     this.grid.getView().focusRow(this.lastActive);
18575                     if(last !== false){
18576                         this.last = last;
18577                     }
18578                 }else{
18579                     this.selectFirstRow();
18580                 }
18581                 this.fireEvent("afterselectionchange", this);
18582             },
18583             scope: this
18584         });
18585
18586         var view = this.grid.view;
18587         view.on("refresh", this.onRefresh, this);
18588         view.on("rowupdated", this.onRowUpdated, this);
18589         view.on("rowremoved", this.onRemove, this);
18590     },
18591
18592     // private
18593     onRefresh : function(){
18594         var ds = this.grid.dataSource, i, v = this.grid.view;
18595         var s = this.selections;
18596         s.each(function(r){
18597             if((i = ds.indexOfId(r.id)) != -1){
18598                 v.onRowSelect(i);
18599             }else{
18600                 s.remove(r);
18601             }
18602         });
18603     },
18604
18605     // private
18606     onRemove : function(v, index, r){
18607         this.selections.remove(r);
18608     },
18609
18610     // private
18611     onRowUpdated : function(v, index, r){
18612         if(this.isSelected(r)){
18613             v.onRowSelect(index);
18614         }
18615     },
18616
18617     /**
18618      * Select records.
18619      * @param {Array} records The records to select
18620      * @param {Boolean} keepExisting (optional) True to keep existing selections
18621      */
18622     selectRecords : function(records, keepExisting){
18623         if(!keepExisting){
18624             this.clearSelections();
18625         }
18626         var ds = this.grid.dataSource;
18627         for(var i = 0, len = records.length; i < len; i++){
18628             this.selectRow(ds.indexOf(records[i]), true);
18629         }
18630     },
18631
18632     /**
18633      * Gets the number of selected rows.
18634      * @return {Number}
18635      */
18636     getCount : function(){
18637         return this.selections.length;
18638     },
18639
18640     /**
18641      * Selects the first row in the grid.
18642      */
18643     selectFirstRow : function(){
18644         this.selectRow(0);
18645     },
18646
18647     /**
18648      * Select the last row.
18649      * @param {Boolean} keepExisting (optional) True to keep existing selections
18650      */
18651     selectLastRow : function(keepExisting){
18652         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18653     },
18654
18655     /**
18656      * Selects the row immediately following the last selected row.
18657      * @param {Boolean} keepExisting (optional) True to keep existing selections
18658      */
18659     selectNext : function(keepExisting){
18660         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18661             this.selectRow(this.last+1, keepExisting);
18662             this.grid.getView().focusRow(this.last);
18663         }
18664     },
18665
18666     /**
18667      * Selects the row that precedes the last selected row.
18668      * @param {Boolean} keepExisting (optional) True to keep existing selections
18669      */
18670     selectPrevious : function(keepExisting){
18671         if(this.last){
18672             this.selectRow(this.last-1, keepExisting);
18673             this.grid.getView().focusRow(this.last);
18674         }
18675     },
18676
18677     /**
18678      * Returns the selected records
18679      * @return {Array} Array of selected records
18680      */
18681     getSelections : function(){
18682         return [].concat(this.selections.items);
18683     },
18684
18685     /**
18686      * Returns the first selected record.
18687      * @return {Record}
18688      */
18689     getSelected : function(){
18690         return this.selections.itemAt(0);
18691     },
18692
18693
18694     /**
18695      * Clears all selections.
18696      */
18697     clearSelections : function(fast){
18698         if(this.locked) return;
18699         if(fast !== true){
18700             var ds = this.grid.dataSource;
18701             var s = this.selections;
18702             s.each(function(r){
18703                 this.deselectRow(ds.indexOfId(r.id));
18704             }, this);
18705             s.clear();
18706         }else{
18707             this.selections.clear();
18708         }
18709         this.last = false;
18710     },
18711
18712
18713     /**
18714      * Selects all rows.
18715      */
18716     selectAll : function(){
18717         if(this.locked) return;
18718         this.selections.clear();
18719         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18720             this.selectRow(i, true);
18721         }
18722     },
18723
18724     /**
18725      * Returns True if there is a selection.
18726      * @return {Boolean}
18727      */
18728     hasSelection : function(){
18729         return this.selections.length > 0;
18730     },
18731
18732     /**
18733      * Returns True if the specified row is selected.
18734      * @param {Number/Record} record The record or index of the record to check
18735      * @return {Boolean}
18736      */
18737     isSelected : function(index){
18738         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18739         return (r && this.selections.key(r.id) ? true : false);
18740     },
18741
18742     /**
18743      * Returns True if the specified record id is selected.
18744      * @param {String} id The id of record to check
18745      * @return {Boolean}
18746      */
18747     isIdSelected : function(id){
18748         return (this.selections.key(id) ? true : false);
18749     },
18750
18751     // private
18752     handleMouseDown : function(e, t){
18753         var view = this.grid.getView(), rowIndex;
18754         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18755             return;
18756         };
18757         if(e.shiftKey && this.last !== false){
18758             var last = this.last;
18759             this.selectRange(last, rowIndex, e.ctrlKey);
18760             this.last = last; // reset the last
18761             view.focusRow(rowIndex);
18762         }else{
18763             var isSelected = this.isSelected(rowIndex);
18764             if(e.button !== 0 && isSelected){
18765                 view.focusRow(rowIndex);
18766             }else if(e.ctrlKey && isSelected){
18767                 this.deselectRow(rowIndex);
18768             }else if(!isSelected){
18769                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18770                 view.focusRow(rowIndex);
18771             }
18772         }
18773         this.fireEvent("afterselectionchange", this);
18774     },
18775     // private
18776     handleDragableRowClick :  function(grid, rowIndex, e) 
18777     {
18778         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18779             this.selectRow(rowIndex, false);
18780             grid.view.focusRow(rowIndex);
18781              this.fireEvent("afterselectionchange", this);
18782         }
18783     },
18784     
18785     /**
18786      * Selects multiple rows.
18787      * @param {Array} rows Array of the indexes of the row to select
18788      * @param {Boolean} keepExisting (optional) True to keep existing selections
18789      */
18790     selectRows : function(rows, keepExisting){
18791         if(!keepExisting){
18792             this.clearSelections();
18793         }
18794         for(var i = 0, len = rows.length; i < len; i++){
18795             this.selectRow(rows[i], true);
18796         }
18797     },
18798
18799     /**
18800      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18801      * @param {Number} startRow The index of the first row in the range
18802      * @param {Number} endRow The index of the last row in the range
18803      * @param {Boolean} keepExisting (optional) True to retain existing selections
18804      */
18805     selectRange : function(startRow, endRow, keepExisting){
18806         if(this.locked) return;
18807         if(!keepExisting){
18808             this.clearSelections();
18809         }
18810         if(startRow <= endRow){
18811             for(var i = startRow; i <= endRow; i++){
18812                 this.selectRow(i, true);
18813             }
18814         }else{
18815             for(var i = startRow; i >= endRow; i--){
18816                 this.selectRow(i, true);
18817             }
18818         }
18819     },
18820
18821     /**
18822      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18823      * @param {Number} startRow The index of the first row in the range
18824      * @param {Number} endRow The index of the last row in the range
18825      */
18826     deselectRange : function(startRow, endRow, preventViewNotify){
18827         if(this.locked) return;
18828         for(var i = startRow; i <= endRow; i++){
18829             this.deselectRow(i, preventViewNotify);
18830         }
18831     },
18832
18833     /**
18834      * Selects a row.
18835      * @param {Number} row The index of the row to select
18836      * @param {Boolean} keepExisting (optional) True to keep existing selections
18837      */
18838     selectRow : function(index, keepExisting, preventViewNotify){
18839         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18840         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18841             if(!keepExisting || this.singleSelect){
18842                 this.clearSelections();
18843             }
18844             var r = this.grid.dataSource.getAt(index);
18845             this.selections.add(r);
18846             this.last = this.lastActive = index;
18847             if(!preventViewNotify){
18848                 this.grid.getView().onRowSelect(index);
18849             }
18850             this.fireEvent("rowselect", this, index, r);
18851             this.fireEvent("selectionchange", this);
18852         }
18853     },
18854
18855     /**
18856      * Deselects a row.
18857      * @param {Number} row The index of the row to deselect
18858      */
18859     deselectRow : function(index, preventViewNotify){
18860         if(this.locked) return;
18861         if(this.last == index){
18862             this.last = false;
18863         }
18864         if(this.lastActive == index){
18865             this.lastActive = false;
18866         }
18867         var r = this.grid.dataSource.getAt(index);
18868         this.selections.remove(r);
18869         if(!preventViewNotify){
18870             this.grid.getView().onRowDeselect(index);
18871         }
18872         this.fireEvent("rowdeselect", this, index);
18873         this.fireEvent("selectionchange", this);
18874     },
18875
18876     // private
18877     restoreLast : function(){
18878         if(this._last){
18879             this.last = this._last;
18880         }
18881     },
18882
18883     // private
18884     acceptsNav : function(row, col, cm){
18885         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18886     },
18887
18888     // private
18889     onEditorKey : function(field, e){
18890         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18891         if(k == e.TAB){
18892             e.stopEvent();
18893             ed.completeEdit();
18894             if(e.shiftKey){
18895                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18896             }else{
18897                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18898             }
18899         }else if(k == e.ENTER && !e.ctrlKey){
18900             e.stopEvent();
18901             ed.completeEdit();
18902             if(e.shiftKey){
18903                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18904             }else{
18905                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18906             }
18907         }else if(k == e.ESC){
18908             ed.cancelEdit();
18909         }
18910         if(newCell){
18911             g.startEditing(newCell[0], newCell[1]);
18912         }
18913     }
18914 });/*
18915  * Based on:
18916  * Ext JS Library 1.1.1
18917  * Copyright(c) 2006-2007, Ext JS, LLC.
18918  *
18919  * Originally Released Under LGPL - original licence link has changed is not relivant.
18920  *
18921  * Fork - LGPL
18922  * <script type="text/javascript">
18923  */
18924  
18925 /**
18926  * @class Roo.bootstrap.PagingToolbar
18927  * @extends Roo.Row
18928  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18929  * @constructor
18930  * Create a new PagingToolbar
18931  * @param {Object} config The config object
18932  */
18933 Roo.bootstrap.PagingToolbar = function(config)
18934 {
18935     // old args format still supported... - xtype is prefered..
18936         // created from xtype...
18937     var ds = config.dataSource;
18938     this.toolbarItems = [];
18939     if (config.items) {
18940         this.toolbarItems = config.items;
18941 //        config.items = [];
18942     }
18943     
18944     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18945     this.ds = ds;
18946     this.cursor = 0;
18947     if (ds) { 
18948         this.bind(ds);
18949     }
18950     
18951     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18952     
18953 };
18954
18955 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18956     /**
18957      * @cfg {Roo.data.Store} dataSource
18958      * The underlying data store providing the paged data
18959      */
18960     /**
18961      * @cfg {String/HTMLElement/Element} container
18962      * container The id or element that will contain the toolbar
18963      */
18964     /**
18965      * @cfg {Boolean} displayInfo
18966      * True to display the displayMsg (defaults to false)
18967      */
18968     /**
18969      * @cfg {Number} pageSize
18970      * The number of records to display per page (defaults to 20)
18971      */
18972     pageSize: 20,
18973     /**
18974      * @cfg {String} displayMsg
18975      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18976      */
18977     displayMsg : 'Displaying {0} - {1} of {2}',
18978     /**
18979      * @cfg {String} emptyMsg
18980      * The message to display when no records are found (defaults to "No data to display")
18981      */
18982     emptyMsg : 'No data to display',
18983     /**
18984      * Customizable piece of the default paging text (defaults to "Page")
18985      * @type String
18986      */
18987     beforePageText : "Page",
18988     /**
18989      * Customizable piece of the default paging text (defaults to "of %0")
18990      * @type String
18991      */
18992     afterPageText : "of {0}",
18993     /**
18994      * Customizable piece of the default paging text (defaults to "First Page")
18995      * @type String
18996      */
18997     firstText : "First Page",
18998     /**
18999      * Customizable piece of the default paging text (defaults to "Previous Page")
19000      * @type String
19001      */
19002     prevText : "Previous Page",
19003     /**
19004      * Customizable piece of the default paging text (defaults to "Next Page")
19005      * @type String
19006      */
19007     nextText : "Next Page",
19008     /**
19009      * Customizable piece of the default paging text (defaults to "Last Page")
19010      * @type String
19011      */
19012     lastText : "Last Page",
19013     /**
19014      * Customizable piece of the default paging text (defaults to "Refresh")
19015      * @type String
19016      */
19017     refreshText : "Refresh",
19018
19019     buttons : false,
19020     // private
19021     onRender : function(ct, position) 
19022     {
19023         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19024         this.navgroup.parentId = this.id;
19025         this.navgroup.onRender(this.el, null);
19026         // add the buttons to the navgroup
19027         
19028         if(this.displayInfo){
19029             Roo.log(this.el.select('ul.navbar-nav',true).first());
19030             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19031             this.displayEl = this.el.select('.x-paging-info', true).first();
19032 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19033 //            this.displayEl = navel.el.select('span',true).first();
19034         }
19035         
19036         var _this = this;
19037         
19038         if(this.buttons){
19039             Roo.each(_this.buttons, function(e){
19040                Roo.factory(e).onRender(_this.el, null);
19041             });
19042         }
19043             
19044         Roo.each(_this.toolbarItems, function(e) {
19045             _this.navgroup.addItem(e);
19046         });
19047         
19048         this.first = this.navgroup.addItem({
19049             tooltip: this.firstText,
19050             cls: "prev",
19051             icon : 'fa fa-backward',
19052             disabled: true,
19053             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19054         });
19055         
19056         this.prev =  this.navgroup.addItem({
19057             tooltip: this.prevText,
19058             cls: "prev",
19059             icon : 'fa fa-step-backward',
19060             disabled: true,
19061             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19062         });
19063     //this.addSeparator();
19064         
19065         
19066         var field = this.navgroup.addItem( {
19067             tagtype : 'span',
19068             cls : 'x-paging-position',
19069             
19070             html : this.beforePageText  +
19071                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19072                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19073          } ); //?? escaped?
19074         
19075         this.field = field.el.select('input', true).first();
19076         this.field.on("keydown", this.onPagingKeydown, this);
19077         this.field.on("focus", function(){this.dom.select();});
19078     
19079     
19080         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19081         //this.field.setHeight(18);
19082         //this.addSeparator();
19083         this.next = this.navgroup.addItem({
19084             tooltip: this.nextText,
19085             cls: "next",
19086             html : ' <i class="fa fa-step-forward">',
19087             disabled: true,
19088             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19089         });
19090         this.last = this.navgroup.addItem({
19091             tooltip: this.lastText,
19092             icon : 'fa fa-forward',
19093             cls: "next",
19094             disabled: true,
19095             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19096         });
19097     //this.addSeparator();
19098         this.loading = this.navgroup.addItem({
19099             tooltip: this.refreshText,
19100             icon: 'fa fa-refresh',
19101             
19102             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19103         });
19104
19105     },
19106
19107     // private
19108     updateInfo : function(){
19109         if(this.displayEl){
19110             var count = this.ds.getCount();
19111             var msg = count == 0 ?
19112                 this.emptyMsg :
19113                 String.format(
19114                     this.displayMsg,
19115                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19116                 );
19117             this.displayEl.update(msg);
19118         }
19119     },
19120
19121     // private
19122     onLoad : function(ds, r, o){
19123        this.cursor = o.params ? o.params.start : 0;
19124        var d = this.getPageData(),
19125             ap = d.activePage,
19126             ps = d.pages;
19127         
19128        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19129        this.field.dom.value = ap;
19130        this.first.setDisabled(ap == 1);
19131        this.prev.setDisabled(ap == 1);
19132        this.next.setDisabled(ap == ps);
19133        this.last.setDisabled(ap == ps);
19134        this.loading.enable();
19135        this.updateInfo();
19136     },
19137
19138     // private
19139     getPageData : function(){
19140         var total = this.ds.getTotalCount();
19141         return {
19142             total : total,
19143             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19144             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19145         };
19146     },
19147
19148     // private
19149     onLoadError : function(){
19150         this.loading.enable();
19151     },
19152
19153     // private
19154     onPagingKeydown : function(e){
19155         var k = e.getKey();
19156         var d = this.getPageData();
19157         if(k == e.RETURN){
19158             var v = this.field.dom.value, pageNum;
19159             if(!v || isNaN(pageNum = parseInt(v, 10))){
19160                 this.field.dom.value = d.activePage;
19161                 return;
19162             }
19163             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19164             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19165             e.stopEvent();
19166         }
19167         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))
19168         {
19169           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19170           this.field.dom.value = pageNum;
19171           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19172           e.stopEvent();
19173         }
19174         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19175         {
19176           var v = this.field.dom.value, pageNum; 
19177           var increment = (e.shiftKey) ? 10 : 1;
19178           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19179             increment *= -1;
19180           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19181             this.field.dom.value = d.activePage;
19182             return;
19183           }
19184           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19185           {
19186             this.field.dom.value = parseInt(v, 10) + increment;
19187             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19188             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19189           }
19190           e.stopEvent();
19191         }
19192     },
19193
19194     // private
19195     beforeLoad : function(){
19196         if(this.loading){
19197             this.loading.disable();
19198         }
19199     },
19200
19201     // private
19202     onClick : function(which){
19203         var ds = this.ds;
19204         if (!ds) {
19205             return;
19206         }
19207         switch(which){
19208             case "first":
19209                 ds.load({params:{start: 0, limit: this.pageSize}});
19210             break;
19211             case "prev":
19212                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19213             break;
19214             case "next":
19215                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19216             break;
19217             case "last":
19218                 var total = ds.getTotalCount();
19219                 var extra = total % this.pageSize;
19220                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19221                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19222             break;
19223             case "refresh":
19224                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19225             break;
19226         }
19227     },
19228
19229     /**
19230      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19231      * @param {Roo.data.Store} store The data store to unbind
19232      */
19233     unbind : function(ds){
19234         ds.un("beforeload", this.beforeLoad, this);
19235         ds.un("load", this.onLoad, this);
19236         ds.un("loadexception", this.onLoadError, this);
19237         ds.un("remove", this.updateInfo, this);
19238         ds.un("add", this.updateInfo, this);
19239         this.ds = undefined;
19240     },
19241
19242     /**
19243      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19244      * @param {Roo.data.Store} store The data store to bind
19245      */
19246     bind : function(ds){
19247         ds.on("beforeload", this.beforeLoad, this);
19248         ds.on("load", this.onLoad, this);
19249         ds.on("loadexception", this.onLoadError, this);
19250         ds.on("remove", this.updateInfo, this);
19251         ds.on("add", this.updateInfo, this);
19252         this.ds = ds;
19253     }
19254 });/*
19255  * - LGPL
19256  *
19257  * element
19258  * 
19259  */
19260
19261 /**
19262  * @class Roo.bootstrap.MessageBar
19263  * @extends Roo.bootstrap.Component
19264  * Bootstrap MessageBar class
19265  * @cfg {String} html contents of the MessageBar
19266  * @cfg {String} weight (info | success | warning | danger) default info
19267  * @cfg {String} beforeClass insert the bar before the given class
19268  * @cfg {Boolean} closable (true | false) default false
19269  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19270  * 
19271  * @constructor
19272  * Create a new Element
19273  * @param {Object} config The config object
19274  */
19275
19276 Roo.bootstrap.MessageBar = function(config){
19277     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19278 };
19279
19280 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19281     
19282     html: '',
19283     weight: 'info',
19284     closable: false,
19285     fixed: false,
19286     beforeClass: 'bootstrap-sticky-wrap',
19287     
19288     getAutoCreate : function(){
19289         
19290         var cfg = {
19291             tag: 'div',
19292             cls: 'alert alert-dismissable alert-' + this.weight,
19293             cn: [
19294                 {
19295                     tag: 'span',
19296                     cls: 'message',
19297                     html: this.html || ''
19298                 }
19299             ]
19300         }
19301         
19302         if(this.fixed){
19303             cfg.cls += ' alert-messages-fixed';
19304         }
19305         
19306         if(this.closable){
19307             cfg.cn.push({
19308                 tag: 'button',
19309                 cls: 'close',
19310                 html: 'x'
19311             });
19312         }
19313         
19314         return cfg;
19315     },
19316     
19317     onRender : function(ct, position)
19318     {
19319         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19320         
19321         if(!this.el){
19322             var cfg = Roo.apply({},  this.getAutoCreate());
19323             cfg.id = Roo.id();
19324             
19325             if (this.cls) {
19326                 cfg.cls += ' ' + this.cls;
19327             }
19328             if (this.style) {
19329                 cfg.style = this.style;
19330             }
19331             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19332             
19333             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19334         }
19335         
19336         this.el.select('>button.close').on('click', this.hide, this);
19337         
19338     },
19339     
19340     show : function()
19341     {
19342         if (!this.rendered) {
19343             this.render();
19344         }
19345         
19346         this.el.show();
19347         
19348         this.fireEvent('show', this);
19349         
19350     },
19351     
19352     hide : function()
19353     {
19354         if (!this.rendered) {
19355             this.render();
19356         }
19357         
19358         this.el.hide();
19359         
19360         this.fireEvent('hide', this);
19361     },
19362     
19363     update : function()
19364     {
19365 //        var e = this.el.dom.firstChild;
19366 //        
19367 //        if(this.closable){
19368 //            e = e.nextSibling;
19369 //        }
19370 //        
19371 //        e.data = this.html || '';
19372
19373         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19374     }
19375    
19376 });
19377
19378  
19379
19380      /*
19381  * - LGPL
19382  *
19383  * Graph
19384  * 
19385  */
19386
19387
19388 /**
19389  * @class Roo.bootstrap.Graph
19390  * @extends Roo.bootstrap.Component
19391  * Bootstrap Graph class
19392 > Prameters
19393  -sm {number} sm 4
19394  -md {number} md 5
19395  @cfg {String} graphtype  bar | vbar | pie
19396  @cfg {number} g_x coodinator | centre x (pie)
19397  @cfg {number} g_y coodinator | centre y (pie)
19398  @cfg {number} g_r radius (pie)
19399  @cfg {number} g_height height of the chart (respected by all elements in the set)
19400  @cfg {number} g_width width of the chart (respected by all elements in the set)
19401  @cfg {Object} title The title of the chart
19402     
19403  -{Array}  values
19404  -opts (object) options for the chart 
19405      o {
19406      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19407      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19408      o vgutter (number)
19409      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.
19410      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19411      o to
19412      o stretch (boolean)
19413      o }
19414  -opts (object) options for the pie
19415      o{
19416      o cut
19417      o startAngle (number)
19418      o endAngle (number)
19419      } 
19420  *
19421  * @constructor
19422  * Create a new Input
19423  * @param {Object} config The config object
19424  */
19425
19426 Roo.bootstrap.Graph = function(config){
19427     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19428     
19429     this.addEvents({
19430         // img events
19431         /**
19432          * @event click
19433          * The img click event for the img.
19434          * @param {Roo.EventObject} e
19435          */
19436         "click" : true
19437     });
19438 };
19439
19440 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19441     
19442     sm: 4,
19443     md: 5,
19444     graphtype: 'bar',
19445     g_height: 250,
19446     g_width: 400,
19447     g_x: 50,
19448     g_y: 50,
19449     g_r: 30,
19450     opts:{
19451         //g_colors: this.colors,
19452         g_type: 'soft',
19453         g_gutter: '20%'
19454
19455     },
19456     title : false,
19457
19458     getAutoCreate : function(){
19459         
19460         var cfg = {
19461             tag: 'div',
19462             html : null
19463         }
19464         
19465         
19466         return  cfg;
19467     },
19468
19469     onRender : function(ct,position){
19470         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19471         this.raphael = Raphael(this.el.dom);
19472         
19473                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19474                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19475                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19476                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19477                 /*
19478                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19479                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19480                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19481                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19482                 
19483                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19484                 r.barchart(330, 10, 300, 220, data1);
19485                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19486                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19487                 */
19488                 
19489                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19490                 // r.barchart(30, 30, 560, 250,  xdata, {
19491                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19492                 //     axis : "0 0 1 1",
19493                 //     axisxlabels :  xdata
19494                 //     //yvalues : cols,
19495                    
19496                 // });
19497 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19498 //        
19499 //        this.load(null,xdata,{
19500 //                axis : "0 0 1 1",
19501 //                axisxlabels :  xdata
19502 //                });
19503
19504     },
19505
19506     load : function(graphtype,xdata,opts){
19507         this.raphael.clear();
19508         if(!graphtype) {
19509             graphtype = this.graphtype;
19510         }
19511         if(!opts){
19512             opts = this.opts;
19513         }
19514         var r = this.raphael,
19515             fin = function () {
19516                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19517             },
19518             fout = function () {
19519                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19520             },
19521             pfin = function() {
19522                 this.sector.stop();
19523                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19524
19525                 if (this.label) {
19526                     this.label[0].stop();
19527                     this.label[0].attr({ r: 7.5 });
19528                     this.label[1].attr({ "font-weight": 800 });
19529                 }
19530             },
19531             pfout = function() {
19532                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19533
19534                 if (this.label) {
19535                     this.label[0].animate({ r: 5 }, 500, "bounce");
19536                     this.label[1].attr({ "font-weight": 400 });
19537                 }
19538             };
19539
19540         switch(graphtype){
19541             case 'bar':
19542                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19543                 break;
19544             case 'hbar':
19545                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19546                 break;
19547             case 'pie':
19548 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19549 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19550 //            
19551                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19552                 
19553                 break;
19554
19555         }
19556         
19557         if(this.title){
19558             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19559         }
19560         
19561     },
19562     
19563     setTitle: function(o)
19564     {
19565         this.title = o;
19566     },
19567     
19568     initEvents: function() {
19569         
19570         if(!this.href){
19571             this.el.on('click', this.onClick, this);
19572         }
19573     },
19574     
19575     onClick : function(e)
19576     {
19577         Roo.log('img onclick');
19578         this.fireEvent('click', this, e);
19579     }
19580    
19581 });
19582
19583  
19584 /*
19585  * - LGPL
19586  *
19587  * numberBox
19588  * 
19589  */
19590 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19591
19592 /**
19593  * @class Roo.bootstrap.dash.NumberBox
19594  * @extends Roo.bootstrap.Component
19595  * Bootstrap NumberBox class
19596  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19597  * @cfg {String} headline Box headline
19598  * @cfg {String} content Box content
19599  * @cfg {String} icon Box icon
19600  * @cfg {String} footer Footer text
19601  * @cfg {String} fhref Footer href
19602  * 
19603  * @constructor
19604  * Create a new NumberBox
19605  * @param {Object} config The config object
19606  */
19607
19608
19609 Roo.bootstrap.dash.NumberBox = function(config){
19610     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19611     
19612 };
19613
19614 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19615     
19616     bgcolor : 'aqua',
19617     headline : '',
19618     content : '',
19619     icon : '',
19620     footer : '',
19621     fhref : '',
19622     ficon : '',
19623     
19624     getAutoCreate : function(){
19625         
19626         var cfg = {
19627             tag : 'div',
19628             cls : 'small-box bg-' + this.bgcolor,
19629             cn : [
19630                 {
19631                     tag : 'div',
19632                     cls : 'inner',
19633                     cn :[
19634                         {
19635                             tag : 'h3',
19636                             cls : 'roo-headline',
19637                             html : this.headline
19638                         },
19639                         {
19640                             tag : 'p',
19641                             cls : 'roo-content',
19642                             html : this.content
19643                         }
19644                     ]
19645                 }
19646             ]
19647         }
19648         
19649         if(this.icon){
19650             cfg.cn.push({
19651                 tag : 'div',
19652                 cls : 'icon',
19653                 cn :[
19654                     {
19655                         tag : 'i',
19656                         cls : 'ion ' + this.icon
19657                     }
19658                 ]
19659             });
19660         }
19661         
19662         if(this.footer){
19663             var footer = {
19664                 tag : 'a',
19665                 cls : 'small-box-footer',
19666                 href : this.fhref || '#',
19667                 html : this.footer
19668             };
19669             
19670             cfg.cn.push(footer);
19671             
19672         }
19673         
19674         return  cfg;
19675     },
19676
19677     onRender : function(ct,position){
19678         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19679
19680
19681        
19682                 
19683     },
19684
19685     setHeadline: function (value)
19686     {
19687         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19688     },
19689     
19690     setFooter: function (value, href)
19691     {
19692         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19693         
19694         if(href){
19695             this.el.select('a.small-box-footer',true).first().attr('href', href);
19696         }
19697         
19698     },
19699
19700     setContent: function (value)
19701     {
19702         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19703     },
19704
19705     initEvents: function() 
19706     {   
19707         
19708     }
19709     
19710 });
19711
19712  
19713 /*
19714  * - LGPL
19715  *
19716  * TabBox
19717  * 
19718  */
19719 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19720
19721 /**
19722  * @class Roo.bootstrap.dash.TabBox
19723  * @extends Roo.bootstrap.Component
19724  * Bootstrap TabBox class
19725  * @cfg {String} title Title of the TabBox
19726  * @cfg {String} icon Icon of the TabBox
19727  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19728  * 
19729  * @constructor
19730  * Create a new TabBox
19731  * @param {Object} config The config object
19732  */
19733
19734
19735 Roo.bootstrap.dash.TabBox = function(config){
19736     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19737     this.addEvents({
19738         // raw events
19739         /**
19740          * @event addpane
19741          * When a pane is added
19742          * @param {Roo.bootstrap.dash.TabPane} pane
19743          */
19744         "addpane" : true
19745          
19746     });
19747 };
19748
19749 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19750
19751     title : '',
19752     icon : false,
19753     showtabs : true,
19754     
19755     getChildContainer : function()
19756     {
19757         return this.el.select('.tab-content', true).first();
19758     },
19759     
19760     getAutoCreate : function(){
19761         
19762         var header = {
19763             tag: 'li',
19764             cls: 'pull-left header',
19765             html: this.title,
19766             cn : []
19767         };
19768         
19769         if(this.icon){
19770             header.cn.push({
19771                 tag: 'i',
19772                 cls: 'fa ' + this.icon
19773             });
19774         }
19775         
19776         
19777         var cfg = {
19778             tag: 'div',
19779             cls: 'nav-tabs-custom',
19780             cn: [
19781                 {
19782                     tag: 'ul',
19783                     cls: 'nav nav-tabs pull-right',
19784                     cn: [
19785                         header
19786                     ]
19787                 },
19788                 {
19789                     tag: 'div',
19790                     cls: 'tab-content no-padding',
19791                     cn: []
19792                 }
19793             ]
19794         }
19795
19796         return  cfg;
19797     },
19798     initEvents : function()
19799     {
19800         //Roo.log('add add pane handler');
19801         this.on('addpane', this.onAddPane, this);
19802     },
19803      /**
19804      * Updates the box title
19805      * @param {String} html to set the title to.
19806      */
19807     setTitle : function(value)
19808     {
19809         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19810     },
19811     onAddPane : function(pane)
19812     {
19813         //Roo.log('addpane');
19814         //Roo.log(pane);
19815         // tabs are rendere left to right..
19816         if(!this.showtabs){
19817             return;
19818         }
19819         
19820         var ctr = this.el.select('.nav-tabs', true).first();
19821          
19822          
19823         var existing = ctr.select('.nav-tab',true);
19824         var qty = existing.getCount();;
19825         
19826         
19827         var tab = ctr.createChild({
19828             tag : 'li',
19829             cls : 'nav-tab' + (qty ? '' : ' active'),
19830             cn : [
19831                 {
19832                     tag : 'a',
19833                     href:'#',
19834                     html : pane.title
19835                 }
19836             ]
19837         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19838         pane.tab = tab;
19839         
19840         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19841         if (!qty) {
19842             pane.el.addClass('active');
19843         }
19844         
19845                 
19846     },
19847     onTabClick : function(ev,un,ob,pane)
19848     {
19849         //Roo.log('tab - prev default');
19850         ev.preventDefault();
19851         
19852         
19853         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19854         pane.tab.addClass('active');
19855         //Roo.log(pane.title);
19856         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19857         // technically we should have a deactivate event.. but maybe add later.
19858         // and it should not de-activate the selected tab...
19859         
19860         pane.el.addClass('active');
19861         pane.fireEvent('activate');
19862         
19863         
19864     }
19865     
19866     
19867 });
19868
19869  
19870 /*
19871  * - LGPL
19872  *
19873  * Tab pane
19874  * 
19875  */
19876 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19877 /**
19878  * @class Roo.bootstrap.TabPane
19879  * @extends Roo.bootstrap.Component
19880  * Bootstrap TabPane class
19881  * @cfg {Boolean} active (false | true) Default false
19882  * @cfg {String} title title of panel
19883
19884  * 
19885  * @constructor
19886  * Create a new TabPane
19887  * @param {Object} config The config object
19888  */
19889
19890 Roo.bootstrap.dash.TabPane = function(config){
19891     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19892     
19893     this.addEvents({
19894         // raw events
19895         /**
19896          * @event activate
19897          * When a pane is activated
19898          * @param {Roo.bootstrap.dash.TabPane} pane
19899          */
19900         "activate" : true
19901          
19902     });
19903 };
19904
19905 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19906     
19907     active : false,
19908     title : '',
19909     
19910     // the tabBox that this is attached to.
19911     tab : false,
19912      
19913     getAutoCreate : function() 
19914     {
19915         var cfg = {
19916             tag: 'div',
19917             cls: 'tab-pane'
19918         }
19919         
19920         if(this.active){
19921             cfg.cls += ' active';
19922         }
19923         
19924         return cfg;
19925     },
19926     initEvents  : function()
19927     {
19928         //Roo.log('trigger add pane handler');
19929         this.parent().fireEvent('addpane', this)
19930     },
19931     
19932      /**
19933      * Updates the tab title 
19934      * @param {String} html to set the title to.
19935      */
19936     setTitle: function(str)
19937     {
19938         if (!this.tab) {
19939             return;
19940         }
19941         this.title = str;
19942         this.tab.select('a', true).first().dom.innerHTML = str;
19943         
19944     }
19945     
19946     
19947     
19948 });
19949
19950  
19951
19952
19953  /*
19954  * - LGPL
19955  *
19956  * menu
19957  * 
19958  */
19959 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19960
19961 /**
19962  * @class Roo.bootstrap.menu.Menu
19963  * @extends Roo.bootstrap.Component
19964  * Bootstrap Menu class - container for Menu
19965  * @cfg {String} html Text of the menu
19966  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19967  * @cfg {String} icon Font awesome icon
19968  * @cfg {String} pos Menu align to (top | bottom) default bottom
19969  * 
19970  * 
19971  * @constructor
19972  * Create a new Menu
19973  * @param {Object} config The config object
19974  */
19975
19976
19977 Roo.bootstrap.menu.Menu = function(config){
19978     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19979     
19980     this.addEvents({
19981         /**
19982          * @event beforeshow
19983          * Fires before this menu is displayed
19984          * @param {Roo.bootstrap.menu.Menu} this
19985          */
19986         beforeshow : true,
19987         /**
19988          * @event beforehide
19989          * Fires before this menu is hidden
19990          * @param {Roo.bootstrap.menu.Menu} this
19991          */
19992         beforehide : true,
19993         /**
19994          * @event show
19995          * Fires after this menu is displayed
19996          * @param {Roo.bootstrap.menu.Menu} this
19997          */
19998         show : true,
19999         /**
20000          * @event hide
20001          * Fires after this menu is hidden
20002          * @param {Roo.bootstrap.menu.Menu} this
20003          */
20004         hide : true,
20005         /**
20006          * @event click
20007          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20008          * @param {Roo.bootstrap.menu.Menu} this
20009          * @param {Roo.EventObject} e
20010          */
20011         click : true
20012     });
20013     
20014 };
20015
20016 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20017     
20018     submenu : false,
20019     html : '',
20020     weight : 'default',
20021     icon : false,
20022     pos : 'bottom',
20023     
20024     
20025     getChildContainer : function() {
20026         if(this.isSubMenu){
20027             return this.el;
20028         }
20029         
20030         return this.el.select('ul.dropdown-menu', true).first();  
20031     },
20032     
20033     getAutoCreate : function()
20034     {
20035         var text = [
20036             {
20037                 tag : 'span',
20038                 cls : 'roo-menu-text',
20039                 html : this.html
20040             }
20041         ];
20042         
20043         if(this.icon){
20044             text.unshift({
20045                 tag : 'i',
20046                 cls : 'fa ' + this.icon
20047             })
20048         }
20049         
20050         
20051         var cfg = {
20052             tag : 'div',
20053             cls : 'btn-group',
20054             cn : [
20055                 {
20056                     tag : 'button',
20057                     cls : 'dropdown-button btn btn-' + this.weight,
20058                     cn : text
20059                 },
20060                 {
20061                     tag : 'button',
20062                     cls : 'dropdown-toggle btn btn-' + this.weight,
20063                     cn : [
20064                         {
20065                             tag : 'span',
20066                             cls : 'caret'
20067                         }
20068                     ]
20069                 },
20070                 {
20071                     tag : 'ul',
20072                     cls : 'dropdown-menu'
20073                 }
20074             ]
20075             
20076         };
20077         
20078         if(this.pos == 'top'){
20079             cfg.cls += ' dropup';
20080         }
20081         
20082         if(this.isSubMenu){
20083             cfg = {
20084                 tag : 'ul',
20085                 cls : 'dropdown-menu'
20086             }
20087         }
20088         
20089         return cfg;
20090     },
20091     
20092     onRender : function(ct, position)
20093     {
20094         this.isSubMenu = ct.hasClass('dropdown-submenu');
20095         
20096         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20097     },
20098     
20099     initEvents : function() 
20100     {
20101         if(this.isSubMenu){
20102             return;
20103         }
20104         
20105         this.hidden = true;
20106         
20107         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20108         this.triggerEl.on('click', this.onTriggerPress, this);
20109         
20110         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20111         this.buttonEl.on('click', this.onClick, this);
20112         
20113     },
20114     
20115     list : function()
20116     {
20117         if(this.isSubMenu){
20118             return this.el;
20119         }
20120         
20121         return this.el.select('ul.dropdown-menu', true).first();
20122     },
20123     
20124     onClick : function(e)
20125     {
20126         this.fireEvent("click", this, e);
20127     },
20128     
20129     onTriggerPress  : function(e)
20130     {   
20131         if (this.isVisible()) {
20132             this.hide();
20133         } else {
20134             this.show();
20135         }
20136     },
20137     
20138     isVisible : function(){
20139         return !this.hidden;
20140     },
20141     
20142     show : function()
20143     {
20144         this.fireEvent("beforeshow", this);
20145         
20146         this.hidden = false;
20147         this.el.addClass('open');
20148         
20149         Roo.get(document).on("mouseup", this.onMouseUp, this);
20150         
20151         this.fireEvent("show", this);
20152         
20153         
20154     },
20155     
20156     hide : function()
20157     {
20158         this.fireEvent("beforehide", this);
20159         
20160         this.hidden = true;
20161         this.el.removeClass('open');
20162         
20163         Roo.get(document).un("mouseup", this.onMouseUp);
20164         
20165         this.fireEvent("hide", this);
20166     },
20167     
20168     onMouseUp : function()
20169     {
20170         this.hide();
20171     }
20172     
20173 });
20174
20175  
20176  /*
20177  * - LGPL
20178  *
20179  * menu item
20180  * 
20181  */
20182 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20183
20184 /**
20185  * @class Roo.bootstrap.menu.Item
20186  * @extends Roo.bootstrap.Component
20187  * Bootstrap MenuItem class
20188  * @cfg {Boolean} submenu (true | false) default false
20189  * @cfg {String} html text of the item
20190  * @cfg {String} href the link
20191  * @cfg {Boolean} disable (true | false) default false
20192  * @cfg {Boolean} preventDefault (true | false) default true
20193  * @cfg {String} icon Font awesome icon
20194  * @cfg {String} pos Submenu align to (left | right) default right 
20195  * 
20196  * 
20197  * @constructor
20198  * Create a new Item
20199  * @param {Object} config The config object
20200  */
20201
20202
20203 Roo.bootstrap.menu.Item = function(config){
20204     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20205     this.addEvents({
20206         /**
20207          * @event mouseover
20208          * Fires when the mouse is hovering over this menu
20209          * @param {Roo.bootstrap.menu.Item} this
20210          * @param {Roo.EventObject} e
20211          */
20212         mouseover : true,
20213         /**
20214          * @event mouseout
20215          * Fires when the mouse exits this menu
20216          * @param {Roo.bootstrap.menu.Item} this
20217          * @param {Roo.EventObject} e
20218          */
20219         mouseout : true,
20220         // raw events
20221         /**
20222          * @event click
20223          * The raw click event for the entire grid.
20224          * @param {Roo.EventObject} e
20225          */
20226         click : true
20227     });
20228 };
20229
20230 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20231     
20232     submenu : false,
20233     href : '',
20234     html : '',
20235     preventDefault: true,
20236     disable : false,
20237     icon : false,
20238     pos : 'right',
20239     
20240     getAutoCreate : function()
20241     {
20242         var text = [
20243             {
20244                 tag : 'span',
20245                 cls : 'roo-menu-item-text',
20246                 html : this.html
20247             }
20248         ];
20249         
20250         if(this.icon){
20251             text.unshift({
20252                 tag : 'i',
20253                 cls : 'fa ' + this.icon
20254             })
20255         }
20256         
20257         var cfg = {
20258             tag : 'li',
20259             cn : [
20260                 {
20261                     tag : 'a',
20262                     href : this.href || '#',
20263                     cn : text
20264                 }
20265             ]
20266         };
20267         
20268         if(this.disable){
20269             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20270         }
20271         
20272         if(this.submenu){
20273             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20274             
20275             if(this.pos == 'left'){
20276                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20277             }
20278         }
20279         
20280         return cfg;
20281     },
20282     
20283     initEvents : function() 
20284     {
20285         this.el.on('mouseover', this.onMouseOver, this);
20286         this.el.on('mouseout', this.onMouseOut, this);
20287         
20288         this.el.select('a', true).first().on('click', this.onClick, this);
20289         
20290     },
20291     
20292     onClick : function(e)
20293     {
20294         if(this.preventDefault){
20295             e.preventDefault();
20296         }
20297         
20298         this.fireEvent("click", this, e);
20299     },
20300     
20301     onMouseOver : function(e)
20302     {
20303         if(this.submenu && this.pos == 'left'){
20304             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20305         }
20306         
20307         this.fireEvent("mouseover", this, e);
20308     },
20309     
20310     onMouseOut : function(e)
20311     {
20312         this.fireEvent("mouseout", this, e);
20313     }
20314 });
20315
20316  
20317
20318  /*
20319  * - LGPL
20320  *
20321  * menu separator
20322  * 
20323  */
20324 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20325
20326 /**
20327  * @class Roo.bootstrap.menu.Separator
20328  * @extends Roo.bootstrap.Component
20329  * Bootstrap Separator class
20330  * 
20331  * @constructor
20332  * Create a new Separator
20333  * @param {Object} config The config object
20334  */
20335
20336
20337 Roo.bootstrap.menu.Separator = function(config){
20338     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20339 };
20340
20341 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20342     
20343     getAutoCreate : function(){
20344         var cfg = {
20345             tag : 'li',
20346             cls: 'divider'
20347         };
20348         
20349         return cfg;
20350     }
20351    
20352 });
20353
20354  
20355
20356