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                
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              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr](true));
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @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
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
775  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
777  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
778  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
779  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
780  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
781  *
782  * 
783  * @cfg {Boolean} hidden (true|false) hide the element
784  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
785  * @cfg {String} fa (ban|check|...) font awesome icon
786  * @cfg {Number} fasize (1|2|....) font awsome size
787
788  * @cfg {String} icon (info-sign|check|...) glyphicon name
789
790  * @cfg {String} html content of column.
791  * 
792  * @constructor
793  * Create a new Column
794  * @param {Object} config The config object
795  */
796
797 Roo.bootstrap.Column = function(config){
798     Roo.bootstrap.Column.superclass.constructor.call(this, config);
799 };
800
801 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
802     
803     xs: false,
804     sm: false,
805     md: false,
806     lg: false,
807     xsoff: false,
808     smoff: false,
809     mdoff: false,
810     lgoff: false,
811     html: '',
812     offset: 0,
813     alert: false,
814     fa: false,
815     icon : false,
816     hidden : false,
817     fasize : 1,
818     
819     getAutoCreate : function(){
820         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
821         
822         cfg = {
823             tag: 'div',
824             cls: 'column'
825         };
826         
827         var settings=this;
828         ['xs','sm','md','lg'].map(function(size){
829             //Roo.log( size + ':' + settings[size]);
830             
831             if (settings[size+'off'] !== false) {
832                 cfg.cls += ' col-' + settings[size+'off'] + '-offset';
833             }
834             
835             if (settings[size] === false) {
836                 return;
837             }
838             Roo.log(settings[size]);
839             if (!settings[size]) { // 0 = hidden
840                 cfg.cls += ' hidden-' + size;
841                 return;
842             }
843             cfg.cls += ' col-' + size + '-' + settings[size];
844             
845         });
846         
847         if (this.hidden) {
848             cfg.cls += ' hidden';
849         }
850         
851         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
852             cfg.cls +=' alert alert-' + this.alert;
853         }
854         
855         
856         if (this.html.length) {
857             cfg.html = this.html;
858         }
859         if (this.fa) {
860             var fasize = '';
861             if (this.fasize > 1) {
862                 fasize = ' fa-' + this.fasize + 'x';
863             }
864             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
865             
866             
867         }
868         if (this.icon) {
869             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
870         }
871         
872         return cfg;
873     }
874    
875 });
876
877  
878
879  /*
880  * - LGPL
881  *
882  * page container.
883  * 
884  */
885
886
887 /**
888  * @class Roo.bootstrap.Container
889  * @extends Roo.bootstrap.Component
890  * Bootstrap Container class
891  * @cfg {Boolean} jumbotron is it a jumbotron element
892  * @cfg {String} html content of element
893  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
894  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
895  * @cfg {String} header content of header (for panel)
896  * @cfg {String} footer content of footer (for panel)
897  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
898  * @cfg {String} tag (header|aside|section) type of HTML tag.
899  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
900  * @cfg {String} fa (ban|check|...) font awesome icon
901  * @cfg {String} icon (info-sign|check|...) glyphicon name
902  * @cfg {Boolean} hidden (true|false) hide the element
903
904  *     
905  * @constructor
906  * Create a new Container
907  * @param {Object} config The config object
908  */
909
910 Roo.bootstrap.Container = function(config){
911     Roo.bootstrap.Container.superclass.constructor.call(this, config);
912 };
913
914 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
915     
916     jumbotron : false,
917     well: '',
918     panel : '',
919     header: '',
920     footer : '',
921     sticky: '',
922     tag : false,
923     alert : false,
924     fa: false,
925     icon : false,
926   
927      
928     getChildContainer : function() {
929         
930         if(!this.el){
931             return false;
932         }
933         
934         if (this.panel.length) {
935             return this.el.select('.panel-body',true).first();
936         }
937         
938         return this.el;
939     },
940     
941     
942     getAutoCreate : function(){
943         
944         var cfg = {
945             tag : this.tag || 'div',
946             html : '',
947             cls : ''
948         };
949         if (this.jumbotron) {
950             cfg.cls = 'jumbotron';
951         }
952         
953         
954         
955         // - this is applied by the parent..
956         //if (this.cls) {
957         //    cfg.cls = this.cls + '';
958         //}
959         
960         if (this.sticky.length) {
961             
962             var bd = Roo.get(document.body);
963             if (!bd.hasClass('bootstrap-sticky')) {
964                 bd.addClass('bootstrap-sticky');
965                 Roo.select('html',true).setStyle('height', '100%');
966             }
967              
968             cfg.cls += 'bootstrap-sticky-' + this.sticky;
969         }
970         
971         
972         if (this.well.length) {
973             switch (this.well) {
974                 case 'lg':
975                 case 'sm':
976                     cfg.cls +=' well well-' +this.well;
977                     break;
978                 default:
979                     cfg.cls +=' well';
980                     break;
981             }
982         }
983         
984         if (this.hidden) {
985             cfg.cls += ' hidden';
986         }
987         
988         
989         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
990             cfg.cls +=' alert alert-' + this.alert;
991         }
992         
993         var body = cfg;
994         
995         if (this.panel.length) {
996             cfg.cls += ' panel panel-' + this.panel;
997             cfg.cn = [];
998             if (this.header.length) {
999                 cfg.cn.push({
1000                     
1001                     cls : 'panel-heading',
1002                     cn : [{
1003                         tag: 'h3',
1004                         cls : 'panel-title',
1005                         html : this.header
1006                     }]
1007                     
1008                 });
1009             }
1010             body = false;
1011             cfg.cn.push({
1012                 cls : 'panel-body',
1013                 html : this.html
1014             });
1015             
1016             
1017             if (this.footer.length) {
1018                 cfg.cn.push({
1019                     cls : 'panel-footer',
1020                     html : this.footer
1021                     
1022                 });
1023             }
1024             
1025         }
1026         
1027         if (body) {
1028             body.html = this.html || cfg.html;
1029             // prefix with the icons..
1030             if (this.fa) {
1031                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1032             }
1033             if (this.icon) {
1034                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1035             }
1036             
1037             
1038         }
1039         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1040             cfg.cls =  'container';
1041         }
1042         
1043         return cfg;
1044     },
1045     
1046     titleEl : function()
1047     {
1048         if(!this.el || !this.panel.length || !this.header.length){
1049             return;
1050         }
1051         
1052         return this.el.select('.panel-title',true).first();
1053     },
1054     
1055     setTitle : function(v)
1056     {
1057         var titleEl = this.titleEl();
1058         
1059         if(!titleEl){
1060             return;
1061         }
1062         
1063         titleEl.dom.innerHTML = v;
1064     },
1065     
1066     getTitle : function()
1067     {
1068         
1069         var titleEl = this.titleEl();
1070         
1071         if(!titleEl){
1072             return '';
1073         }
1074         
1075         return titleEl.dom.innerHTML;
1076     }
1077    
1078 });
1079
1080  /*
1081  * - LGPL
1082  *
1083  * image
1084  * 
1085  */
1086
1087
1088 /**
1089  * @class Roo.bootstrap.Img
1090  * @extends Roo.bootstrap.Component
1091  * Bootstrap Img class
1092  * @cfg {Boolean} imgResponsive false | true
1093  * @cfg {String} border rounded | circle | thumbnail
1094  * @cfg {String} src image source
1095  * @cfg {String} alt image alternative text
1096  * @cfg {String} href a tag href
1097  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1098  * 
1099  * @constructor
1100  * Create a new Input
1101  * @param {Object} config The config object
1102  */
1103
1104 Roo.bootstrap.Img = function(config){
1105     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1106     
1107     this.addEvents({
1108         // img events
1109         /**
1110          * @event click
1111          * The img click event for the img.
1112          * @param {Roo.EventObject} e
1113          */
1114         "click" : true
1115     });
1116 };
1117
1118 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1119     
1120     imgResponsive: true,
1121     border: '',
1122     src: '',
1123     href: false,
1124     target: false,
1125
1126     getAutoCreate : function(){
1127         
1128         var cfg = {
1129             tag: 'img',
1130             cls: (this.imgResponsive) ? 'img-responsive' : '',
1131             html : null
1132         }
1133         
1134         cfg.html = this.html || cfg.html;
1135         
1136         cfg.src = this.src || cfg.src;
1137         
1138         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1139             cfg.cls += ' img-' + this.border;
1140         }
1141         
1142         if(this.alt){
1143             cfg.alt = this.alt;
1144         }
1145         
1146         if(this.href){
1147             var a = {
1148                 tag: 'a',
1149                 href: this.href,
1150                 cn: [
1151                     cfg
1152                 ]
1153             }
1154             
1155             if(this.target){
1156                 a.target = this.target;
1157             }
1158             
1159         }
1160         
1161         
1162         return (this.href) ? a : cfg;
1163     },
1164     
1165     initEvents: function() {
1166         
1167         if(!this.href){
1168             this.el.on('click', this.onClick, this);
1169         }
1170     },
1171     
1172     onClick : function(e)
1173     {
1174         Roo.log('img onclick');
1175         this.fireEvent('click', this, e);
1176     }
1177    
1178 });
1179
1180  /*
1181  * - LGPL
1182  *
1183  * image
1184  * 
1185  */
1186
1187
1188 /**
1189  * @class Roo.bootstrap.Link
1190  * @extends Roo.bootstrap.Component
1191  * Bootstrap Link Class
1192  * @cfg {String} alt image alternative text
1193  * @cfg {String} href a tag href
1194  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1195  * @cfg {String} html the content of the link.
1196  * @cfg {String} anchor name for the anchor link
1197
1198  * @cfg {Boolean} preventDefault (true | false) default false
1199
1200  * 
1201  * @constructor
1202  * Create a new Input
1203  * @param {Object} config The config object
1204  */
1205
1206 Roo.bootstrap.Link = function(config){
1207     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1208     
1209     this.addEvents({
1210         // img events
1211         /**
1212          * @event click
1213          * The img click event for the img.
1214          * @param {Roo.EventObject} e
1215          */
1216         "click" : true
1217     });
1218 };
1219
1220 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1221     
1222     href: false,
1223     target: false,
1224     preventDefault: false,
1225     anchor : false,
1226     alt : false,
1227
1228     getAutoCreate : function()
1229     {
1230         
1231         var cfg = {
1232             tag: 'a'
1233         };
1234         // anchor's do not require html/href...
1235         if (this.anchor === false) {
1236             cfg.html = this.html || 'html-missing';
1237             cfg.href = this.href || '#';
1238         } else {
1239             cfg.name = this.anchor;
1240             if (this.html !== false) {
1241                 cfg.html = this.html;
1242             }
1243             if (this.href !== false) {
1244                 cfg.href = this.href;
1245             }
1246         }
1247         
1248         if(this.alt !== false){
1249             cfg.alt = this.alt;
1250         }
1251         
1252         
1253         if(this.target !== false) {
1254             cfg.target = this.target;
1255         }
1256         
1257         return cfg;
1258     },
1259     
1260     initEvents: function() {
1261         
1262         if(!this.href || this.preventDefault){
1263             this.el.on('click', this.onClick, this);
1264         }
1265     },
1266     
1267     onClick : function(e)
1268     {
1269         if(this.preventDefault){
1270             e.preventDefault();
1271         }
1272         //Roo.log('img onclick');
1273         this.fireEvent('click', this, e);
1274     }
1275    
1276 });
1277
1278  /*
1279  * - LGPL
1280  *
1281  * header
1282  * 
1283  */
1284
1285 /**
1286  * @class Roo.bootstrap.Header
1287  * @extends Roo.bootstrap.Component
1288  * Bootstrap Header class
1289  * @cfg {String} html content of header
1290  * @cfg {Number} level (1|2|3|4|5|6) default 1
1291  * 
1292  * @constructor
1293  * Create a new Header
1294  * @param {Object} config The config object
1295  */
1296
1297
1298 Roo.bootstrap.Header  = function(config){
1299     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1300 };
1301
1302 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1303     
1304     //href : false,
1305     html : false,
1306     level : 1,
1307     
1308     
1309     
1310     getAutoCreate : function(){
1311         
1312         var cfg = {
1313             tag: 'h' + (1 *this.level),
1314             html: this.html || 'fill in html'
1315         } ;
1316         
1317         return cfg;
1318     }
1319    
1320 });
1321
1322  
1323
1324  /*
1325  * Based on:
1326  * Ext JS Library 1.1.1
1327  * Copyright(c) 2006-2007, Ext JS, LLC.
1328  *
1329  * Originally Released Under LGPL - original licence link has changed is not relivant.
1330  *
1331  * Fork - LGPL
1332  * <script type="text/javascript">
1333  */
1334  
1335 /**
1336  * @class Roo.bootstrap.MenuMgr
1337  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1338  * @singleton
1339  */
1340 Roo.bootstrap.MenuMgr = function(){
1341    var menus, active, groups = {}, attached = false, lastShow = new Date();
1342
1343    // private - called when first menu is created
1344    function init(){
1345        menus = {};
1346        active = new Roo.util.MixedCollection();
1347        Roo.get(document).addKeyListener(27, function(){
1348            if(active.length > 0){
1349                hideAll();
1350            }
1351        });
1352    }
1353
1354    // private
1355    function hideAll(){
1356        if(active && active.length > 0){
1357            var c = active.clone();
1358            c.each(function(m){
1359                m.hide();
1360            });
1361        }
1362    }
1363
1364    // private
1365    function onHide(m){
1366        active.remove(m);
1367        if(active.length < 1){
1368            Roo.get(document).un("mouseup", onMouseDown);
1369             
1370            attached = false;
1371        }
1372    }
1373
1374    // private
1375    function onShow(m){
1376        var last = active.last();
1377        lastShow = new Date();
1378        active.add(m);
1379        if(!attached){
1380           Roo.get(document).on("mouseup", onMouseDown);
1381            
1382            attached = true;
1383        }
1384        if(m.parentMenu){
1385           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1386           m.parentMenu.activeChild = m;
1387        }else if(last && last.isVisible()){
1388           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1389        }
1390    }
1391
1392    // private
1393    function onBeforeHide(m){
1394        if(m.activeChild){
1395            m.activeChild.hide();
1396        }
1397        if(m.autoHideTimer){
1398            clearTimeout(m.autoHideTimer);
1399            delete m.autoHideTimer;
1400        }
1401    }
1402
1403    // private
1404    function onBeforeShow(m){
1405        var pm = m.parentMenu;
1406        if(!pm && !m.allowOtherMenus){
1407            hideAll();
1408        }else if(pm && pm.activeChild && active != m){
1409            pm.activeChild.hide();
1410        }
1411    }
1412
1413    // private
1414    function onMouseDown(e){
1415         Roo.log("on MouseDown");
1416         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1417            hideAll();
1418         }
1419         
1420         
1421    }
1422
1423    // private
1424    function onBeforeCheck(mi, state){
1425        if(state){
1426            var g = groups[mi.group];
1427            for(var i = 0, l = g.length; i < l; i++){
1428                if(g[i] != mi){
1429                    g[i].setChecked(false);
1430                }
1431            }
1432        }
1433    }
1434
1435    return {
1436
1437        /**
1438         * Hides all menus that are currently visible
1439         */
1440        hideAll : function(){
1441             hideAll();  
1442        },
1443
1444        // private
1445        register : function(menu){
1446            if(!menus){
1447                init();
1448            }
1449            menus[menu.id] = menu;
1450            menu.on("beforehide", onBeforeHide);
1451            menu.on("hide", onHide);
1452            menu.on("beforeshow", onBeforeShow);
1453            menu.on("show", onShow);
1454            var g = menu.group;
1455            if(g && menu.events["checkchange"]){
1456                if(!groups[g]){
1457                    groups[g] = [];
1458                }
1459                groups[g].push(menu);
1460                menu.on("checkchange", onCheck);
1461            }
1462        },
1463
1464         /**
1465          * Returns a {@link Roo.menu.Menu} object
1466          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1467          * be used to generate and return a new Menu instance.
1468          */
1469        get : function(menu){
1470            if(typeof menu == "string"){ // menu id
1471                return menus[menu];
1472            }else if(menu.events){  // menu instance
1473                return menu;
1474            }
1475            /*else if(typeof menu.length == 'number'){ // array of menu items?
1476                return new Roo.bootstrap.Menu({items:menu});
1477            }else{ // otherwise, must be a config
1478                return new Roo.bootstrap.Menu(menu);
1479            }
1480            */
1481            return false;
1482        },
1483
1484        // private
1485        unregister : function(menu){
1486            delete menus[menu.id];
1487            menu.un("beforehide", onBeforeHide);
1488            menu.un("hide", onHide);
1489            menu.un("beforeshow", onBeforeShow);
1490            menu.un("show", onShow);
1491            var g = menu.group;
1492            if(g && menu.events["checkchange"]){
1493                groups[g].remove(menu);
1494                menu.un("checkchange", onCheck);
1495            }
1496        },
1497
1498        // private
1499        registerCheckable : function(menuItem){
1500            var g = menuItem.group;
1501            if(g){
1502                if(!groups[g]){
1503                    groups[g] = [];
1504                }
1505                groups[g].push(menuItem);
1506                menuItem.on("beforecheckchange", onBeforeCheck);
1507            }
1508        },
1509
1510        // private
1511        unregisterCheckable : function(menuItem){
1512            var g = menuItem.group;
1513            if(g){
1514                groups[g].remove(menuItem);
1515                menuItem.un("beforecheckchange", onBeforeCheck);
1516            }
1517        }
1518    };
1519 }();/*
1520  * - LGPL
1521  *
1522  * menu
1523  * 
1524  */
1525
1526 /**
1527  * @class Roo.bootstrap.Menu
1528  * @extends Roo.bootstrap.Component
1529  * Bootstrap Menu class - container for MenuItems
1530  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1531  * 
1532  * @constructor
1533  * Create a new Menu
1534  * @param {Object} config The config object
1535  */
1536
1537
1538 Roo.bootstrap.Menu = function(config){
1539     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1540     if (this.registerMenu) {
1541         Roo.bootstrap.MenuMgr.register(this);
1542     }
1543     this.addEvents({
1544         /**
1545          * @event beforeshow
1546          * Fires before this menu is displayed
1547          * @param {Roo.menu.Menu} this
1548          */
1549         beforeshow : true,
1550         /**
1551          * @event beforehide
1552          * Fires before this menu is hidden
1553          * @param {Roo.menu.Menu} this
1554          */
1555         beforehide : true,
1556         /**
1557          * @event show
1558          * Fires after this menu is displayed
1559          * @param {Roo.menu.Menu} this
1560          */
1561         show : true,
1562         /**
1563          * @event hide
1564          * Fires after this menu is hidden
1565          * @param {Roo.menu.Menu} this
1566          */
1567         hide : true,
1568         /**
1569          * @event click
1570          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1571          * @param {Roo.menu.Menu} this
1572          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1573          * @param {Roo.EventObject} e
1574          */
1575         click : true,
1576         /**
1577          * @event mouseover
1578          * Fires when the mouse is hovering over this menu
1579          * @param {Roo.menu.Menu} this
1580          * @param {Roo.EventObject} e
1581          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1582          */
1583         mouseover : true,
1584         /**
1585          * @event mouseout
1586          * Fires when the mouse exits this menu
1587          * @param {Roo.menu.Menu} this
1588          * @param {Roo.EventObject} e
1589          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1590          */
1591         mouseout : true,
1592         /**
1593          * @event itemclick
1594          * Fires when a menu item contained in this menu is clicked
1595          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1596          * @param {Roo.EventObject} e
1597          */
1598         itemclick: true
1599     });
1600     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1601 };
1602
1603 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1604     
1605    /// html : false,
1606     //align : '',
1607     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1608     type: false,
1609     /**
1610      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1611      */
1612     registerMenu : true,
1613     
1614     menuItems :false, // stores the menu items..
1615     
1616     hidden:true,
1617     
1618     parentMenu : false,
1619     
1620     getChildContainer : function() {
1621         return this.el;  
1622     },
1623     
1624     getAutoCreate : function(){
1625          
1626         //if (['right'].indexOf(this.align)!==-1) {
1627         //    cfg.cn[1].cls += ' pull-right'
1628         //}
1629         
1630         
1631         var cfg = {
1632             tag : 'ul',
1633             cls : 'dropdown-menu' ,
1634             style : 'z-index:1000'
1635             
1636         }
1637         
1638         if (this.type === 'submenu') {
1639             cfg.cls = 'submenu active';
1640         }
1641         if (this.type === 'treeview') {
1642             cfg.cls = 'treeview-menu';
1643         }
1644         
1645         return cfg;
1646     },
1647     initEvents : function() {
1648         
1649        // Roo.log("ADD event");
1650        // Roo.log(this.triggerEl.dom);
1651         this.triggerEl.on('click', this.onTriggerPress, this);
1652         this.triggerEl.addClass('dropdown-toggle');
1653         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1654
1655         this.el.on("mouseover", this.onMouseOver, this);
1656         this.el.on("mouseout", this.onMouseOut, this);
1657         
1658         
1659     },
1660     findTargetItem : function(e){
1661         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1662         if(!t){
1663             return false;
1664         }
1665         //Roo.log(t);         Roo.log(t.id);
1666         if(t && t.id){
1667             //Roo.log(this.menuitems);
1668             return this.menuitems.get(t.id);
1669             
1670             //return this.items.get(t.menuItemId);
1671         }
1672         
1673         return false;
1674     },
1675     onClick : function(e){
1676         Roo.log("menu.onClick");
1677         var t = this.findTargetItem(e);
1678         if(!t){
1679             return;
1680         }
1681         Roo.log(e);
1682         /*
1683         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1684             if(t == this.activeItem && t.shouldDeactivate(e)){
1685                 this.activeItem.deactivate();
1686                 delete this.activeItem;
1687                 return;
1688             }
1689             if(t.canActivate){
1690                 this.setActiveItem(t, true);
1691             }
1692             return;
1693             
1694             
1695         }
1696         */
1697         Roo.log('pass click event');
1698         
1699         t.onClick(e);
1700         
1701         this.fireEvent("click", this, t, e);
1702         
1703         this.hide();
1704     },
1705      onMouseOver : function(e){
1706         var t  = this.findTargetItem(e);
1707         //Roo.log(t);
1708         //if(t){
1709         //    if(t.canActivate && !t.disabled){
1710         //        this.setActiveItem(t, true);
1711         //    }
1712         //}
1713         
1714         this.fireEvent("mouseover", this, e, t);
1715     },
1716     isVisible : function(){
1717         return !this.hidden;
1718     },
1719      onMouseOut : function(e){
1720         var t  = this.findTargetItem(e);
1721         
1722         //if(t ){
1723         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1724         //        this.activeItem.deactivate();
1725         //        delete this.activeItem;
1726         //    }
1727         //}
1728         this.fireEvent("mouseout", this, e, t);
1729     },
1730     
1731     
1732     /**
1733      * Displays this menu relative to another element
1734      * @param {String/HTMLElement/Roo.Element} element The element to align to
1735      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1736      * the element (defaults to this.defaultAlign)
1737      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1738      */
1739     show : function(el, pos, parentMenu){
1740         this.parentMenu = parentMenu;
1741         if(!this.el){
1742             this.render();
1743         }
1744         this.fireEvent("beforeshow", this);
1745         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1746     },
1747      /**
1748      * Displays this menu at a specific xy position
1749      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1750      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1751      */
1752     showAt : function(xy, parentMenu, /* private: */_e){
1753         this.parentMenu = parentMenu;
1754         if(!this.el){
1755             this.render();
1756         }
1757         if(_e !== false){
1758             this.fireEvent("beforeshow", this);
1759             
1760             //xy = this.el.adjustForConstraints(xy);
1761         }
1762         //this.el.setXY(xy);
1763         //this.el.show();
1764         this.hideMenuItems();
1765         this.hidden = false;
1766         this.triggerEl.addClass('open');
1767         this.focus();
1768         this.fireEvent("show", this);
1769     },
1770     
1771     focus : function(){
1772         return;
1773         if(!this.hidden){
1774             this.doFocus.defer(50, this);
1775         }
1776     },
1777
1778     doFocus : function(){
1779         if(!this.hidden){
1780             this.focusEl.focus();
1781         }
1782     },
1783
1784     /**
1785      * Hides this menu and optionally all parent menus
1786      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1787      */
1788     hide : function(deep){
1789         
1790         this.hideMenuItems();
1791         if(this.el && this.isVisible()){
1792             this.fireEvent("beforehide", this);
1793             if(this.activeItem){
1794                 this.activeItem.deactivate();
1795                 this.activeItem = null;
1796             }
1797             this.triggerEl.removeClass('open');;
1798             this.hidden = true;
1799             this.fireEvent("hide", this);
1800         }
1801         if(deep === true && this.parentMenu){
1802             this.parentMenu.hide(true);
1803         }
1804     },
1805     
1806     onTriggerPress  : function(e)
1807     {
1808         
1809         Roo.log('trigger press');
1810         //Roo.log(e.getTarget());
1811        // Roo.log(this.triggerEl.dom);
1812         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1813             return;
1814         }
1815         if (this.isVisible()) {
1816             Roo.log('hide');
1817             this.hide();
1818         } else {
1819             this.show(this.triggerEl, false, false);
1820         }
1821         
1822         
1823     },
1824     
1825          
1826        
1827     
1828     hideMenuItems : function()
1829     {
1830         //$(backdrop).remove()
1831         Roo.select('.open',true).each(function(aa) {
1832             
1833             aa.removeClass('open');
1834           //var parent = getParent($(this))
1835           //var relatedTarget = { relatedTarget: this }
1836           
1837            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1838           //if (e.isDefaultPrevented()) return
1839            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1840         })
1841     },
1842     addxtypeChild : function (tree, cntr) {
1843         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1844           
1845         this.menuitems.add(comp);
1846         return comp;
1847
1848     },
1849     getEl : function()
1850     {
1851         Roo.log(this.el);
1852         return this.el;
1853     }
1854 });
1855
1856  
1857  /*
1858  * - LGPL
1859  *
1860  * menu item
1861  * 
1862  */
1863
1864
1865 /**
1866  * @class Roo.bootstrap.MenuItem
1867  * @extends Roo.bootstrap.Component
1868  * Bootstrap MenuItem class
1869  * @cfg {String} html the menu label
1870  * @cfg {String} href the link
1871  * @cfg {Boolean} preventDefault (true | false) default true
1872  * 
1873  * 
1874  * @constructor
1875  * Create a new MenuItem
1876  * @param {Object} config The config object
1877  */
1878
1879
1880 Roo.bootstrap.MenuItem = function(config){
1881     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1882     this.addEvents({
1883         // raw events
1884         /**
1885          * @event click
1886          * The raw click event for the entire grid.
1887          * @param {Roo.EventObject} e
1888          */
1889         "click" : true
1890     });
1891 };
1892
1893 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1894     
1895     href : false,
1896     html : false,
1897     preventDefault: true,
1898     
1899     getAutoCreate : function(){
1900         var cfg= {
1901             tag: 'li',
1902             cls: 'dropdown-menu-item',
1903             cn: [
1904                     {
1905                         tag : 'a',
1906                         href : '#',
1907                         html : 'Link'
1908                     }
1909                 ]
1910         };
1911         if (this.parent().type == 'treeview') {
1912             cfg.cls = 'treeview-menu';
1913         }
1914         
1915         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1916         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1917         return cfg;
1918     },
1919     
1920     initEvents: function() {
1921         
1922         //this.el.select('a').on('click', this.onClick, this);
1923         
1924     },
1925     onClick : function(e)
1926     {
1927         Roo.log('item on click ');
1928         //if(this.preventDefault){
1929         //    e.preventDefault();
1930         //}
1931         //this.parent().hideMenuItems();
1932         
1933         this.fireEvent('click', this, e);
1934     },
1935     getEl : function()
1936     {
1937         return this.el;
1938     }
1939 });
1940
1941  
1942
1943  /*
1944  * - LGPL
1945  *
1946  * menu separator
1947  * 
1948  */
1949
1950
1951 /**
1952  * @class Roo.bootstrap.MenuSeparator
1953  * @extends Roo.bootstrap.Component
1954  * Bootstrap MenuSeparator class
1955  * 
1956  * @constructor
1957  * Create a new MenuItem
1958  * @param {Object} config The config object
1959  */
1960
1961
1962 Roo.bootstrap.MenuSeparator = function(config){
1963     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1964 };
1965
1966 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1967     
1968     getAutoCreate : function(){
1969         var cfg = {
1970             cls: 'divider',
1971             tag : 'li'
1972         };
1973         
1974         return cfg;
1975     }
1976    
1977 });
1978
1979  
1980
1981  
1982 /*
1983 <div class="modal fade">
1984   <div class="modal-dialog">
1985     <div class="modal-content">
1986       <div class="modal-header">
1987         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1988         <h4 class="modal-title">Modal title</h4>
1989       </div>
1990       <div class="modal-body">
1991         <p>One fine body&hellip;</p>
1992       </div>
1993       <div class="modal-footer">
1994         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1995         <button type="button" class="btn btn-primary">Save changes</button>
1996       </div>
1997     </div><!-- /.modal-content -->
1998   </div><!-- /.modal-dialog -->
1999 </div><!-- /.modal -->
2000 */
2001 /*
2002  * - LGPL
2003  *
2004  * page contgainer.
2005  * 
2006  */
2007
2008 /**
2009  * @class Roo.bootstrap.Modal
2010  * @extends Roo.bootstrap.Component
2011  * Bootstrap Modal class
2012  * @cfg {String} title Title of dialog
2013  * @cfg {Boolean} specificTitle (true|false) default false
2014  * @cfg {Array} buttons Array of buttons or standard button set..
2015  * @cfg {String} buttonPosition (left|right|center) default right
2016  * 
2017  * @constructor
2018  * Create a new Modal Dialog
2019  * @param {Object} config The config object
2020  */
2021
2022 Roo.bootstrap.Modal = function(config){
2023     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2024     this.addEvents({
2025         // raw events
2026         /**
2027          * @event btnclick
2028          * The raw btnclick event for the button
2029          * @param {Roo.EventObject} e
2030          */
2031         "btnclick" : true
2032     });
2033     this.buttons = this.buttons || [];
2034 };
2035
2036 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2037     
2038     title : 'test dialog',
2039    
2040     buttons : false,
2041     
2042     // set on load...
2043     body:  false,
2044     
2045     specificTitle: false,
2046     
2047     buttonPosition: 'right',
2048     
2049     onRender : function(ct, position)
2050     {
2051         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2052      
2053         if(!this.el){
2054             var cfg = Roo.apply({},  this.getAutoCreate());
2055             cfg.id = Roo.id();
2056             //if(!cfg.name){
2057             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2058             //}
2059             //if (!cfg.name.length) {
2060             //    delete cfg.name;
2061            // }
2062             if (this.cls) {
2063                 cfg.cls += ' ' + this.cls;
2064             }
2065             if (this.style) {
2066                 cfg.style = this.style;
2067             }
2068             this.el = Roo.get(document.body).createChild(cfg, position);
2069         }
2070         //var type = this.el.dom.type;
2071         
2072         if(this.tabIndex !== undefined){
2073             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2074         }
2075         
2076         
2077         
2078         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2079         this.maskEl.enableDisplayMode("block");
2080         this.maskEl.hide();
2081         //this.el.addClass("x-dlg-modal");
2082     
2083         if (this.buttons.length) {
2084             Roo.each(this.buttons, function(bb) {
2085                 b = Roo.apply({}, bb);
2086                 b.xns = b.xns || Roo.bootstrap;
2087                 b.xtype = b.xtype || 'Button';
2088                 if (typeof(b.listeners) == 'undefined') {
2089                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2090                 }
2091                 
2092                 var btn = Roo.factory(b);
2093                 
2094                 btn.onRender(this.el.select('.modal-footer div').first());
2095                 
2096             },this);
2097         }
2098         // render the children.
2099         var nitems = [];
2100         
2101         if(typeof(this.items) != 'undefined'){
2102             var items = this.items;
2103             delete this.items;
2104
2105             for(var i =0;i < items.length;i++) {
2106                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2107             }
2108         }
2109         
2110         this.items = nitems;
2111         
2112         this.body = this.el.select('.modal-body',true).first();
2113         this.close = this.el.select('.modal-header .close', true).first();
2114         this.footer = this.el.select('.modal-footer',true).first();
2115         this.initEvents();
2116         //this.el.addClass([this.fieldClass, this.cls]);
2117         
2118     },
2119     getAutoCreate : function(){
2120         
2121         
2122         var bdy = {
2123                 cls : 'modal-body',
2124                 html : this.html || ''
2125         };
2126         
2127         var title = {
2128             tag: 'h4',
2129             cls : 'modal-title',
2130             html : this.title
2131         };
2132         
2133         if(this.specificTitle){
2134             title = this.title;
2135         };
2136         
2137         return modal = {
2138             cls: "modal fade",
2139             style : 'display: none',
2140             cn : [
2141                 {
2142                     cls: "modal-dialog",
2143                     cn : [
2144                         {
2145                             cls : "modal-content",
2146                             cn : [
2147                                 {
2148                                     cls : 'modal-header',
2149                                     cn : [
2150                                         {
2151                                             tag: 'button',
2152                                             cls : 'close',
2153                                             html : '&times'
2154                                         },
2155                                         title
2156                                     ]
2157                                 },
2158                                 bdy,
2159                                 {
2160                                     cls : 'modal-footer',
2161                                     cn : [
2162                                         {
2163                                             tag: 'div',
2164                                             cls: 'btn-' + this.buttonPosition
2165                                         }
2166                                     ]
2167                                     
2168                                 }
2169                                 
2170                                 
2171                             ]
2172                             
2173                         }
2174                     ]
2175                         
2176                 }
2177             ]
2178             
2179             
2180         };
2181           
2182     },
2183     getChildContainer : function() {
2184          
2185          return this.el.select('.modal-body',true).first();
2186         
2187     },
2188     getButtonContainer : function() {
2189          return this.el.select('.modal-footer div',true).first();
2190         
2191     },
2192     initEvents : function()
2193     {
2194         this.el.select('.modal-header .close').on('click', this.hide, this);
2195 //        
2196 //        this.addxtype(this);
2197     },
2198     show : function() {
2199         
2200         if (!this.rendered) {
2201             this.render();
2202         }
2203        
2204         this.el.addClass('on');
2205         this.el.removeClass('fade');
2206         this.el.setStyle('display', 'block');
2207         Roo.get(document.body).addClass("x-body-masked");
2208         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2209         this.maskEl.show();
2210         this.el.setStyle('zIndex', '10001');
2211         this.fireEvent('show', this);
2212         
2213         
2214     },
2215     hide : function()
2216     {
2217         Roo.log('Modal hide?!');
2218         this.maskEl.hide();
2219         Roo.get(document.body).removeClass("x-body-masked");
2220         this.el.removeClass('on');
2221         this.el.addClass('fade');
2222         this.el.setStyle('display', 'none');
2223         this.fireEvent('hide', this);
2224     },
2225     
2226     addButton : function(str, cb)
2227     {
2228          
2229         
2230         var b = Roo.apply({}, { html : str } );
2231         b.xns = b.xns || Roo.bootstrap;
2232         b.xtype = b.xtype || 'Button';
2233         if (typeof(b.listeners) == 'undefined') {
2234             b.listeners = { click : cb.createDelegate(this)  };
2235         }
2236         
2237         var btn = Roo.factory(b);
2238            
2239         btn.onRender(this.el.select('.modal-footer div').first());
2240         
2241         return btn;   
2242        
2243     },
2244     
2245     setDefaultButton : function(btn)
2246     {
2247         //this.el.select('.modal-footer').()
2248     },
2249     resizeTo: function(w,h)
2250     {
2251         // skip..
2252     },
2253     setContentSize  : function(w, h)
2254     {
2255         
2256     },
2257     onButtonClick: function(btn,e)
2258     {
2259         //Roo.log([a,b,c]);
2260         this.fireEvent('btnclick', btn.name, e);
2261     },
2262     setTitle: function(str) {
2263         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2264         
2265     }
2266 });
2267
2268
2269 Roo.apply(Roo.bootstrap.Modal,  {
2270     /**
2271          * Button config that displays a single OK button
2272          * @type Object
2273          */
2274         OK :  [{
2275             name : 'ok',
2276             weight : 'primary',
2277             html : 'OK'
2278         }], 
2279         /**
2280          * Button config that displays Yes and No buttons
2281          * @type Object
2282          */
2283         YESNO : [
2284             {
2285                 name  : 'no',
2286                 html : 'No'
2287             },
2288             {
2289                 name  :'yes',
2290                 weight : 'primary',
2291                 html : 'Yes'
2292             }
2293         ],
2294         
2295         /**
2296          * Button config that displays OK and Cancel buttons
2297          * @type Object
2298          */
2299         OKCANCEL : [
2300             {
2301                name : 'cancel',
2302                 html : 'Cancel'
2303             },
2304             {
2305                 name : 'ok',
2306                 weight : 'primary',
2307                 html : 'OK'
2308             }
2309         ],
2310         /**
2311          * Button config that displays Yes, No and Cancel buttons
2312          * @type Object
2313          */
2314         YESNOCANCEL : [
2315             {
2316                 name : 'yes',
2317                 weight : 'primary',
2318                 html : 'Yes'
2319             },
2320             {
2321                 name : 'no',
2322                 html : 'No'
2323             },
2324             {
2325                 name : 'cancel',
2326                 html : 'Cancel'
2327             }
2328         ]
2329 });
2330  /*
2331  * - LGPL
2332  *
2333  * messagebox - can be used as a replace
2334  * 
2335  */
2336 /**
2337  * @class Roo.MessageBox
2338  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2339  * Example usage:
2340  *<pre><code>
2341 // Basic alert:
2342 Roo.Msg.alert('Status', 'Changes saved successfully.');
2343
2344 // Prompt for user data:
2345 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2346     if (btn == 'ok'){
2347         // process text value...
2348     }
2349 });
2350
2351 // Show a dialog using config options:
2352 Roo.Msg.show({
2353    title:'Save Changes?',
2354    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2355    buttons: Roo.Msg.YESNOCANCEL,
2356    fn: processResult,
2357    animEl: 'elId'
2358 });
2359 </code></pre>
2360  * @singleton
2361  */
2362 Roo.bootstrap.MessageBox = function(){
2363     var dlg, opt, mask, waitTimer;
2364     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2365     var buttons, activeTextEl, bwidth;
2366
2367     
2368     // private
2369     var handleButton = function(button){
2370         dlg.hide();
2371         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2372     };
2373
2374     // private
2375     var handleHide = function(){
2376         if(opt && opt.cls){
2377             dlg.el.removeClass(opt.cls);
2378         }
2379         //if(waitTimer){
2380         //    Roo.TaskMgr.stop(waitTimer);
2381         //    waitTimer = null;
2382         //}
2383     };
2384
2385     // private
2386     var updateButtons = function(b){
2387         var width = 0;
2388         if(!b){
2389             buttons["ok"].hide();
2390             buttons["cancel"].hide();
2391             buttons["yes"].hide();
2392             buttons["no"].hide();
2393             //dlg.footer.dom.style.display = 'none';
2394             return width;
2395         }
2396         dlg.footer.dom.style.display = '';
2397         for(var k in buttons){
2398             if(typeof buttons[k] != "function"){
2399                 if(b[k]){
2400                     buttons[k].show();
2401                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2402                     width += buttons[k].el.getWidth()+15;
2403                 }else{
2404                     buttons[k].hide();
2405                 }
2406             }
2407         }
2408         return width;
2409     };
2410
2411     // private
2412     var handleEsc = function(d, k, e){
2413         if(opt && opt.closable !== false){
2414             dlg.hide();
2415         }
2416         if(e){
2417             e.stopEvent();
2418         }
2419     };
2420
2421     return {
2422         /**
2423          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2424          * @return {Roo.BasicDialog} The BasicDialog element
2425          */
2426         getDialog : function(){
2427            if(!dlg){
2428                 dlg = new Roo.bootstrap.Modal( {
2429                     //draggable: true,
2430                     //resizable:false,
2431                     //constraintoviewport:false,
2432                     //fixedcenter:true,
2433                     //collapsible : false,
2434                     //shim:true,
2435                     //modal: true,
2436                   //  width:400,
2437                   //  height:100,
2438                     //buttonAlign:"center",
2439                     closeClick : function(){
2440                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2441                             handleButton("no");
2442                         }else{
2443                             handleButton("cancel");
2444                         }
2445                     }
2446                 });
2447                 dlg.render();
2448                 dlg.on("hide", handleHide);
2449                 mask = dlg.mask;
2450                 //dlg.addKeyListener(27, handleEsc);
2451                 buttons = {};
2452                 this.buttons = buttons;
2453                 var bt = this.buttonText;
2454                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2455                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2456                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2457                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2458                 Roo.log(buttons)
2459                 bodyEl = dlg.body.createChild({
2460
2461                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2462                         '<textarea class="roo-mb-textarea"></textarea>' +
2463                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2464                 });
2465                 msgEl = bodyEl.dom.firstChild;
2466                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2467                 textboxEl.enableDisplayMode();
2468                 textboxEl.addKeyListener([10,13], function(){
2469                     if(dlg.isVisible() && opt && opt.buttons){
2470                         if(opt.buttons.ok){
2471                             handleButton("ok");
2472                         }else if(opt.buttons.yes){
2473                             handleButton("yes");
2474                         }
2475                     }
2476                 });
2477                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2478                 textareaEl.enableDisplayMode();
2479                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2480                 progressEl.enableDisplayMode();
2481                 var pf = progressEl.dom.firstChild;
2482                 if (pf) {
2483                     pp = Roo.get(pf.firstChild);
2484                     pp.setHeight(pf.offsetHeight);
2485                 }
2486                 
2487             }
2488             return dlg;
2489         },
2490
2491         /**
2492          * Updates the message box body text
2493          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2494          * the XHTML-compliant non-breaking space character '&amp;#160;')
2495          * @return {Roo.MessageBox} This message box
2496          */
2497         updateText : function(text){
2498             if(!dlg.isVisible() && !opt.width){
2499                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2500             }
2501             msgEl.innerHTML = text || '&#160;';
2502       
2503             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2504             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2505             var w = Math.max(
2506                     Math.min(opt.width || cw , this.maxWidth), 
2507                     Math.max(opt.minWidth || this.minWidth, bwidth)
2508             );
2509             if(opt.prompt){
2510                 activeTextEl.setWidth(w);
2511             }
2512             if(dlg.isVisible()){
2513                 dlg.fixedcenter = false;
2514             }
2515             // to big, make it scroll. = But as usual stupid IE does not support
2516             // !important..
2517             
2518             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2519                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2520                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2521             } else {
2522                 bodyEl.dom.style.height = '';
2523                 bodyEl.dom.style.overflowY = '';
2524             }
2525             if (cw > w) {
2526                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2527             } else {
2528                 bodyEl.dom.style.overflowX = '';
2529             }
2530             
2531             dlg.setContentSize(w, bodyEl.getHeight());
2532             if(dlg.isVisible()){
2533                 dlg.fixedcenter = true;
2534             }
2535             return this;
2536         },
2537
2538         /**
2539          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2540          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2541          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2542          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2543          * @return {Roo.MessageBox} This message box
2544          */
2545         updateProgress : function(value, text){
2546             if(text){
2547                 this.updateText(text);
2548             }
2549             if (pp) { // weird bug on my firefox - for some reason this is not defined
2550                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2551             }
2552             return this;
2553         },        
2554
2555         /**
2556          * Returns true if the message box is currently displayed
2557          * @return {Boolean} True if the message box is visible, else false
2558          */
2559         isVisible : function(){
2560             return dlg && dlg.isVisible();  
2561         },
2562
2563         /**
2564          * Hides the message box if it is displayed
2565          */
2566         hide : function(){
2567             if(this.isVisible()){
2568                 dlg.hide();
2569             }  
2570         },
2571
2572         /**
2573          * Displays a new message box, or reinitializes an existing message box, based on the config options
2574          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2575          * The following config object properties are supported:
2576          * <pre>
2577 Property    Type             Description
2578 ----------  ---------------  ------------------------------------------------------------------------------------
2579 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2580                                    closes (defaults to undefined)
2581 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2582                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2583 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2584                                    progress and wait dialogs will ignore this property and always hide the
2585                                    close button as they can only be closed programmatically.
2586 cls               String           A custom CSS class to apply to the message box element
2587 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2588                                    displayed (defaults to 75)
2589 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2590                                    function will be btn (the name of the button that was clicked, if applicable,
2591                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2592                                    Progress and wait dialogs will ignore this option since they do not respond to
2593                                    user actions and can only be closed programmatically, so any required function
2594                                    should be called by the same code after it closes the dialog.
2595 icon              String           A CSS class that provides a background image to be used as an icon for
2596                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2597 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2598 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2599 modal             Boolean          False to allow user interaction with the page while the message box is
2600                                    displayed (defaults to true)
2601 msg               String           A string that will replace the existing message box body text (defaults
2602                                    to the XHTML-compliant non-breaking space character '&#160;')
2603 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2604 progress          Boolean          True to display a progress bar (defaults to false)
2605 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2606 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2607 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2608 title             String           The title text
2609 value             String           The string value to set into the active textbox element if displayed
2610 wait              Boolean          True to display a progress bar (defaults to false)
2611 width             Number           The width of the dialog in pixels
2612 </pre>
2613          *
2614          * Example usage:
2615          * <pre><code>
2616 Roo.Msg.show({
2617    title: 'Address',
2618    msg: 'Please enter your address:',
2619    width: 300,
2620    buttons: Roo.MessageBox.OKCANCEL,
2621    multiline: true,
2622    fn: saveAddress,
2623    animEl: 'addAddressBtn'
2624 });
2625 </code></pre>
2626          * @param {Object} config Configuration options
2627          * @return {Roo.MessageBox} This message box
2628          */
2629         show : function(options)
2630         {
2631             
2632             // this causes nightmares if you show one dialog after another
2633             // especially on callbacks..
2634              
2635             if(this.isVisible()){
2636                 
2637                 this.hide();
2638                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2639                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2640                 Roo.log("New Dialog Message:" +  options.msg )
2641                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2642                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2643                 
2644             }
2645             var d = this.getDialog();
2646             opt = options;
2647             d.setTitle(opt.title || "&#160;");
2648             d.close.setDisplayed(opt.closable !== false);
2649             activeTextEl = textboxEl;
2650             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2651             if(opt.prompt){
2652                 if(opt.multiline){
2653                     textboxEl.hide();
2654                     textareaEl.show();
2655                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2656                         opt.multiline : this.defaultTextHeight);
2657                     activeTextEl = textareaEl;
2658                 }else{
2659                     textboxEl.show();
2660                     textareaEl.hide();
2661                 }
2662             }else{
2663                 textboxEl.hide();
2664                 textareaEl.hide();
2665             }
2666             progressEl.setDisplayed(opt.progress === true);
2667             this.updateProgress(0);
2668             activeTextEl.dom.value = opt.value || "";
2669             if(opt.prompt){
2670                 dlg.setDefaultButton(activeTextEl);
2671             }else{
2672                 var bs = opt.buttons;
2673                 var db = null;
2674                 if(bs && bs.ok){
2675                     db = buttons["ok"];
2676                 }else if(bs && bs.yes){
2677                     db = buttons["yes"];
2678                 }
2679                 dlg.setDefaultButton(db);
2680             }
2681             bwidth = updateButtons(opt.buttons);
2682             this.updateText(opt.msg);
2683             if(opt.cls){
2684                 d.el.addClass(opt.cls);
2685             }
2686             d.proxyDrag = opt.proxyDrag === true;
2687             d.modal = opt.modal !== false;
2688             d.mask = opt.modal !== false ? mask : false;
2689             if(!d.isVisible()){
2690                 // force it to the end of the z-index stack so it gets a cursor in FF
2691                 document.body.appendChild(dlg.el.dom);
2692                 d.animateTarget = null;
2693                 d.show(options.animEl);
2694             }
2695             return this;
2696         },
2697
2698         /**
2699          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2700          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2701          * and closing the message box when the process is complete.
2702          * @param {String} title The title bar text
2703          * @param {String} msg The message box body text
2704          * @return {Roo.MessageBox} This message box
2705          */
2706         progress : function(title, msg){
2707             this.show({
2708                 title : title,
2709                 msg : msg,
2710                 buttons: false,
2711                 progress:true,
2712                 closable:false,
2713                 minWidth: this.minProgressWidth,
2714                 modal : true
2715             });
2716             return this;
2717         },
2718
2719         /**
2720          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2721          * If a callback function is passed it will be called after the user clicks the button, and the
2722          * id of the button that was clicked will be passed as the only parameter to the callback
2723          * (could also be the top-right close button).
2724          * @param {String} title The title bar text
2725          * @param {String} msg The message box body text
2726          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2727          * @param {Object} scope (optional) The scope of the callback function
2728          * @return {Roo.MessageBox} This message box
2729          */
2730         alert : function(title, msg, fn, scope){
2731             this.show({
2732                 title : title,
2733                 msg : msg,
2734                 buttons: this.OK,
2735                 fn: fn,
2736                 scope : scope,
2737                 modal : true
2738             });
2739             return this;
2740         },
2741
2742         /**
2743          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2744          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2745          * You are responsible for closing the message box when the process is complete.
2746          * @param {String} msg The message box body text
2747          * @param {String} title (optional) The title bar text
2748          * @return {Roo.MessageBox} This message box
2749          */
2750         wait : function(msg, title){
2751             this.show({
2752                 title : title,
2753                 msg : msg,
2754                 buttons: false,
2755                 closable:false,
2756                 progress:true,
2757                 modal:true,
2758                 width:300,
2759                 wait:true
2760             });
2761             waitTimer = Roo.TaskMgr.start({
2762                 run: function(i){
2763                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2764                 },
2765                 interval: 1000
2766             });
2767             return this;
2768         },
2769
2770         /**
2771          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2772          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2773          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2774          * @param {String} title The title bar text
2775          * @param {String} msg The message box body text
2776          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2777          * @param {Object} scope (optional) The scope of the callback function
2778          * @return {Roo.MessageBox} This message box
2779          */
2780         confirm : function(title, msg, fn, scope){
2781             this.show({
2782                 title : title,
2783                 msg : msg,
2784                 buttons: this.YESNO,
2785                 fn: fn,
2786                 scope : scope,
2787                 modal : true
2788             });
2789             return this;
2790         },
2791
2792         /**
2793          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2794          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2795          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2796          * (could also be the top-right close button) and the text that was entered will be passed as the two
2797          * parameters to the callback.
2798          * @param {String} title The title bar text
2799          * @param {String} msg The message box body text
2800          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2801          * @param {Object} scope (optional) The scope of the callback function
2802          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2803          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2804          * @return {Roo.MessageBox} This message box
2805          */
2806         prompt : function(title, msg, fn, scope, multiline){
2807             this.show({
2808                 title : title,
2809                 msg : msg,
2810                 buttons: this.OKCANCEL,
2811                 fn: fn,
2812                 minWidth:250,
2813                 scope : scope,
2814                 prompt:true,
2815                 multiline: multiline,
2816                 modal : true
2817             });
2818             return this;
2819         },
2820
2821         /**
2822          * Button config that displays a single OK button
2823          * @type Object
2824          */
2825         OK : {ok:true},
2826         /**
2827          * Button config that displays Yes and No buttons
2828          * @type Object
2829          */
2830         YESNO : {yes:true, no:true},
2831         /**
2832          * Button config that displays OK and Cancel buttons
2833          * @type Object
2834          */
2835         OKCANCEL : {ok:true, cancel:true},
2836         /**
2837          * Button config that displays Yes, No and Cancel buttons
2838          * @type Object
2839          */
2840         YESNOCANCEL : {yes:true, no:true, cancel:true},
2841
2842         /**
2843          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2844          * @type Number
2845          */
2846         defaultTextHeight : 75,
2847         /**
2848          * The maximum width in pixels of the message box (defaults to 600)
2849          * @type Number
2850          */
2851         maxWidth : 600,
2852         /**
2853          * The minimum width in pixels of the message box (defaults to 100)
2854          * @type Number
2855          */
2856         minWidth : 100,
2857         /**
2858          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2859          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2860          * @type Number
2861          */
2862         minProgressWidth : 250,
2863         /**
2864          * An object containing the default button text strings that can be overriden for localized language support.
2865          * Supported properties are: ok, cancel, yes and no.
2866          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2867          * @type Object
2868          */
2869         buttonText : {
2870             ok : "OK",
2871             cancel : "Cancel",
2872             yes : "Yes",
2873             no : "No"
2874         }
2875     };
2876 }();
2877
2878 /**
2879  * Shorthand for {@link Roo.MessageBox}
2880  */
2881 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2882 Roo.Msg = Roo.Msg || Roo.MessageBox;
2883 /*
2884  * - LGPL
2885  *
2886  * navbar
2887  * 
2888  */
2889
2890 /**
2891  * @class Roo.bootstrap.Navbar
2892  * @extends Roo.bootstrap.Component
2893  * Bootstrap Navbar class
2894
2895  * @constructor
2896  * Create a new Navbar
2897  * @param {Object} config The config object
2898  */
2899
2900
2901 Roo.bootstrap.Navbar = function(config){
2902     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2903     
2904 };
2905
2906 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2907     
2908     
2909    
2910     // private
2911     navItems : false,
2912     loadMask : false,
2913     
2914     
2915     getAutoCreate : function(){
2916         
2917         
2918         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2919         
2920     },
2921     
2922     initEvents :function ()
2923     {
2924         //Roo.log(this.el.select('.navbar-toggle',true));
2925         this.el.select('.navbar-toggle',true).on('click', function() {
2926            // Roo.log('click');
2927             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2928         }, this);
2929         
2930         var mark = {
2931             tag: "div",
2932             cls:"x-dlg-mask"
2933         }
2934         
2935         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2936         
2937         var size = this.el.getSize();
2938         this.maskEl.setSize(size.width, size.height);
2939         this.maskEl.enableDisplayMode("block");
2940         this.maskEl.hide();
2941         
2942         if(this.loadMask){
2943             this.maskEl.show();
2944         }
2945     },
2946     
2947     
2948     getChildContainer : function()
2949     {
2950         if (this.el.select('.collapse').getCount()) {
2951             return this.el.select('.collapse',true).first();
2952         }
2953         
2954         return this.el;
2955     },
2956     
2957     mask : function()
2958     {
2959         this.maskEl.show();
2960     },
2961     
2962     unmask : function()
2963     {
2964         this.maskEl.hide();
2965     } 
2966     
2967     
2968     
2969     
2970 });
2971
2972
2973
2974  
2975
2976  /*
2977  * - LGPL
2978  *
2979  * navbar
2980  * 
2981  */
2982
2983 /**
2984  * @class Roo.bootstrap.NavSimplebar
2985  * @extends Roo.bootstrap.Navbar
2986  * Bootstrap Sidebar class
2987  *
2988  * @cfg {Boolean} inverse is inverted color
2989  * 
2990  * @cfg {String} type (nav | pills | tabs)
2991  * @cfg {Boolean} arrangement stacked | justified
2992  * @cfg {String} align (left | right) alignment
2993  * 
2994  * @cfg {Boolean} main (true|false) main nav bar? default false
2995  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2996  * 
2997  * @cfg {String} tag (header|footer|nav|div) default is nav 
2998
2999  * 
3000  * 
3001  * 
3002  * @constructor
3003  * Create a new Sidebar
3004  * @param {Object} config The config object
3005  */
3006
3007
3008 Roo.bootstrap.NavSimplebar = function(config){
3009     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3010 };
3011
3012 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3013     
3014     inverse: false,
3015     
3016     type: false,
3017     arrangement: '',
3018     align : false,
3019     
3020     
3021     
3022     main : false,
3023     
3024     
3025     tag : false,
3026     
3027     
3028     getAutoCreate : function(){
3029         
3030         
3031         var cfg = {
3032             tag : this.tag || 'div',
3033             cls : 'navbar'
3034         };
3035           
3036         
3037         cfg.cn = [
3038             {
3039                 cls: 'nav',
3040                 tag : 'ul'
3041             }
3042         ];
3043         
3044          
3045         this.type = this.type || 'nav';
3046         if (['tabs','pills'].indexOf(this.type)!==-1) {
3047             cfg.cn[0].cls += ' nav-' + this.type
3048         
3049         
3050         } else {
3051             if (this.type!=='nav') {
3052                 Roo.log('nav type must be nav/tabs/pills')
3053             }
3054             cfg.cn[0].cls += ' navbar-nav'
3055         }
3056         
3057         
3058         
3059         
3060         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3061             cfg.cn[0].cls += ' nav-' + this.arrangement;
3062         }
3063         
3064         
3065         if (this.align === 'right') {
3066             cfg.cn[0].cls += ' navbar-right';
3067         }
3068         
3069         if (this.inverse) {
3070             cfg.cls += ' navbar-inverse';
3071             
3072         }
3073         
3074         
3075         return cfg;
3076     
3077         
3078     }
3079     
3080     
3081     
3082 });
3083
3084
3085
3086  
3087
3088  
3089        /*
3090  * - LGPL
3091  *
3092  * navbar
3093  * 
3094  */
3095
3096 /**
3097  * @class Roo.bootstrap.NavHeaderbar
3098  * @extends Roo.bootstrap.NavSimplebar
3099  * Bootstrap Sidebar class
3100  *
3101  * @cfg {String} brand what is brand
3102  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3103  * @cfg {String} brand_href href of the brand
3104  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3105  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3106  * 
3107  * @constructor
3108  * Create a new Sidebar
3109  * @param {Object} config The config object
3110  */
3111
3112
3113 Roo.bootstrap.NavHeaderbar = function(config){
3114     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3115 };
3116
3117 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3118     
3119     position: '',
3120     brand: '',
3121     brand_href: false,
3122     srButton : true,
3123     autohide : false,
3124     
3125     getAutoCreate : function(){
3126         
3127         var   cfg = {
3128             tag: this.nav || 'nav',
3129             cls: 'navbar',
3130             role: 'navigation',
3131             cn: []
3132         };
3133         
3134         if(this.srButton){
3135             cfg.cn.push({
3136                 tag: 'div',
3137                 cls: 'navbar-header',
3138                 cn: [
3139                     {
3140                         tag: 'button',
3141                         type: 'button',
3142                         cls: 'navbar-toggle',
3143                         'data-toggle': 'collapse',
3144                         cn: [
3145                             {
3146                                 tag: 'span',
3147                                 cls: 'sr-only',
3148                                 html: 'Toggle navigation'
3149                             },
3150                             {
3151                                 tag: 'span',
3152                                 cls: 'icon-bar'
3153                             },
3154                             {
3155                                 tag: 'span',
3156                                 cls: 'icon-bar'
3157                             },
3158                             {
3159                                 tag: 'span',
3160                                 cls: 'icon-bar'
3161                             }
3162                         ]
3163                     }
3164                 ]
3165             });
3166         }
3167         
3168         cfg.cn.push({
3169             tag: 'div',
3170             cls: 'collapse navbar-collapse',
3171             cn : []
3172         });
3173         
3174         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3175         
3176         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3177             cfg.cls += ' navbar-' + this.position;
3178             
3179             // tag can override this..
3180             
3181             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3182         }
3183         
3184         if (this.brand !== '') {
3185             cfg.cn[0].cn.push({
3186                 tag: 'a',
3187                 href: this.brand_href ? this.brand_href : '#',
3188                 cls: 'navbar-brand',
3189                 cn: [
3190                 this.brand
3191                 ]
3192             });
3193         }
3194         
3195         if(this.main){
3196             cfg.cls += ' main-nav';
3197         }
3198         
3199         
3200         return cfg;
3201
3202         
3203     },
3204     initEvents : function()
3205     {
3206         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3207         
3208         if (this.autohide) {
3209             
3210             var prevScroll = 0;
3211             var ft = this.el;
3212             
3213             Roo.get(document).on('scroll',function(e) {
3214                 var ns = Roo.get(document).getScroll().top;
3215                 var os = prevScroll;
3216                 prevScroll = ns;
3217                 
3218                 if(ns > os){
3219                     ft.removeClass('slideDown');
3220                     ft.addClass('slideUp');
3221                     return;
3222                 }
3223                 ft.removeClass('slideUp');
3224                 ft.addClass('slideDown');
3225                  
3226               
3227           },this);
3228         }
3229     }    
3230           
3231       
3232     
3233     
3234 });
3235
3236
3237
3238  
3239
3240  /*
3241  * - LGPL
3242  *
3243  * navbar
3244  * 
3245  */
3246
3247 /**
3248  * @class Roo.bootstrap.NavSidebar
3249  * @extends Roo.bootstrap.Navbar
3250  * Bootstrap Sidebar class
3251  * 
3252  * @constructor
3253  * Create a new Sidebar
3254  * @param {Object} config The config object
3255  */
3256
3257
3258 Roo.bootstrap.NavSidebar = function(config){
3259     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3260 };
3261
3262 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3263     
3264     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3265     
3266     getAutoCreate : function(){
3267         
3268         
3269         return  {
3270             tag: 'div',
3271             cls: 'sidebar sidebar-nav'
3272         };
3273     
3274         
3275     }
3276     
3277     
3278     
3279 });
3280
3281
3282
3283  
3284
3285  /*
3286  * - LGPL
3287  *
3288  * nav group
3289  * 
3290  */
3291
3292 /**
3293  * @class Roo.bootstrap.NavGroup
3294  * @extends Roo.bootstrap.Component
3295  * Bootstrap NavGroup class
3296  * @cfg {String} align left | right
3297  * @cfg {Boolean} inverse false | true
3298  * @cfg {String} type (nav|pills|tab) default nav
3299  * @cfg {String} navId - reference Id for navbar.
3300
3301  * 
3302  * @constructor
3303  * Create a new nav group
3304  * @param {Object} config The config object
3305  */
3306
3307 Roo.bootstrap.NavGroup = function(config){
3308     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3309     this.navItems = [];
3310    
3311     Roo.bootstrap.NavGroup.register(this);
3312      this.addEvents({
3313         /**
3314              * @event changed
3315              * Fires when the active item changes
3316              * @param {Roo.bootstrap.NavGroup} this
3317              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3318              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3319          */
3320         'changed': true
3321      });
3322     
3323 };
3324
3325 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3326     
3327     align: '',
3328     inverse: false,
3329     form: false,
3330     type: 'nav',
3331     navId : '',
3332     // private
3333     
3334     navItems : false, 
3335     
3336     getAutoCreate : function()
3337     {
3338         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3339         
3340         cfg = {
3341             tag : 'ul',
3342             cls: 'nav' 
3343         }
3344         
3345         if (['tabs','pills'].indexOf(this.type)!==-1) {
3346             cfg.cls += ' nav-' + this.type
3347         } else {
3348             if (this.type!=='nav') {
3349                 Roo.log('nav type must be nav/tabs/pills')
3350             }
3351             cfg.cls += ' navbar-nav'
3352         }
3353         
3354         if (this.parent().sidebar) {
3355             cfg = {
3356                 tag: 'ul',
3357                 cls: 'dashboard-menu sidebar-menu'
3358             }
3359             
3360             return cfg;
3361         }
3362         
3363         if (this.form === true) {
3364             cfg = {
3365                 tag: 'form',
3366                 cls: 'navbar-form'
3367             }
3368             
3369             if (this.align === 'right') {
3370                 cfg.cls += ' navbar-right';
3371             } else {
3372                 cfg.cls += ' navbar-left';
3373             }
3374         }
3375         
3376         if (this.align === 'right') {
3377             cfg.cls += ' navbar-right';
3378         }
3379         
3380         if (this.inverse) {
3381             cfg.cls += ' navbar-inverse';
3382             
3383         }
3384         
3385         
3386         return cfg;
3387     },
3388     /**
3389     * sets the active Navigation item
3390     * @param {Roo.bootstrap.NavItem} the new current navitem
3391     */
3392     setActiveItem : function(item)
3393     {
3394         var prev = false;
3395         Roo.each(this.navItems, function(v){
3396             if (v == item) {
3397                 return ;
3398             }
3399             if (v.isActive()) {
3400                 v.setActive(false, true);
3401                 prev = v;
3402                 
3403             }
3404             
3405         });
3406
3407         item.setActive(true, true);
3408         this.fireEvent('changed', this, item, prev);
3409         
3410         
3411     },
3412     /**
3413     * gets the active Navigation item
3414     * @return {Roo.bootstrap.NavItem} the current navitem
3415     */
3416     getActive : function()
3417     {
3418         
3419         var prev = false;
3420         Roo.each(this.navItems, function(v){
3421             
3422             if (v.isActive()) {
3423                 prev = v;
3424                 
3425             }
3426             
3427         });
3428         return prev;
3429     },
3430     
3431     indexOfNav : function()
3432     {
3433         
3434         var prev = false;
3435         Roo.each(this.navItems, function(v,i){
3436             
3437             if (v.isActive()) {
3438                 prev = i;
3439                 
3440             }
3441             
3442         });
3443         return prev;
3444     },
3445     /**
3446     * adds a Navigation item
3447     * @param {Roo.bootstrap.NavItem} the navitem to add
3448     */
3449     addItem : function(cfg)
3450     {
3451         var cn = new Roo.bootstrap.NavItem(cfg);
3452         this.register(cn);
3453         cn.parentId = this.id;
3454         cn.onRender(this.el, null);
3455         return cn;
3456     },
3457     /**
3458     * register a Navigation item
3459     * @param {Roo.bootstrap.NavItem} the navitem to add
3460     */
3461     register : function(item)
3462     {
3463         this.navItems.push( item);
3464         item.navId = this.navId;
3465     
3466     },
3467   
3468     
3469     getNavItem: function(tabId)
3470     {
3471         var ret = false;
3472         Roo.each(this.navItems, function(e) {
3473             if (e.tabId == tabId) {
3474                ret =  e;
3475                return false;
3476             }
3477             return true;
3478             
3479         });
3480         return ret;
3481     },
3482     
3483     setActiveNext : function()
3484     {
3485         var i = this.indexOfNav(this.getActive());
3486         if (i > this.navItems.length) {
3487             return;
3488         }
3489         this.setActiveItem(this.navItems[i+1]);
3490     },
3491     setActivePrev : function()
3492     {
3493         var i = this.indexOfNav(this.getActive());
3494         if (i  < 1) {
3495             return;
3496         }
3497         this.setActiveItem(this.navItems[i-1]);
3498     },
3499     clearWasActive : function(except) {
3500         Roo.each(this.navItems, function(e) {
3501             if (e.tabId != except.tabId && e.was_active) {
3502                e.was_active = false;
3503                return false;
3504             }
3505             return true;
3506             
3507         });
3508     },
3509     getWasActive : function ()
3510     {
3511         var r = false;
3512         Roo.each(this.navItems, function(e) {
3513             if (e.was_active) {
3514                r = e;
3515                return false;
3516             }
3517             return true;
3518             
3519         });
3520         return r;
3521     }
3522     
3523     
3524 });
3525
3526  
3527 Roo.apply(Roo.bootstrap.NavGroup, {
3528     
3529     groups: {},
3530      /**
3531     * register a Navigation Group
3532     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3533     */
3534     register : function(navgrp)
3535     {
3536         this.groups[navgrp.navId] = navgrp;
3537         
3538     },
3539     /**
3540     * fetch a Navigation Group based on the navigation ID
3541     * @param {string} the navgroup to add
3542     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3543     */
3544     get: function(navId) {
3545         if (typeof(this.groups[navId]) == 'undefined') {
3546             return false;
3547             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3548         }
3549         return this.groups[navId] ;
3550     }
3551     
3552     
3553     
3554 });
3555
3556  /*
3557  * - LGPL
3558  *
3559  * row
3560  * 
3561  */
3562
3563 /**
3564  * @class Roo.bootstrap.NavItem
3565  * @extends Roo.bootstrap.Component
3566  * Bootstrap Navbar.NavItem class
3567  * @cfg {String} href  link to
3568  * @cfg {String} html content of button
3569  * @cfg {String} badge text inside badge
3570  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3571  * @cfg {String} glyphicon name of glyphicon
3572  * @cfg {String} icon name of font awesome icon
3573  * @cfg {Boolean} active Is item active
3574  * @cfg {Boolean} disabled Is item disabled
3575  
3576  * @cfg {Boolean} preventDefault (true | false) default false
3577  * @cfg {String} tabId the tab that this item activates.
3578  * @cfg {String} tagtype (a|span) render as a href or span?
3579   
3580  * @constructor
3581  * Create a new Navbar Item
3582  * @param {Object} config The config object
3583  */
3584 Roo.bootstrap.NavItem = function(config){
3585     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3586     this.addEvents({
3587         // raw events
3588         /**
3589          * @event click
3590          * The raw click event for the entire grid.
3591          * @param {Roo.EventObject} e
3592          */
3593         "click" : true,
3594          /**
3595             * @event changed
3596             * Fires when the active item active state changes
3597             * @param {Roo.bootstrap.NavItem} this
3598             * @param {boolean} state the new state
3599              
3600          */
3601         'changed': true
3602     });
3603    
3604 };
3605
3606 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3607     
3608     href: false,
3609     html: '',
3610     badge: '',
3611     icon: false,
3612     glyphicon: false,
3613     active: false,
3614     preventDefault : false,
3615     tabId : false,
3616     tagtype : 'a',
3617     disabled : false,
3618     
3619     was_active : false,
3620     
3621     getAutoCreate : function(){
3622          
3623         var cfg = {
3624             tag: 'li',
3625             cls: 'nav-item'
3626             
3627         }
3628         if (this.active) {
3629             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3630         }
3631         if (this.disabled) {
3632             cfg.cls += ' disabled';
3633         }
3634         
3635         if (this.href || this.html || this.glyphicon || this.icon) {
3636             cfg.cn = [
3637                 {
3638                     tag: this.tagtype,
3639                     href : this.href || "#",
3640                     html: this.html || ''
3641                 }
3642             ];
3643             
3644             if (this.icon) {
3645                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3646             }
3647
3648             if(this.glyphicon) {
3649                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3650             }
3651             
3652             if (this.menu) {
3653                 
3654                 cfg.cn[0].html += " <span class='caret'></span>";
3655              
3656             }
3657             
3658             if (this.badge !== '') {
3659                  
3660                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3661             }
3662         }
3663         
3664         
3665         
3666         return cfg;
3667     },
3668     initEvents: function() {
3669        // Roo.log('init events?');
3670        // Roo.log(this.el.dom);
3671         if (typeof (this.menu) != 'undefined') {
3672             this.menu.parentType = this.xtype;
3673             this.menu.triggerEl = this.el;
3674             this.addxtype(Roo.apply({}, this.menu));
3675         }
3676
3677        
3678         this.el.select('a',true).on('click', this.onClick, this);
3679         // at this point parent should be available..
3680         this.parent().register(this);
3681     },
3682     
3683     onClick : function(e)
3684     {
3685          
3686         if(this.preventDefault){
3687             e.preventDefault();
3688         }
3689         if (this.disabled) {
3690             return;
3691         }
3692         
3693         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3694         if (tg && tg.transition) {
3695             Roo.log("waiting for the transitionend");
3696             return;
3697         }
3698         
3699         Roo.log("fire event clicked");
3700         if(this.fireEvent('click', this, e) === false){
3701             return;
3702         };
3703         var p = this.parent();
3704         if (['tabs','pills'].indexOf(p.type)!==-1) {
3705             if (typeof(p.setActiveItem) !== 'undefined') {
3706                 p.setActiveItem(this);
3707             }
3708         }
3709         // if parent is a navbarheader....
3710         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3711             // remove the collapsed menu expand...
3712             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3713         }
3714         
3715     },
3716     
3717     isActive: function () {
3718         return this.active
3719     },
3720     setActive : function(state, fire, is_was_active)
3721     {
3722         if (this.active && !state & this.navId) {
3723             this.was_active = true;
3724             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3725             if (nv) {
3726                 nv.clearWasActive(this);
3727             }
3728             
3729         }
3730         this.active = state;
3731         
3732         if (!state ) {
3733             this.el.removeClass('active');
3734         } else if (!this.el.hasClass('active')) {
3735             this.el.addClass('active');
3736         }
3737         if (fire) {
3738             this.fireEvent('changed', this, state);
3739         }
3740         
3741         // show a panel if it's registered and related..
3742         
3743         if (!this.navId || !this.tabId || !state || is_was_active) {
3744             return;
3745         }
3746         
3747         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3748         if (!tg) {
3749             return;
3750         }
3751         var pan = tg.getPanelByName(this.tabId);
3752         if (!pan) {
3753             return;
3754         }
3755         // if we can not flip to new panel - go back to old nav highlight..
3756         if (false == tg.showPanel(pan)) {
3757             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3758             if (nv) {
3759                 var onav = nv.getWasActive();
3760                 if (onav) {
3761                     onav.setActive(true, false, true);
3762                 }
3763             }
3764             
3765         }
3766         
3767         
3768         
3769     },
3770      // this should not be here...
3771     setDisabled : function(state)
3772     {
3773         this.disabled = state;
3774         if (!state ) {
3775             this.el.removeClass('disabled');
3776         } else if (!this.el.hasClass('disabled')) {
3777             this.el.addClass('disabled');
3778         }
3779         
3780     }
3781 });
3782  
3783
3784  /*
3785  * - LGPL
3786  *
3787  * sidebar item
3788  *
3789  *  li
3790  *    <span> icon </span>
3791  *    <span> text </span>
3792  *    <span>badge </span>
3793  */
3794
3795 /**
3796  * @class Roo.bootstrap.NavSidebarItem
3797  * @extends Roo.bootstrap.NavItem
3798  * Bootstrap Navbar.NavSidebarItem class
3799  * @constructor
3800  * Create a new Navbar Button
3801  * @param {Object} config The config object
3802  */
3803 Roo.bootstrap.NavSidebarItem = function(config){
3804     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3805     this.addEvents({
3806         // raw events
3807         /**
3808          * @event click
3809          * The raw click event for the entire grid.
3810          * @param {Roo.EventObject} e
3811          */
3812         "click" : true,
3813          /**
3814             * @event changed
3815             * Fires when the active item active state changes
3816             * @param {Roo.bootstrap.NavSidebarItem} this
3817             * @param {boolean} state the new state
3818              
3819          */
3820         'changed': true
3821     });
3822    
3823 };
3824
3825 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3826     
3827     
3828     getAutoCreate : function(){
3829         
3830         
3831         var a = {
3832                 tag: 'a',
3833                 href : this.href || '#',
3834                 cls: '',
3835                 html : '',
3836                 cn : []
3837         };
3838         var cfg = {
3839             tag: 'li',
3840             cls: '',
3841             cn: [ a ]
3842         }
3843         var span = {
3844             tag: 'span',
3845             html : this.html || ''
3846         }
3847         
3848         
3849         if (this.active) {
3850             cfg.cls += ' active';
3851         }
3852         
3853         // left icon..
3854         if (this.glyphicon || this.icon) {
3855             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3856             a.cn.push({ tag : 'i', cls : c }) ;
3857         }
3858         // html..
3859         a.cn.push(span);
3860         // then badge..
3861         if (this.badge !== '') {
3862             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3863         }
3864         // fi
3865         if (this.menu) {
3866             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3867             a.cls += 'dropdown-toggle treeview' ;
3868             
3869         }
3870         
3871         
3872         
3873         return cfg;
3874          
3875            
3876     }
3877    
3878      
3879  
3880 });
3881  
3882
3883  /*
3884  * - LGPL
3885  *
3886  * row
3887  * 
3888  */
3889
3890 /**
3891  * @class Roo.bootstrap.Row
3892  * @extends Roo.bootstrap.Component
3893  * Bootstrap Row class (contains columns...)
3894  * 
3895  * @constructor
3896  * Create a new Row
3897  * @param {Object} config The config object
3898  */
3899
3900 Roo.bootstrap.Row = function(config){
3901     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3902 };
3903
3904 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3905     
3906     getAutoCreate : function(){
3907        return {
3908             cls: 'row clearfix'
3909        };
3910     }
3911     
3912     
3913 });
3914
3915  
3916
3917  /*
3918  * - LGPL
3919  *
3920  * element
3921  * 
3922  */
3923
3924 /**
3925  * @class Roo.bootstrap.Element
3926  * @extends Roo.bootstrap.Component
3927  * Bootstrap Element class
3928  * @cfg {String} html contents of the element
3929  * @cfg {String} tag tag of the element
3930  * @cfg {String} cls class of the element
3931  * 
3932  * @constructor
3933  * Create a new Element
3934  * @param {Object} config The config object
3935  */
3936
3937 Roo.bootstrap.Element = function(config){
3938     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3939 };
3940
3941 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3942     
3943     tag: 'div',
3944     cls: '',
3945     html: '',
3946      
3947     
3948     getAutoCreate : function(){
3949         
3950         var cfg = {
3951             tag: this.tag,
3952             cls: this.cls,
3953             html: this.html
3954         }
3955         
3956         
3957         
3958         return cfg;
3959     }
3960    
3961 });
3962
3963  
3964
3965  /*
3966  * - LGPL
3967  *
3968  * pagination
3969  * 
3970  */
3971
3972 /**
3973  * @class Roo.bootstrap.Pagination
3974  * @extends Roo.bootstrap.Component
3975  * Bootstrap Pagination class
3976  * @cfg {String} size xs | sm | md | lg
3977  * @cfg {Boolean} inverse false | true
3978  * 
3979  * @constructor
3980  * Create a new Pagination
3981  * @param {Object} config The config object
3982  */
3983
3984 Roo.bootstrap.Pagination = function(config){
3985     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3986 };
3987
3988 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3989     
3990     cls: false,
3991     size: false,
3992     inverse: false,
3993     
3994     getAutoCreate : function(){
3995         var cfg = {
3996             tag: 'ul',
3997                 cls: 'pagination'
3998         };
3999         if (this.inverse) {
4000             cfg.cls += ' inverse';
4001         }
4002         if (this.html) {
4003             cfg.html=this.html;
4004         }
4005         if (this.cls) {
4006             cfg.cls += " " + this.cls;
4007         }
4008         return cfg;
4009     }
4010    
4011 });
4012
4013  
4014
4015  /*
4016  * - LGPL
4017  *
4018  * Pagination item
4019  * 
4020  */
4021
4022
4023 /**
4024  * @class Roo.bootstrap.PaginationItem
4025  * @extends Roo.bootstrap.Component
4026  * Bootstrap PaginationItem class
4027  * @cfg {String} html text
4028  * @cfg {String} href the link
4029  * @cfg {Boolean} preventDefault (true | false) default true
4030  * @cfg {Boolean} active (true | false) default false
4031  * 
4032  * 
4033  * @constructor
4034  * Create a new PaginationItem
4035  * @param {Object} config The config object
4036  */
4037
4038
4039 Roo.bootstrap.PaginationItem = function(config){
4040     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4041     this.addEvents({
4042         // raw events
4043         /**
4044          * @event click
4045          * The raw click event for the entire grid.
4046          * @param {Roo.EventObject} e
4047          */
4048         "click" : true
4049     });
4050 };
4051
4052 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4053     
4054     href : false,
4055     html : false,
4056     preventDefault: true,
4057     active : false,
4058     cls : false,
4059     
4060     getAutoCreate : function(){
4061         var cfg= {
4062             tag: 'li',
4063             cn: [
4064                 {
4065                     tag : 'a',
4066                     href : this.href ? this.href : '#',
4067                     html : this.html ? this.html : ''
4068                 }
4069             ]
4070         };
4071         
4072         if(this.cls){
4073             cfg.cls = this.cls;
4074         }
4075         
4076         if(this.active){
4077             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4078         }
4079         
4080         return cfg;
4081     },
4082     
4083     initEvents: function() {
4084         
4085         this.el.on('click', this.onClick, this);
4086         
4087     },
4088     onClick : function(e)
4089     {
4090         Roo.log('PaginationItem on click ');
4091         if(this.preventDefault){
4092             e.preventDefault();
4093         }
4094         
4095         this.fireEvent('click', this, e);
4096     }
4097    
4098 });
4099
4100  
4101
4102  /*
4103  * - LGPL
4104  *
4105  * slider
4106  * 
4107  */
4108
4109
4110 /**
4111  * @class Roo.bootstrap.Slider
4112  * @extends Roo.bootstrap.Component
4113  * Bootstrap Slider class
4114  *    
4115  * @constructor
4116  * Create a new Slider
4117  * @param {Object} config The config object
4118  */
4119
4120 Roo.bootstrap.Slider = function(config){
4121     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4122 };
4123
4124 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4125     
4126     getAutoCreate : function(){
4127         
4128         var cfg = {
4129             tag: 'div',
4130             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4131             cn: [
4132                 {
4133                     tag: 'a',
4134                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4135                 }
4136             ]
4137         }
4138         
4139         return cfg;
4140     }
4141    
4142 });
4143
4144  /*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154  
4155
4156 /**
4157  * @class Roo.grid.ColumnModel
4158  * @extends Roo.util.Observable
4159  * This is the default implementation of a ColumnModel used by the Grid. It defines
4160  * the columns in the grid.
4161  * <br>Usage:<br>
4162  <pre><code>
4163  var colModel = new Roo.grid.ColumnModel([
4164         {header: "Ticker", width: 60, sortable: true, locked: true},
4165         {header: "Company Name", width: 150, sortable: true},
4166         {header: "Market Cap.", width: 100, sortable: true},
4167         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4168         {header: "Employees", width: 100, sortable: true, resizable: false}
4169  ]);
4170  </code></pre>
4171  * <p>
4172  
4173  * The config options listed for this class are options which may appear in each
4174  * individual column definition.
4175  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4176  * @constructor
4177  * @param {Object} config An Array of column config objects. See this class's
4178  * config objects for details.
4179 */
4180 Roo.grid.ColumnModel = function(config){
4181         /**
4182      * The config passed into the constructor
4183      */
4184     this.config = config;
4185     this.lookup = {};
4186
4187     // if no id, create one
4188     // if the column does not have a dataIndex mapping,
4189     // map it to the order it is in the config
4190     for(var i = 0, len = config.length; i < len; i++){
4191         var c = config[i];
4192         if(typeof c.dataIndex == "undefined"){
4193             c.dataIndex = i;
4194         }
4195         if(typeof c.renderer == "string"){
4196             c.renderer = Roo.util.Format[c.renderer];
4197         }
4198         if(typeof c.id == "undefined"){
4199             c.id = Roo.id();
4200         }
4201         if(c.editor && c.editor.xtype){
4202             c.editor  = Roo.factory(c.editor, Roo.grid);
4203         }
4204         if(c.editor && c.editor.isFormField){
4205             c.editor = new Roo.grid.GridEditor(c.editor);
4206         }
4207         this.lookup[c.id] = c;
4208     }
4209
4210     /**
4211      * The width of columns which have no width specified (defaults to 100)
4212      * @type Number
4213      */
4214     this.defaultWidth = 100;
4215
4216     /**
4217      * Default sortable of columns which have no sortable specified (defaults to false)
4218      * @type Boolean
4219      */
4220     this.defaultSortable = false;
4221
4222     this.addEvents({
4223         /**
4224              * @event widthchange
4225              * Fires when the width of a column changes.
4226              * @param {ColumnModel} this
4227              * @param {Number} columnIndex The column index
4228              * @param {Number} newWidth The new width
4229              */
4230             "widthchange": true,
4231         /**
4232              * @event headerchange
4233              * Fires when the text of a header changes.
4234              * @param {ColumnModel} this
4235              * @param {Number} columnIndex The column index
4236              * @param {Number} newText The new header text
4237              */
4238             "headerchange": true,
4239         /**
4240              * @event hiddenchange
4241              * Fires when a column is hidden or "unhidden".
4242              * @param {ColumnModel} this
4243              * @param {Number} columnIndex The column index
4244              * @param {Boolean} hidden true if hidden, false otherwise
4245              */
4246             "hiddenchange": true,
4247             /**
4248          * @event columnmoved
4249          * Fires when a column is moved.
4250          * @param {ColumnModel} this
4251          * @param {Number} oldIndex
4252          * @param {Number} newIndex
4253          */
4254         "columnmoved" : true,
4255         /**
4256          * @event columlockchange
4257          * Fires when a column's locked state is changed
4258          * @param {ColumnModel} this
4259          * @param {Number} colIndex
4260          * @param {Boolean} locked true if locked
4261          */
4262         "columnlockchange" : true
4263     });
4264     Roo.grid.ColumnModel.superclass.constructor.call(this);
4265 };
4266 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4267     /**
4268      * @cfg {String} header The header text to display in the Grid view.
4269      */
4270     /**
4271      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4272      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4273      * specified, the column's index is used as an index into the Record's data Array.
4274      */
4275     /**
4276      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4277      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4278      */
4279     /**
4280      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4281      * Defaults to the value of the {@link #defaultSortable} property.
4282      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4283      */
4284     /**
4285      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4286      */
4287     /**
4288      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4289      */
4290     /**
4291      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4292      */
4293     /**
4294      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4295      */
4296     /**
4297      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4298      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4299      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4300      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4301      */
4302        /**
4303      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4304      */
4305     /**
4306      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4307      */
4308
4309     /**
4310      * Returns the id of the column at the specified index.
4311      * @param {Number} index The column index
4312      * @return {String} the id
4313      */
4314     getColumnId : function(index){
4315         return this.config[index].id;
4316     },
4317
4318     /**
4319      * Returns the column for a specified id.
4320      * @param {String} id The column id
4321      * @return {Object} the column
4322      */
4323     getColumnById : function(id){
4324         return this.lookup[id];
4325     },
4326
4327     
4328     /**
4329      * Returns the column for a specified dataIndex.
4330      * @param {String} dataIndex The column dataIndex
4331      * @return {Object|Boolean} the column or false if not found
4332      */
4333     getColumnByDataIndex: function(dataIndex){
4334         var index = this.findColumnIndex(dataIndex);
4335         return index > -1 ? this.config[index] : false;
4336     },
4337     
4338     /**
4339      * Returns the index for a specified column id.
4340      * @param {String} id The column id
4341      * @return {Number} the index, or -1 if not found
4342      */
4343     getIndexById : function(id){
4344         for(var i = 0, len = this.config.length; i < len; i++){
4345             if(this.config[i].id == id){
4346                 return i;
4347             }
4348         }
4349         return -1;
4350     },
4351     
4352     /**
4353      * Returns the index for a specified column dataIndex.
4354      * @param {String} dataIndex The column dataIndex
4355      * @return {Number} the index, or -1 if not found
4356      */
4357     
4358     findColumnIndex : function(dataIndex){
4359         for(var i = 0, len = this.config.length; i < len; i++){
4360             if(this.config[i].dataIndex == dataIndex){
4361                 return i;
4362             }
4363         }
4364         return -1;
4365     },
4366     
4367     
4368     moveColumn : function(oldIndex, newIndex){
4369         var c = this.config[oldIndex];
4370         this.config.splice(oldIndex, 1);
4371         this.config.splice(newIndex, 0, c);
4372         this.dataMap = null;
4373         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4374     },
4375
4376     isLocked : function(colIndex){
4377         return this.config[colIndex].locked === true;
4378     },
4379
4380     setLocked : function(colIndex, value, suppressEvent){
4381         if(this.isLocked(colIndex) == value){
4382             return;
4383         }
4384         this.config[colIndex].locked = value;
4385         if(!suppressEvent){
4386             this.fireEvent("columnlockchange", this, colIndex, value);
4387         }
4388     },
4389
4390     getTotalLockedWidth : function(){
4391         var totalWidth = 0;
4392         for(var i = 0; i < this.config.length; i++){
4393             if(this.isLocked(i) && !this.isHidden(i)){
4394                 this.totalWidth += this.getColumnWidth(i);
4395             }
4396         }
4397         return totalWidth;
4398     },
4399
4400     getLockedCount : function(){
4401         for(var i = 0, len = this.config.length; i < len; i++){
4402             if(!this.isLocked(i)){
4403                 return i;
4404             }
4405         }
4406     },
4407
4408     /**
4409      * Returns the number of columns.
4410      * @return {Number}
4411      */
4412     getColumnCount : function(visibleOnly){
4413         if(visibleOnly === true){
4414             var c = 0;
4415             for(var i = 0, len = this.config.length; i < len; i++){
4416                 if(!this.isHidden(i)){
4417                     c++;
4418                 }
4419             }
4420             return c;
4421         }
4422         return this.config.length;
4423     },
4424
4425     /**
4426      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4427      * @param {Function} fn
4428      * @param {Object} scope (optional)
4429      * @return {Array} result
4430      */
4431     getColumnsBy : function(fn, scope){
4432         var r = [];
4433         for(var i = 0, len = this.config.length; i < len; i++){
4434             var c = this.config[i];
4435             if(fn.call(scope||this, c, i) === true){
4436                 r[r.length] = c;
4437             }
4438         }
4439         return r;
4440     },
4441
4442     /**
4443      * Returns true if the specified column is sortable.
4444      * @param {Number} col The column index
4445      * @return {Boolean}
4446      */
4447     isSortable : function(col){
4448         if(typeof this.config[col].sortable == "undefined"){
4449             return this.defaultSortable;
4450         }
4451         return this.config[col].sortable;
4452     },
4453
4454     /**
4455      * Returns the rendering (formatting) function defined for the column.
4456      * @param {Number} col The column index.
4457      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4458      */
4459     getRenderer : function(col){
4460         if(!this.config[col].renderer){
4461             return Roo.grid.ColumnModel.defaultRenderer;
4462         }
4463         return this.config[col].renderer;
4464     },
4465
4466     /**
4467      * Sets the rendering (formatting) function for a column.
4468      * @param {Number} col The column index
4469      * @param {Function} fn The function to use to process the cell's raw data
4470      * to return HTML markup for the grid view. The render function is called with
4471      * the following parameters:<ul>
4472      * <li>Data value.</li>
4473      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4474      * <li>css A CSS style string to apply to the table cell.</li>
4475      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4476      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4477      * <li>Row index</li>
4478      * <li>Column index</li>
4479      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4480      */
4481     setRenderer : function(col, fn){
4482         this.config[col].renderer = fn;
4483     },
4484
4485     /**
4486      * Returns the width for the specified column.
4487      * @param {Number} col The column index
4488      * @return {Number}
4489      */
4490     getColumnWidth : function(col){
4491         return this.config[col].width * 1 || this.defaultWidth;
4492     },
4493
4494     /**
4495      * Sets the width for a column.
4496      * @param {Number} col The column index
4497      * @param {Number} width The new width
4498      */
4499     setColumnWidth : function(col, width, suppressEvent){
4500         this.config[col].width = width;
4501         this.totalWidth = null;
4502         if(!suppressEvent){
4503              this.fireEvent("widthchange", this, col, width);
4504         }
4505     },
4506
4507     /**
4508      * Returns the total width of all columns.
4509      * @param {Boolean} includeHidden True to include hidden column widths
4510      * @return {Number}
4511      */
4512     getTotalWidth : function(includeHidden){
4513         if(!this.totalWidth){
4514             this.totalWidth = 0;
4515             for(var i = 0, len = this.config.length; i < len; i++){
4516                 if(includeHidden || !this.isHidden(i)){
4517                     this.totalWidth += this.getColumnWidth(i);
4518                 }
4519             }
4520         }
4521         return this.totalWidth;
4522     },
4523
4524     /**
4525      * Returns the header for the specified column.
4526      * @param {Number} col The column index
4527      * @return {String}
4528      */
4529     getColumnHeader : function(col){
4530         return this.config[col].header;
4531     },
4532
4533     /**
4534      * Sets the header for a column.
4535      * @param {Number} col The column index
4536      * @param {String} header The new header
4537      */
4538     setColumnHeader : function(col, header){
4539         this.config[col].header = header;
4540         this.fireEvent("headerchange", this, col, header);
4541     },
4542
4543     /**
4544      * Returns the tooltip for the specified column.
4545      * @param {Number} col The column index
4546      * @return {String}
4547      */
4548     getColumnTooltip : function(col){
4549             return this.config[col].tooltip;
4550     },
4551     /**
4552      * Sets the tooltip for a column.
4553      * @param {Number} col The column index
4554      * @param {String} tooltip The new tooltip
4555      */
4556     setColumnTooltip : function(col, tooltip){
4557             this.config[col].tooltip = tooltip;
4558     },
4559
4560     /**
4561      * Returns the dataIndex for the specified column.
4562      * @param {Number} col The column index
4563      * @return {Number}
4564      */
4565     getDataIndex : function(col){
4566         return this.config[col].dataIndex;
4567     },
4568
4569     /**
4570      * Sets the dataIndex for a column.
4571      * @param {Number} col The column index
4572      * @param {Number} dataIndex The new dataIndex
4573      */
4574     setDataIndex : function(col, dataIndex){
4575         this.config[col].dataIndex = dataIndex;
4576     },
4577
4578     
4579     
4580     /**
4581      * Returns true if the cell is editable.
4582      * @param {Number} colIndex The column index
4583      * @param {Number} rowIndex The row index
4584      * @return {Boolean}
4585      */
4586     isCellEditable : function(colIndex, rowIndex){
4587         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4588     },
4589
4590     /**
4591      * Returns the editor defined for the cell/column.
4592      * return false or null to disable editing.
4593      * @param {Number} colIndex The column index
4594      * @param {Number} rowIndex The row index
4595      * @return {Object}
4596      */
4597     getCellEditor : function(colIndex, rowIndex){
4598         return this.config[colIndex].editor;
4599     },
4600
4601     /**
4602      * Sets if a column is editable.
4603      * @param {Number} col The column index
4604      * @param {Boolean} editable True if the column is editable
4605      */
4606     setEditable : function(col, editable){
4607         this.config[col].editable = editable;
4608     },
4609
4610
4611     /**
4612      * Returns true if the column is hidden.
4613      * @param {Number} colIndex The column index
4614      * @return {Boolean}
4615      */
4616     isHidden : function(colIndex){
4617         return this.config[colIndex].hidden;
4618     },
4619
4620
4621     /**
4622      * Returns true if the column width cannot be changed
4623      */
4624     isFixed : function(colIndex){
4625         return this.config[colIndex].fixed;
4626     },
4627
4628     /**
4629      * Returns true if the column can be resized
4630      * @return {Boolean}
4631      */
4632     isResizable : function(colIndex){
4633         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4634     },
4635     /**
4636      * Sets if a column is hidden.
4637      * @param {Number} colIndex The column index
4638      * @param {Boolean} hidden True if the column is hidden
4639      */
4640     setHidden : function(colIndex, hidden){
4641         this.config[colIndex].hidden = hidden;
4642         this.totalWidth = null;
4643         this.fireEvent("hiddenchange", this, colIndex, hidden);
4644     },
4645
4646     /**
4647      * Sets the editor for a column.
4648      * @param {Number} col The column index
4649      * @param {Object} editor The editor object
4650      */
4651     setEditor : function(col, editor){
4652         this.config[col].editor = editor;
4653     }
4654 });
4655
4656 Roo.grid.ColumnModel.defaultRenderer = function(value){
4657         if(typeof value == "string" && value.length < 1){
4658             return "&#160;";
4659         }
4660         return value;
4661 };
4662
4663 // Alias for backwards compatibility
4664 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4665 /*
4666  * Based on:
4667  * Ext JS Library 1.1.1
4668  * Copyright(c) 2006-2007, Ext JS, LLC.
4669  *
4670  * Originally Released Under LGPL - original licence link has changed is not relivant.
4671  *
4672  * Fork - LGPL
4673  * <script type="text/javascript">
4674  */
4675  
4676 /**
4677  * @class Roo.LoadMask
4678  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4679  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4680  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4681  * element's UpdateManager load indicator and will be destroyed after the initial load.
4682  * @constructor
4683  * Create a new LoadMask
4684  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4685  * @param {Object} config The config object
4686  */
4687 Roo.LoadMask = function(el, config){
4688     this.el = Roo.get(el);
4689     Roo.apply(this, config);
4690     if(this.store){
4691         this.store.on('beforeload', this.onBeforeLoad, this);
4692         this.store.on('load', this.onLoad, this);
4693         this.store.on('loadexception', this.onLoadException, this);
4694         this.removeMask = false;
4695     }else{
4696         var um = this.el.getUpdateManager();
4697         um.showLoadIndicator = false; // disable the default indicator
4698         um.on('beforeupdate', this.onBeforeLoad, this);
4699         um.on('update', this.onLoad, this);
4700         um.on('failure', this.onLoad, this);
4701         this.removeMask = true;
4702     }
4703 };
4704
4705 Roo.LoadMask.prototype = {
4706     /**
4707      * @cfg {Boolean} removeMask
4708      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4709      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4710      */
4711     /**
4712      * @cfg {String} msg
4713      * The text to display in a centered loading message box (defaults to 'Loading...')
4714      */
4715     msg : 'Loading...',
4716     /**
4717      * @cfg {String} msgCls
4718      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4719      */
4720     msgCls : 'x-mask-loading',
4721
4722     /**
4723      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4724      * @type Boolean
4725      */
4726     disabled: false,
4727
4728     /**
4729      * Disables the mask to prevent it from being displayed
4730      */
4731     disable : function(){
4732        this.disabled = true;
4733     },
4734
4735     /**
4736      * Enables the mask so that it can be displayed
4737      */
4738     enable : function(){
4739         this.disabled = false;
4740     },
4741     
4742     onLoadException : function()
4743     {
4744         Roo.log(arguments);
4745         
4746         if (typeof(arguments[3]) != 'undefined') {
4747             Roo.MessageBox.alert("Error loading",arguments[3]);
4748         } 
4749         /*
4750         try {
4751             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4752                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4753             }   
4754         } catch(e) {
4755             
4756         }
4757         */
4758     
4759         
4760         
4761         this.el.unmask(this.removeMask);
4762     },
4763     // private
4764     onLoad : function()
4765     {
4766         this.el.unmask(this.removeMask);
4767     },
4768
4769     // private
4770     onBeforeLoad : function(){
4771         if(!this.disabled){
4772             this.el.mask(this.msg, this.msgCls);
4773         }
4774     },
4775
4776     // private
4777     destroy : function(){
4778         if(this.store){
4779             this.store.un('beforeload', this.onBeforeLoad, this);
4780             this.store.un('load', this.onLoad, this);
4781             this.store.un('loadexception', this.onLoadException, this);
4782         }else{
4783             var um = this.el.getUpdateManager();
4784             um.un('beforeupdate', this.onBeforeLoad, this);
4785             um.un('update', this.onLoad, this);
4786             um.un('failure', this.onLoad, this);
4787         }
4788     }
4789 };/*
4790  * - LGPL
4791  *
4792  * table
4793  * 
4794  */
4795
4796 /**
4797  * @class Roo.bootstrap.Table
4798  * @extends Roo.bootstrap.Component
4799  * Bootstrap Table class
4800  * @cfg {String} cls table class
4801  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4802  * @cfg {String} bgcolor Specifies the background color for a table
4803  * @cfg {Number} border Specifies whether the table cells should have borders or not
4804  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4805  * @cfg {Number} cellspacing Specifies the space between cells
4806  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4807  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4808  * @cfg {String} sortable Specifies that the table should be sortable
4809  * @cfg {String} summary Specifies a summary of the content of a table
4810  * @cfg {Number} width Specifies the width of a table
4811  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4812  * 
4813  * @cfg {boolean} striped Should the rows be alternative striped
4814  * @cfg {boolean} bordered Add borders to the table
4815  * @cfg {boolean} hover Add hover highlighting
4816  * @cfg {boolean} condensed Format condensed
4817  * @cfg {boolean} responsive Format condensed
4818  * @cfg {Boolean} loadMask (true|false) default false
4819  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4820  * @cfg {Boolean} thead (true|false) generate thead, default true
4821  * @cfg {Boolean} RowSelection (true|false) default false
4822  * @cfg {Boolean} CellSelection (true|false) default false
4823  *
4824  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4825  
4826  * 
4827  * @constructor
4828  * Create a new Table
4829  * @param {Object} config The config object
4830  */
4831
4832 Roo.bootstrap.Table = function(config){
4833     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4834     
4835     if (this.sm) {
4836         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4837         this.sm = this.selModel;
4838         this.sm.xmodule = this.xmodule || false;
4839     }
4840     if (this.cm && typeof(this.cm.config) == 'undefined') {
4841         this.colModel = new Roo.grid.ColumnModel(this.cm);
4842         this.cm = this.colModel;
4843         this.cm.xmodule = this.xmodule || false;
4844     }
4845     if (this.store) {
4846         this.store= Roo.factory(this.store, Roo.data);
4847         this.ds = this.store;
4848         this.ds.xmodule = this.xmodule || false;
4849          
4850     }
4851     if (this.footer && this.store) {
4852         this.footer.dataSource = this.ds;
4853         this.footer = Roo.factory(this.footer);
4854     }
4855     
4856     /** @private */
4857     this.addEvents({
4858         /**
4859          * @event cellclick
4860          * Fires when a cell is clicked
4861          * @param {Roo.bootstrap.Table} this
4862          * @param {Roo.Element} el
4863          * @param {Number} rowIndex
4864          * @param {Number} columnIndex
4865          * @param {Roo.EventObject} e
4866          */
4867         "cellclick" : true,
4868         /**
4869          * @event celldblclick
4870          * Fires when a cell is double clicked
4871          * @param {Roo.bootstrap.Table} this
4872          * @param {Roo.Element} el
4873          * @param {Number} rowIndex
4874          * @param {Number} columnIndex
4875          * @param {Roo.EventObject} e
4876          */
4877         "celldblclick" : true,
4878         /**
4879          * @event rowclick
4880          * Fires when a row is clicked
4881          * @param {Roo.bootstrap.Table} this
4882          * @param {Roo.Element} el
4883          * @param {Number} rowIndex
4884          * @param {Roo.EventObject} e
4885          */
4886         "rowclick" : true,
4887         /**
4888          * @event rowdblclick
4889          * Fires when a row is double clicked
4890          * @param {Roo.bootstrap.Table} this
4891          * @param {Roo.Element} el
4892          * @param {Number} rowIndex
4893          * @param {Roo.EventObject} e
4894          */
4895         "rowdblclick" : true,
4896         /**
4897          * @event mouseover
4898          * Fires when a mouseover occur
4899          * @param {Roo.bootstrap.Table} this
4900          * @param {Roo.Element} el
4901          * @param {Number} rowIndex
4902          * @param {Number} columnIndex
4903          * @param {Roo.EventObject} e
4904          */
4905         "mouseover" : true,
4906         /**
4907          * @event mouseout
4908          * Fires when a mouseout occur
4909          * @param {Roo.bootstrap.Table} this
4910          * @param {Roo.Element} el
4911          * @param {Number} rowIndex
4912          * @param {Number} columnIndex
4913          * @param {Roo.EventObject} e
4914          */
4915         "mouseout" : true,
4916         /**
4917          * @event rowclass
4918          * Fires when a row is rendered, so you can change add a style to it.
4919          * @param {Roo.bootstrap.Table} this
4920          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4921          */
4922         'rowclass' : true
4923         
4924     });
4925 };
4926
4927 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4928     
4929     cls: false,
4930     align: false,
4931     bgcolor: false,
4932     border: false,
4933     cellpadding: false,
4934     cellspacing: false,
4935     frame: false,
4936     rules: false,
4937     sortable: false,
4938     summary: false,
4939     width: false,
4940     striped : false,
4941     bordered: false,
4942     hover:  false,
4943     condensed : false,
4944     responsive : false,
4945     sm : false,
4946     cm : false,
4947     store : false,
4948     loadMask : false,
4949     tfoot : true,
4950     thead : true,
4951     RowSelection : false,
4952     CellSelection : false,
4953     layout : false,
4954     
4955     // Roo.Element - the tbody
4956     mainBody: false, 
4957     
4958     getAutoCreate : function(){
4959         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4960         
4961         cfg = {
4962             tag: 'table',
4963             cls : 'table',
4964             cn : []
4965         }
4966             
4967         if (this.striped) {
4968             cfg.cls += ' table-striped';
4969         }
4970         
4971         if (this.hover) {
4972             cfg.cls += ' table-hover';
4973         }
4974         if (this.bordered) {
4975             cfg.cls += ' table-bordered';
4976         }
4977         if (this.condensed) {
4978             cfg.cls += ' table-condensed';
4979         }
4980         if (this.responsive) {
4981             cfg.cls += ' table-responsive';
4982         }
4983         
4984         if (this.cls) {
4985             cfg.cls+=  ' ' +this.cls;
4986         }
4987         
4988         // this lot should be simplifed...
4989         
4990         if (this.align) {
4991             cfg.align=this.align;
4992         }
4993         if (this.bgcolor) {
4994             cfg.bgcolor=this.bgcolor;
4995         }
4996         if (this.border) {
4997             cfg.border=this.border;
4998         }
4999         if (this.cellpadding) {
5000             cfg.cellpadding=this.cellpadding;
5001         }
5002         if (this.cellspacing) {
5003             cfg.cellspacing=this.cellspacing;
5004         }
5005         if (this.frame) {
5006             cfg.frame=this.frame;
5007         }
5008         if (this.rules) {
5009             cfg.rules=this.rules;
5010         }
5011         if (this.sortable) {
5012             cfg.sortable=this.sortable;
5013         }
5014         if (this.summary) {
5015             cfg.summary=this.summary;
5016         }
5017         if (this.width) {
5018             cfg.width=this.width;
5019         }
5020         if (this.layout) {
5021             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5022         }
5023         
5024         if(this.store || this.cm){
5025             if(this.thead){
5026                 cfg.cn.push(this.renderHeader());
5027             }
5028             
5029             cfg.cn.push(this.renderBody());
5030             
5031             if(this.tfoot){
5032                 cfg.cn.push(this.renderFooter());
5033             }
5034             
5035             cfg.cls+=  ' TableGrid';
5036         }
5037         
5038         return { cn : [ cfg ] };
5039     },
5040     
5041     initEvents : function()
5042     {   
5043         if(!this.store || !this.cm){
5044             return;
5045         }
5046         
5047         //Roo.log('initEvents with ds!!!!');
5048         
5049         this.mainBody = this.el.select('tbody', true).first();
5050         
5051         
5052         var _this = this;
5053         
5054         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5055             e.on('click', _this.sort, _this);
5056         });
5057         
5058         this.el.on("click", this.onClick, this);
5059         this.el.on("dblclick", this.onDblClick, this);
5060         
5061         this.parent().el.setStyle('position', 'relative');
5062         if (this.footer) {
5063             this.footer.parentId = this.id;
5064             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5065         }
5066         
5067         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5068         
5069         this.store.on('load', this.onLoad, this);
5070         this.store.on('beforeload', this.onBeforeLoad, this);
5071         this.store.on('update', this.onUpdate, this);
5072         
5073     },
5074     
5075     onMouseover : function(e, el)
5076     {
5077         var cell = Roo.get(el);
5078         
5079         if(!cell){
5080             return;
5081         }
5082         
5083         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5084             cell = cell.findParent('td', false, true);
5085         }
5086         
5087         var row = cell.findParent('tr', false, true);
5088         var cellIndex = cell.dom.cellIndex;
5089         var rowIndex = row.dom.rowIndex - 1; // start from 0
5090         
5091         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5092         
5093     },
5094     
5095     onMouseout : function(e, el)
5096     {
5097         var cell = Roo.get(el);
5098         
5099         if(!cell){
5100             return;
5101         }
5102         
5103         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5104             cell = cell.findParent('td', false, true);
5105         }
5106         
5107         var row = cell.findParent('tr', false, true);
5108         var cellIndex = cell.dom.cellIndex;
5109         var rowIndex = row.dom.rowIndex - 1; // start from 0
5110         
5111         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5112         
5113     },
5114     
5115     onClick : function(e, el)
5116     {
5117         var cell = Roo.get(el);
5118         
5119         if(!cell || (!this.CellSelection && !this.RowSelection)){
5120             return;
5121         }
5122         
5123         
5124         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5125             cell = cell.findParent('td', false, true);
5126         }
5127         
5128         var row = cell.findParent('tr', false, true);
5129         var cellIndex = cell.dom.cellIndex;
5130         var rowIndex = row.dom.rowIndex - 1;
5131         
5132         if(this.CellSelection){
5133             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5134         }
5135         
5136         if(this.RowSelection){
5137             this.fireEvent('rowclick', this, row, rowIndex, e);
5138         }
5139         
5140         
5141     },
5142     
5143     onDblClick : function(e,el)
5144     {
5145         var cell = Roo.get(el);
5146         
5147         if(!cell || (!this.CellSelection && !this.RowSelection)){
5148             return;
5149         }
5150         
5151         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5152             cell = cell.findParent('td', false, true);
5153         }
5154         
5155         var row = cell.findParent('tr', false, true);
5156         var cellIndex = cell.dom.cellIndex;
5157         var rowIndex = row.dom.rowIndex - 1;
5158         
5159         if(this.CellSelection){
5160             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5161         }
5162         
5163         if(this.RowSelection){
5164             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5165         }
5166     },
5167     
5168     sort : function(e,el)
5169     {
5170         var col = Roo.get(el)
5171         
5172         if(!col.hasClass('sortable')){
5173             return;
5174         }
5175         
5176         var sort = col.attr('sort');
5177         var dir = 'ASC';
5178         
5179         if(col.hasClass('glyphicon-arrow-up')){
5180             dir = 'DESC';
5181         }
5182         
5183         this.store.sortInfo = {field : sort, direction : dir};
5184         
5185         if (this.footer) {
5186             Roo.log("calling footer first");
5187             this.footer.onClick('first');
5188         } else {
5189         
5190             this.store.load({ params : { start : 0 } });
5191         }
5192     },
5193     
5194     renderHeader : function()
5195     {
5196         var header = {
5197             tag: 'thead',
5198             cn : []
5199         };
5200         
5201         var cm = this.cm;
5202         
5203         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5204             
5205             var config = cm.config[i];
5206                     
5207             var c = {
5208                 tag: 'th',
5209                 style : '',
5210                 html: cm.getColumnHeader(i)
5211             };
5212             
5213             if(typeof(config.hidden) != 'undefined' && config.hidden){
5214                 c.style += ' display:none;';
5215             }
5216             
5217             if(typeof(config.dataIndex) != 'undefined'){
5218                 c.sort = config.dataIndex;
5219             }
5220             
5221             if(typeof(config.sortable) != 'undefined' && config.sortable){
5222                 c.cls = 'sortable';
5223             }
5224             
5225             if(typeof(config.align) != 'undefined' && config.align.length){
5226                 c.style += ' text-align:' + config.align + ';';
5227             }
5228             
5229             if(typeof(config.width) != 'undefined'){
5230                 c.style += ' width:' + config.width + 'px;';
5231             }
5232             
5233             header.cn.push(c)
5234         }
5235         
5236         return header;
5237     },
5238     
5239     renderBody : function()
5240     {
5241         var body = {
5242             tag: 'tbody',
5243             cn : [
5244                 {
5245                     tag: 'tr',
5246                     cn : [
5247                         {
5248                             tag : 'td',
5249                             colspan :  this.cm.getColumnCount()
5250                         }
5251                     ]
5252                 }
5253             ]
5254         };
5255         
5256         return body;
5257     },
5258     
5259     renderFooter : function()
5260     {
5261         var footer = {
5262             tag: 'tfoot',
5263             cn : [
5264                 {
5265                     tag: 'tr',
5266                     cn : [
5267                         {
5268                             tag : 'td',
5269                             colspan :  this.cm.getColumnCount()
5270                         }
5271                     ]
5272                 }
5273             ]
5274         };
5275         
5276         return footer;
5277     },
5278     
5279     
5280     
5281     onLoad : function()
5282     {
5283         Roo.log('ds onload');
5284         this.clear();
5285         
5286         var _this = this;
5287         var cm = this.cm;
5288         var ds = this.store;
5289         
5290         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5291             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5292             
5293             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5294                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5295             }
5296             
5297             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5298                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5299             }
5300         });
5301         
5302         var tbody =  this.mainBody;
5303               
5304         if(ds.getCount() > 0){
5305             ds.data.each(function(d,rowIndex){
5306                 var row =  this.renderRow(cm, ds, rowIndex);
5307                 
5308                 tbody.createChild(row);
5309                 
5310                 var _this = this;
5311                 
5312                 if(row.cellObjects.length){
5313                     Roo.each(row.cellObjects, function(r){
5314                         _this.renderCellObject(r);
5315                     })
5316                 }
5317                 
5318             }, this);
5319         }
5320         
5321         Roo.each(this.el.select('tbody td', true).elements, function(e){
5322             e.on('mouseover', _this.onMouseover, _this);
5323         });
5324         
5325         Roo.each(this.el.select('tbody td', true).elements, function(e){
5326             e.on('mouseout', _this.onMouseout, _this);
5327         });
5328
5329         //if(this.loadMask){
5330         //    this.maskEl.hide();
5331         //}
5332     },
5333     
5334     
5335     onUpdate : function(ds,record)
5336     {
5337         this.refreshRow(record);
5338     },
5339     onRemove : function(ds, record, index, isUpdate){
5340         if(isUpdate !== true){
5341             this.fireEvent("beforerowremoved", this, index, record);
5342         }
5343         var bt = this.mainBody.dom;
5344         if(bt.rows[index]){
5345             bt.removeChild(bt.rows[index]);
5346         }
5347         
5348         if(isUpdate !== true){
5349             //this.stripeRows(index);
5350             //this.syncRowHeights(index, index);
5351             //this.layout();
5352             this.fireEvent("rowremoved", this, index, record);
5353         }
5354     },
5355     
5356     
5357     refreshRow : function(record){
5358         var ds = this.store, index;
5359         if(typeof record == 'number'){
5360             index = record;
5361             record = ds.getAt(index);
5362         }else{
5363             index = ds.indexOf(record);
5364         }
5365         this.insertRow(ds, index, true);
5366         this.onRemove(ds, record, index+1, true);
5367         //this.syncRowHeights(index, index);
5368         //this.layout();
5369         this.fireEvent("rowupdated", this, index, record);
5370     },
5371     
5372     insertRow : function(dm, rowIndex, isUpdate){
5373         
5374         if(!isUpdate){
5375             this.fireEvent("beforerowsinserted", this, rowIndex);
5376         }
5377             //var s = this.getScrollState();
5378         var row = this.renderRow(this.cm, this.store, rowIndex);
5379         // insert before rowIndex..
5380         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5381         
5382         var _this = this;
5383                 
5384         if(row.cellObjects.length){
5385             Roo.each(row.cellObjects, function(r){
5386                 _this.renderCellObject(r);
5387             })
5388         }
5389             
5390         if(!isUpdate){
5391             this.fireEvent("rowsinserted", this, rowIndex);
5392             //this.syncRowHeights(firstRow, lastRow);
5393             //this.stripeRows(firstRow);
5394             //this.layout();
5395         }
5396         
5397     },
5398     
5399     
5400     getRowDom : function(rowIndex)
5401     {
5402         // not sure if I need to check this.. but let's do it anyway..
5403         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5404                 this.mainBody.dom.rows[rowIndex] : false
5405     },
5406     // returns the object tree for a tr..
5407   
5408     
5409     renderRow : function(cm, ds, rowIndex) {
5410         
5411         var d = ds.getAt(rowIndex);
5412         
5413         var row = {
5414             tag : 'tr',
5415             cn : []
5416         };
5417             
5418         var cellObjects = [];
5419         
5420         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5421             var config = cm.config[i];
5422             
5423             var renderer = cm.getRenderer(i);
5424             var value = '';
5425             var id = false;
5426             
5427             if(typeof(renderer) !== 'undefined'){
5428                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5429             }
5430             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5431             // and are rendered into the cells after the row is rendered - using the id for the element.
5432             
5433             if(typeof(value) === 'object'){
5434                 id = Roo.id();
5435                 cellObjects.push({
5436                     container : id,
5437                     cfg : value 
5438                 })
5439             }
5440             
5441             var rowcfg = {
5442                 record: d,
5443                 rowIndex : rowIndex,
5444                 colIndex : i,
5445                 rowClass : ''
5446             }
5447
5448             this.fireEvent('rowclass', this, rowcfg);
5449             
5450             var td = {
5451                 tag: 'td',
5452                 cls : rowcfg.rowClass,
5453                 style: '',
5454                 html: (typeof(value) === 'object') ? '' : value
5455             };
5456             
5457             if (id) {
5458                 td.id = id;
5459             }
5460             
5461             if(typeof(config.hidden) != 'undefined' && config.hidden){
5462                 td.style += ' display:none;';
5463             }
5464             
5465             if(typeof(config.align) != 'undefined' && config.align.length){
5466                 td.style += ' text-align:' + config.align + ';';
5467             }
5468             
5469             if(typeof(config.width) != 'undefined'){
5470                 td.style += ' width:' +  config.width + 'px;';
5471             }
5472              
5473             row.cn.push(td);
5474            
5475         }
5476         
5477         row.cellObjects = cellObjects;
5478         
5479         return row;
5480           
5481     },
5482     
5483     
5484     
5485     onBeforeLoad : function()
5486     {
5487         //Roo.log('ds onBeforeLoad');
5488         
5489         //this.clear();
5490         
5491         //if(this.loadMask){
5492         //    this.maskEl.show();
5493         //}
5494     },
5495     
5496     clear : function()
5497     {
5498         this.el.select('tbody', true).first().dom.innerHTML = '';
5499     },
5500     
5501     getSelectionModel : function(){
5502         if(!this.selModel){
5503             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5504         }
5505         return this.selModel;
5506     },
5507     /*
5508      * Render the Roo.bootstrap object from renderder
5509      */
5510     renderCellObject : function(r)
5511     {
5512         var _this = this;
5513         
5514         var t = r.cfg.render(r.container);
5515         
5516         if(r.cfg.cn){
5517             Roo.each(r.cfg.cn, function(c){
5518                 var child = {
5519                     container: t.getChildContainer(),
5520                     cfg: c
5521                 }
5522                 _this.renderCellObject(child);
5523             })
5524         }
5525     }
5526    
5527 });
5528
5529  
5530
5531  /*
5532  * - LGPL
5533  *
5534  * table cell
5535  * 
5536  */
5537
5538 /**
5539  * @class Roo.bootstrap.TableCell
5540  * @extends Roo.bootstrap.Component
5541  * Bootstrap TableCell class
5542  * @cfg {String} html cell contain text
5543  * @cfg {String} cls cell class
5544  * @cfg {String} tag cell tag (td|th) default td
5545  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5546  * @cfg {String} align Aligns the content in a cell
5547  * @cfg {String} axis Categorizes cells
5548  * @cfg {String} bgcolor Specifies the background color of a cell
5549  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5550  * @cfg {Number} colspan Specifies the number of columns a cell should span
5551  * @cfg {String} headers Specifies one or more header cells a cell is related to
5552  * @cfg {Number} height Sets the height of a cell
5553  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5554  * @cfg {Number} rowspan Sets the number of rows a cell should span
5555  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5556  * @cfg {String} valign Vertical aligns the content in a cell
5557  * @cfg {Number} width Specifies the width of a cell
5558  * 
5559  * @constructor
5560  * Create a new TableCell
5561  * @param {Object} config The config object
5562  */
5563
5564 Roo.bootstrap.TableCell = function(config){
5565     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5566 };
5567
5568 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5569     
5570     html: false,
5571     cls: false,
5572     tag: false,
5573     abbr: false,
5574     align: false,
5575     axis: false,
5576     bgcolor: false,
5577     charoff: false,
5578     colspan: false,
5579     headers: false,
5580     height: false,
5581     nowrap: false,
5582     rowspan: false,
5583     scope: false,
5584     valign: false,
5585     width: false,
5586     
5587     
5588     getAutoCreate : function(){
5589         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5590         
5591         cfg = {
5592             tag: 'td'
5593         }
5594         
5595         if(this.tag){
5596             cfg.tag = this.tag;
5597         }
5598         
5599         if (this.html) {
5600             cfg.html=this.html
5601         }
5602         if (this.cls) {
5603             cfg.cls=this.cls
5604         }
5605         if (this.abbr) {
5606             cfg.abbr=this.abbr
5607         }
5608         if (this.align) {
5609             cfg.align=this.align
5610         }
5611         if (this.axis) {
5612             cfg.axis=this.axis
5613         }
5614         if (this.bgcolor) {
5615             cfg.bgcolor=this.bgcolor
5616         }
5617         if (this.charoff) {
5618             cfg.charoff=this.charoff
5619         }
5620         if (this.colspan) {
5621             cfg.colspan=this.colspan
5622         }
5623         if (this.headers) {
5624             cfg.headers=this.headers
5625         }
5626         if (this.height) {
5627             cfg.height=this.height
5628         }
5629         if (this.nowrap) {
5630             cfg.nowrap=this.nowrap
5631         }
5632         if (this.rowspan) {
5633             cfg.rowspan=this.rowspan
5634         }
5635         if (this.scope) {
5636             cfg.scope=this.scope
5637         }
5638         if (this.valign) {
5639             cfg.valign=this.valign
5640         }
5641         if (this.width) {
5642             cfg.width=this.width
5643         }
5644         
5645         
5646         return cfg;
5647     }
5648    
5649 });
5650
5651  
5652
5653  /*
5654  * - LGPL
5655  *
5656  * table row
5657  * 
5658  */
5659
5660 /**
5661  * @class Roo.bootstrap.TableRow
5662  * @extends Roo.bootstrap.Component
5663  * Bootstrap TableRow class
5664  * @cfg {String} cls row class
5665  * @cfg {String} align Aligns the content in a table row
5666  * @cfg {String} bgcolor Specifies a background color for a table row
5667  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5668  * @cfg {String} valign Vertical aligns the content in a table row
5669  * 
5670  * @constructor
5671  * Create a new TableRow
5672  * @param {Object} config The config object
5673  */
5674
5675 Roo.bootstrap.TableRow = function(config){
5676     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5677 };
5678
5679 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5680     
5681     cls: false,
5682     align: false,
5683     bgcolor: false,
5684     charoff: false,
5685     valign: false,
5686     
5687     getAutoCreate : function(){
5688         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5689         
5690         cfg = {
5691             tag: 'tr'
5692         }
5693             
5694         if(this.cls){
5695             cfg.cls = this.cls;
5696         }
5697         if(this.align){
5698             cfg.align = this.align;
5699         }
5700         if(this.bgcolor){
5701             cfg.bgcolor = this.bgcolor;
5702         }
5703         if(this.charoff){
5704             cfg.charoff = this.charoff;
5705         }
5706         if(this.valign){
5707             cfg.valign = this.valign;
5708         }
5709         
5710         return cfg;
5711     }
5712    
5713 });
5714
5715  
5716
5717  /*
5718  * - LGPL
5719  *
5720  * table body
5721  * 
5722  */
5723
5724 /**
5725  * @class Roo.bootstrap.TableBody
5726  * @extends Roo.bootstrap.Component
5727  * Bootstrap TableBody class
5728  * @cfg {String} cls element class
5729  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5730  * @cfg {String} align Aligns the content inside the element
5731  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5732  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5733  * 
5734  * @constructor
5735  * Create a new TableBody
5736  * @param {Object} config The config object
5737  */
5738
5739 Roo.bootstrap.TableBody = function(config){
5740     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5741 };
5742
5743 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5744     
5745     cls: false,
5746     tag: false,
5747     align: false,
5748     charoff: false,
5749     valign: false,
5750     
5751     getAutoCreate : function(){
5752         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5753         
5754         cfg = {
5755             tag: 'tbody'
5756         }
5757             
5758         if (this.cls) {
5759             cfg.cls=this.cls
5760         }
5761         if(this.tag){
5762             cfg.tag = this.tag;
5763         }
5764         
5765         if(this.align){
5766             cfg.align = this.align;
5767         }
5768         if(this.charoff){
5769             cfg.charoff = this.charoff;
5770         }
5771         if(this.valign){
5772             cfg.valign = this.valign;
5773         }
5774         
5775         return cfg;
5776     }
5777     
5778     
5779 //    initEvents : function()
5780 //    {
5781 //        
5782 //        if(!this.store){
5783 //            return;
5784 //        }
5785 //        
5786 //        this.store = Roo.factory(this.store, Roo.data);
5787 //        this.store.on('load', this.onLoad, this);
5788 //        
5789 //        this.store.load();
5790 //        
5791 //    },
5792 //    
5793 //    onLoad: function () 
5794 //    {   
5795 //        this.fireEvent('load', this);
5796 //    }
5797 //    
5798 //   
5799 });
5800
5801  
5802
5803  /*
5804  * Based on:
5805  * Ext JS Library 1.1.1
5806  * Copyright(c) 2006-2007, Ext JS, LLC.
5807  *
5808  * Originally Released Under LGPL - original licence link has changed is not relivant.
5809  *
5810  * Fork - LGPL
5811  * <script type="text/javascript">
5812  */
5813
5814 // as we use this in bootstrap.
5815 Roo.namespace('Roo.form');
5816  /**
5817  * @class Roo.form.Action
5818  * Internal Class used to handle form actions
5819  * @constructor
5820  * @param {Roo.form.BasicForm} el The form element or its id
5821  * @param {Object} config Configuration options
5822  */
5823
5824  
5825  
5826 // define the action interface
5827 Roo.form.Action = function(form, options){
5828     this.form = form;
5829     this.options = options || {};
5830 };
5831 /**
5832  * Client Validation Failed
5833  * @const 
5834  */
5835 Roo.form.Action.CLIENT_INVALID = 'client';
5836 /**
5837  * Server Validation Failed
5838  * @const 
5839  */
5840 Roo.form.Action.SERVER_INVALID = 'server';
5841  /**
5842  * Connect to Server Failed
5843  * @const 
5844  */
5845 Roo.form.Action.CONNECT_FAILURE = 'connect';
5846 /**
5847  * Reading Data from Server Failed
5848  * @const 
5849  */
5850 Roo.form.Action.LOAD_FAILURE = 'load';
5851
5852 Roo.form.Action.prototype = {
5853     type : 'default',
5854     failureType : undefined,
5855     response : undefined,
5856     result : undefined,
5857
5858     // interface method
5859     run : function(options){
5860
5861     },
5862
5863     // interface method
5864     success : function(response){
5865
5866     },
5867
5868     // interface method
5869     handleResponse : function(response){
5870
5871     },
5872
5873     // default connection failure
5874     failure : function(response){
5875         
5876         this.response = response;
5877         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5878         this.form.afterAction(this, false);
5879     },
5880
5881     processResponse : function(response){
5882         this.response = response;
5883         if(!response.responseText){
5884             return true;
5885         }
5886         this.result = this.handleResponse(response);
5887         return this.result;
5888     },
5889
5890     // utility functions used internally
5891     getUrl : function(appendParams){
5892         var url = this.options.url || this.form.url || this.form.el.dom.action;
5893         if(appendParams){
5894             var p = this.getParams();
5895             if(p){
5896                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5897             }
5898         }
5899         return url;
5900     },
5901
5902     getMethod : function(){
5903         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5904     },
5905
5906     getParams : function(){
5907         var bp = this.form.baseParams;
5908         var p = this.options.params;
5909         if(p){
5910             if(typeof p == "object"){
5911                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5912             }else if(typeof p == 'string' && bp){
5913                 p += '&' + Roo.urlEncode(bp);
5914             }
5915         }else if(bp){
5916             p = Roo.urlEncode(bp);
5917         }
5918         return p;
5919     },
5920
5921     createCallback : function(){
5922         return {
5923             success: this.success,
5924             failure: this.failure,
5925             scope: this,
5926             timeout: (this.form.timeout*1000),
5927             upload: this.form.fileUpload ? this.success : undefined
5928         };
5929     }
5930 };
5931
5932 Roo.form.Action.Submit = function(form, options){
5933     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5934 };
5935
5936 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5937     type : 'submit',
5938
5939     haveProgress : false,
5940     uploadComplete : false,
5941     
5942     // uploadProgress indicator.
5943     uploadProgress : function()
5944     {
5945         if (!this.form.progressUrl) {
5946             return;
5947         }
5948         
5949         if (!this.haveProgress) {
5950             Roo.MessageBox.progress("Uploading", "Uploading");
5951         }
5952         if (this.uploadComplete) {
5953            Roo.MessageBox.hide();
5954            return;
5955         }
5956         
5957         this.haveProgress = true;
5958    
5959         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5960         
5961         var c = new Roo.data.Connection();
5962         c.request({
5963             url : this.form.progressUrl,
5964             params: {
5965                 id : uid
5966             },
5967             method: 'GET',
5968             success : function(req){
5969                //console.log(data);
5970                 var rdata = false;
5971                 var edata;
5972                 try  {
5973                    rdata = Roo.decode(req.responseText)
5974                 } catch (e) {
5975                     Roo.log("Invalid data from server..");
5976                     Roo.log(edata);
5977                     return;
5978                 }
5979                 if (!rdata || !rdata.success) {
5980                     Roo.log(rdata);
5981                     Roo.MessageBox.alert(Roo.encode(rdata));
5982                     return;
5983                 }
5984                 var data = rdata.data;
5985                 
5986                 if (this.uploadComplete) {
5987                    Roo.MessageBox.hide();
5988                    return;
5989                 }
5990                    
5991                 if (data){
5992                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5993                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5994                     );
5995                 }
5996                 this.uploadProgress.defer(2000,this);
5997             },
5998        
5999             failure: function(data) {
6000                 Roo.log('progress url failed ');
6001                 Roo.log(data);
6002             },
6003             scope : this
6004         });
6005            
6006     },
6007     
6008     
6009     run : function()
6010     {
6011         // run get Values on the form, so it syncs any secondary forms.
6012         this.form.getValues();
6013         
6014         var o = this.options;
6015         var method = this.getMethod();
6016         var isPost = method == 'POST';
6017         if(o.clientValidation === false || this.form.isValid()){
6018             
6019             if (this.form.progressUrl) {
6020                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6021                     (new Date() * 1) + '' + Math.random());
6022                     
6023             } 
6024             
6025             
6026             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6027                 form:this.form.el.dom,
6028                 url:this.getUrl(!isPost),
6029                 method: method,
6030                 params:isPost ? this.getParams() : null,
6031                 isUpload: this.form.fileUpload
6032             }));
6033             
6034             this.uploadProgress();
6035
6036         }else if (o.clientValidation !== false){ // client validation failed
6037             this.failureType = Roo.form.Action.CLIENT_INVALID;
6038             this.form.afterAction(this, false);
6039         }
6040     },
6041
6042     success : function(response)
6043     {
6044         this.uploadComplete= true;
6045         if (this.haveProgress) {
6046             Roo.MessageBox.hide();
6047         }
6048         
6049         
6050         var result = this.processResponse(response);
6051         if(result === true || result.success){
6052             this.form.afterAction(this, true);
6053             return;
6054         }
6055         if(result.errors){
6056             this.form.markInvalid(result.errors);
6057             this.failureType = Roo.form.Action.SERVER_INVALID;
6058         }
6059         this.form.afterAction(this, false);
6060     },
6061     failure : function(response)
6062     {
6063         this.uploadComplete= true;
6064         if (this.haveProgress) {
6065             Roo.MessageBox.hide();
6066         }
6067         
6068         this.response = response;
6069         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6070         this.form.afterAction(this, false);
6071     },
6072     
6073     handleResponse : function(response){
6074         if(this.form.errorReader){
6075             var rs = this.form.errorReader.read(response);
6076             var errors = [];
6077             if(rs.records){
6078                 for(var i = 0, len = rs.records.length; i < len; i++) {
6079                     var r = rs.records[i];
6080                     errors[i] = r.data;
6081                 }
6082             }
6083             if(errors.length < 1){
6084                 errors = null;
6085             }
6086             return {
6087                 success : rs.success,
6088                 errors : errors
6089             };
6090         }
6091         var ret = false;
6092         try {
6093             ret = Roo.decode(response.responseText);
6094         } catch (e) {
6095             ret = {
6096                 success: false,
6097                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6098                 errors : []
6099             };
6100         }
6101         return ret;
6102         
6103     }
6104 });
6105
6106
6107 Roo.form.Action.Load = function(form, options){
6108     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6109     this.reader = this.form.reader;
6110 };
6111
6112 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6113     type : 'load',
6114
6115     run : function(){
6116         
6117         Roo.Ajax.request(Roo.apply(
6118                 this.createCallback(), {
6119                     method:this.getMethod(),
6120                     url:this.getUrl(false),
6121                     params:this.getParams()
6122         }));
6123     },
6124
6125     success : function(response){
6126         
6127         var result = this.processResponse(response);
6128         if(result === true || !result.success || !result.data){
6129             this.failureType = Roo.form.Action.LOAD_FAILURE;
6130             this.form.afterAction(this, false);
6131             return;
6132         }
6133         this.form.clearInvalid();
6134         this.form.setValues(result.data);
6135         this.form.afterAction(this, true);
6136     },
6137
6138     handleResponse : function(response){
6139         if(this.form.reader){
6140             var rs = this.form.reader.read(response);
6141             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6142             return {
6143                 success : rs.success,
6144                 data : data
6145             };
6146         }
6147         return Roo.decode(response.responseText);
6148     }
6149 });
6150
6151 Roo.form.Action.ACTION_TYPES = {
6152     'load' : Roo.form.Action.Load,
6153     'submit' : Roo.form.Action.Submit
6154 };/*
6155  * - LGPL
6156  *
6157  * form
6158  * 
6159  */
6160
6161 /**
6162  * @class Roo.bootstrap.Form
6163  * @extends Roo.bootstrap.Component
6164  * Bootstrap Form class
6165  * @cfg {String} method  GET | POST (default POST)
6166  * @cfg {String} labelAlign top | left (default top)
6167  * @cfg {String} align left  | right - for navbars
6168  * @cfg {Boolean} loadMask load mask when submit (default true)
6169
6170  * 
6171  * @constructor
6172  * Create a new Form
6173  * @param {Object} config The config object
6174  */
6175
6176
6177 Roo.bootstrap.Form = function(config){
6178     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6179     this.addEvents({
6180         /**
6181          * @event clientvalidation
6182          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6183          * @param {Form} this
6184          * @param {Boolean} valid true if the form has passed client-side validation
6185          */
6186         clientvalidation: true,
6187         /**
6188          * @event beforeaction
6189          * Fires before any action is performed. Return false to cancel the action.
6190          * @param {Form} this
6191          * @param {Action} action The action to be performed
6192          */
6193         beforeaction: true,
6194         /**
6195          * @event actionfailed
6196          * Fires when an action fails.
6197          * @param {Form} this
6198          * @param {Action} action The action that failed
6199          */
6200         actionfailed : true,
6201         /**
6202          * @event actioncomplete
6203          * Fires when an action is completed.
6204          * @param {Form} this
6205          * @param {Action} action The action that completed
6206          */
6207         actioncomplete : true
6208     });
6209     
6210 };
6211
6212 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6213       
6214      /**
6215      * @cfg {String} method
6216      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6217      */
6218     method : 'POST',
6219     /**
6220      * @cfg {String} url
6221      * The URL to use for form actions if one isn't supplied in the action options.
6222      */
6223     /**
6224      * @cfg {Boolean} fileUpload
6225      * Set to true if this form is a file upload.
6226      */
6227      
6228     /**
6229      * @cfg {Object} baseParams
6230      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6231      */
6232       
6233     /**
6234      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6235      */
6236     timeout: 30,
6237     /**
6238      * @cfg {Sting} align (left|right) for navbar forms
6239      */
6240     align : 'left',
6241
6242     // private
6243     activeAction : null,
6244  
6245     /**
6246      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6247      * element by passing it or its id or mask the form itself by passing in true.
6248      * @type Mixed
6249      */
6250     waitMsgTarget : false,
6251     
6252     loadMask : true,
6253     
6254     getAutoCreate : function(){
6255         
6256         var cfg = {
6257             tag: 'form',
6258             method : this.method || 'POST',
6259             id : this.id || Roo.id(),
6260             cls : ''
6261         }
6262         if (this.parent().xtype.match(/^Nav/)) {
6263             cfg.cls = 'navbar-form navbar-' + this.align;
6264             
6265         }
6266         
6267         if (this.labelAlign == 'left' ) {
6268             cfg.cls += ' form-horizontal';
6269         }
6270         
6271         
6272         return cfg;
6273     },
6274     initEvents : function()
6275     {
6276         this.el.on('submit', this.onSubmit, this);
6277         // this was added as random key presses on the form where triggering form submit.
6278         this.el.on('keypress', function(e) {
6279             if (e.getCharCode() != 13) {
6280                 return true;
6281             }
6282             // we might need to allow it for textareas.. and some other items.
6283             // check e.getTarget().
6284             
6285             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6286                 return true;
6287             }
6288         
6289             Roo.log("keypress blocked");
6290             
6291             e.preventDefault();
6292             return false;
6293         });
6294         
6295     },
6296     // private
6297     onSubmit : function(e){
6298         e.stopEvent();
6299     },
6300     
6301      /**
6302      * Returns true if client-side validation on the form is successful.
6303      * @return Boolean
6304      */
6305     isValid : function(){
6306         var items = this.getItems();
6307         var valid = true;
6308         items.each(function(f){
6309            if(!f.validate()){
6310                valid = false;
6311                
6312            }
6313         });
6314         return valid;
6315     },
6316     /**
6317      * Returns true if any fields in this form have changed since their original load.
6318      * @return Boolean
6319      */
6320     isDirty : function(){
6321         var dirty = false;
6322         var items = this.getItems();
6323         items.each(function(f){
6324            if(f.isDirty()){
6325                dirty = true;
6326                return false;
6327            }
6328            return true;
6329         });
6330         return dirty;
6331     },
6332      /**
6333      * Performs a predefined action (submit or load) or custom actions you define on this form.
6334      * @param {String} actionName The name of the action type
6335      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6336      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6337      * accept other config options):
6338      * <pre>
6339 Property          Type             Description
6340 ----------------  ---------------  ----------------------------------------------------------------------------------
6341 url               String           The url for the action (defaults to the form's url)
6342 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6343 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6344 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6345                                    validate the form on the client (defaults to false)
6346      * </pre>
6347      * @return {BasicForm} this
6348      */
6349     doAction : function(action, options){
6350         if(typeof action == 'string'){
6351             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6352         }
6353         if(this.fireEvent('beforeaction', this, action) !== false){
6354             this.beforeAction(action);
6355             action.run.defer(100, action);
6356         }
6357         return this;
6358     },
6359     
6360     // private
6361     beforeAction : function(action){
6362         var o = action.options;
6363         
6364         if(this.loadMask){
6365             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6366         }
6367         // not really supported yet.. ??
6368         
6369         //if(this.waitMsgTarget === true){
6370         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6371         //}else if(this.waitMsgTarget){
6372         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6373         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6374         //}else {
6375         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6376        // }
6377          
6378     },
6379
6380     // private
6381     afterAction : function(action, success){
6382         this.activeAction = null;
6383         var o = action.options;
6384         
6385         //if(this.waitMsgTarget === true){
6386             this.el.unmask();
6387         //}else if(this.waitMsgTarget){
6388         //    this.waitMsgTarget.unmask();
6389         //}else{
6390         //    Roo.MessageBox.updateProgress(1);
6391         //    Roo.MessageBox.hide();
6392        // }
6393         // 
6394         if(success){
6395             if(o.reset){
6396                 this.reset();
6397             }
6398             Roo.callback(o.success, o.scope, [this, action]);
6399             this.fireEvent('actioncomplete', this, action);
6400             
6401         }else{
6402             
6403             // failure condition..
6404             // we have a scenario where updates need confirming.
6405             // eg. if a locking scenario exists..
6406             // we look for { errors : { needs_confirm : true }} in the response.
6407             if (
6408                 (typeof(action.result) != 'undefined')  &&
6409                 (typeof(action.result.errors) != 'undefined')  &&
6410                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6411            ){
6412                 var _t = this;
6413                 Roo.log("not supported yet");
6414                  /*
6415                 
6416                 Roo.MessageBox.confirm(
6417                     "Change requires confirmation",
6418                     action.result.errorMsg,
6419                     function(r) {
6420                         if (r != 'yes') {
6421                             return;
6422                         }
6423                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6424                     }
6425                     
6426                 );
6427                 */
6428                 
6429                 
6430                 return;
6431             }
6432             
6433             Roo.callback(o.failure, o.scope, [this, action]);
6434             // show an error message if no failed handler is set..
6435             if (!this.hasListener('actionfailed')) {
6436                 Roo.log("need to add dialog support");
6437                 /*
6438                 Roo.MessageBox.alert("Error",
6439                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6440                         action.result.errorMsg :
6441                         "Saving Failed, please check your entries or try again"
6442                 );
6443                 */
6444             }
6445             
6446             this.fireEvent('actionfailed', this, action);
6447         }
6448         
6449     },
6450     /**
6451      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6452      * @param {String} id The value to search for
6453      * @return Field
6454      */
6455     findField : function(id){
6456         var items = this.getItems();
6457         var field = items.get(id);
6458         if(!field){
6459              items.each(function(f){
6460                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6461                     field = f;
6462                     return false;
6463                 }
6464                 return true;
6465             });
6466         }
6467         return field || null;
6468     },
6469      /**
6470      * Mark fields in this form invalid in bulk.
6471      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6472      * @return {BasicForm} this
6473      */
6474     markInvalid : function(errors){
6475         if(errors instanceof Array){
6476             for(var i = 0, len = errors.length; i < len; i++){
6477                 var fieldError = errors[i];
6478                 var f = this.findField(fieldError.id);
6479                 if(f){
6480                     f.markInvalid(fieldError.msg);
6481                 }
6482             }
6483         }else{
6484             var field, id;
6485             for(id in errors){
6486                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6487                     field.markInvalid(errors[id]);
6488                 }
6489             }
6490         }
6491         //Roo.each(this.childForms || [], function (f) {
6492         //    f.markInvalid(errors);
6493         //});
6494         
6495         return this;
6496     },
6497
6498     /**
6499      * Set values for fields in this form in bulk.
6500      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6501      * @return {BasicForm} this
6502      */
6503     setValues : function(values){
6504         if(values instanceof Array){ // array of objects
6505             for(var i = 0, len = values.length; i < len; i++){
6506                 var v = values[i];
6507                 var f = this.findField(v.id);
6508                 if(f){
6509                     f.setValue(v.value);
6510                     if(this.trackResetOnLoad){
6511                         f.originalValue = f.getValue();
6512                     }
6513                 }
6514             }
6515         }else{ // object hash
6516             var field, id;
6517             for(id in values){
6518                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6519                     
6520                     if (field.setFromData && 
6521                         field.valueField && 
6522                         field.displayField &&
6523                         // combos' with local stores can 
6524                         // be queried via setValue()
6525                         // to set their value..
6526                         (field.store && !field.store.isLocal)
6527                         ) {
6528                         // it's a combo
6529                         var sd = { };
6530                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6531                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6532                         field.setFromData(sd);
6533                         
6534                     } else {
6535                         field.setValue(values[id]);
6536                     }
6537                     
6538                     
6539                     if(this.trackResetOnLoad){
6540                         field.originalValue = field.getValue();
6541                     }
6542                 }
6543             }
6544         }
6545          
6546         //Roo.each(this.childForms || [], function (f) {
6547         //    f.setValues(values);
6548         //});
6549                 
6550         return this;
6551     },
6552
6553     /**
6554      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6555      * they are returned as an array.
6556      * @param {Boolean} asString
6557      * @return {Object}
6558      */
6559     getValues : function(asString){
6560         //if (this.childForms) {
6561             // copy values from the child forms
6562         //    Roo.each(this.childForms, function (f) {
6563         //        this.setValues(f.getValues());
6564         //    }, this);
6565         //}
6566         
6567         
6568         
6569         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6570         if(asString === true){
6571             return fs;
6572         }
6573         return Roo.urlDecode(fs);
6574     },
6575     
6576     /**
6577      * Returns the fields in this form as an object with key/value pairs. 
6578      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6579      * @return {Object}
6580      */
6581     getFieldValues : function(with_hidden)
6582     {
6583         var items = this.getItems();
6584         var ret = {};
6585         items.each(function(f){
6586             if (!f.getName()) {
6587                 return;
6588             }
6589             var v = f.getValue();
6590             if (f.inputType =='radio') {
6591                 if (typeof(ret[f.getName()]) == 'undefined') {
6592                     ret[f.getName()] = ''; // empty..
6593                 }
6594                 
6595                 if (!f.el.dom.checked) {
6596                     return;
6597                     
6598                 }
6599                 v = f.el.dom.value;
6600                 
6601             }
6602             
6603             // not sure if this supported any more..
6604             if ((typeof(v) == 'object') && f.getRawValue) {
6605                 v = f.getRawValue() ; // dates..
6606             }
6607             // combo boxes where name != hiddenName...
6608             if (f.name != f.getName()) {
6609                 ret[f.name] = f.getRawValue();
6610             }
6611             ret[f.getName()] = v;
6612         });
6613         
6614         return ret;
6615     },
6616
6617     /**
6618      * Clears all invalid messages in this form.
6619      * @return {BasicForm} this
6620      */
6621     clearInvalid : function(){
6622         var items = this.getItems();
6623         
6624         items.each(function(f){
6625            f.clearInvalid();
6626         });
6627         
6628         
6629         
6630         return this;
6631     },
6632
6633     /**
6634      * Resets this form.
6635      * @return {BasicForm} this
6636      */
6637     reset : function(){
6638         var items = this.getItems();
6639         items.each(function(f){
6640             f.reset();
6641         });
6642         
6643         Roo.each(this.childForms || [], function (f) {
6644             f.reset();
6645         });
6646        
6647         
6648         return this;
6649     },
6650     getItems : function()
6651     {
6652         var r=new Roo.util.MixedCollection(false, function(o){
6653             return o.id || (o.id = Roo.id());
6654         });
6655         var iter = function(el) {
6656             if (el.inputEl) {
6657                 r.add(el);
6658             }
6659             if (!el.items) {
6660                 return;
6661             }
6662             Roo.each(el.items,function(e) {
6663                 iter(e);
6664             });
6665             
6666             
6667         };
6668         iter(this);
6669         return r;
6670         
6671         
6672         
6673         
6674     }
6675     
6676 });
6677
6678  
6679 /*
6680  * Based on:
6681  * Ext JS Library 1.1.1
6682  * Copyright(c) 2006-2007, Ext JS, LLC.
6683  *
6684  * Originally Released Under LGPL - original licence link has changed is not relivant.
6685  *
6686  * Fork - LGPL
6687  * <script type="text/javascript">
6688  */
6689 /**
6690  * @class Roo.form.VTypes
6691  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6692  * @singleton
6693  */
6694 Roo.form.VTypes = function(){
6695     // closure these in so they are only created once.
6696     var alpha = /^[a-zA-Z_]+$/;
6697     var alphanum = /^[a-zA-Z0-9_]+$/;
6698     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6699     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6700
6701     // All these messages and functions are configurable
6702     return {
6703         /**
6704          * The function used to validate email addresses
6705          * @param {String} value The email address
6706          */
6707         'email' : function(v){
6708             return email.test(v);
6709         },
6710         /**
6711          * The error text to display when the email validation function returns false
6712          * @type String
6713          */
6714         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6715         /**
6716          * The keystroke filter mask to be applied on email input
6717          * @type RegExp
6718          */
6719         'emailMask' : /[a-z0-9_\.\-@]/i,
6720
6721         /**
6722          * The function used to validate URLs
6723          * @param {String} value The URL
6724          */
6725         'url' : function(v){
6726             return url.test(v);
6727         },
6728         /**
6729          * The error text to display when the url validation function returns false
6730          * @type String
6731          */
6732         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6733         
6734         /**
6735          * The function used to validate alpha values
6736          * @param {String} value The value
6737          */
6738         'alpha' : function(v){
6739             return alpha.test(v);
6740         },
6741         /**
6742          * The error text to display when the alpha validation function returns false
6743          * @type String
6744          */
6745         'alphaText' : 'This field should only contain letters and _',
6746         /**
6747          * The keystroke filter mask to be applied on alpha input
6748          * @type RegExp
6749          */
6750         'alphaMask' : /[a-z_]/i,
6751
6752         /**
6753          * The function used to validate alphanumeric values
6754          * @param {String} value The value
6755          */
6756         'alphanum' : function(v){
6757             return alphanum.test(v);
6758         },
6759         /**
6760          * The error text to display when the alphanumeric validation function returns false
6761          * @type String
6762          */
6763         'alphanumText' : 'This field should only contain letters, numbers and _',
6764         /**
6765          * The keystroke filter mask to be applied on alphanumeric input
6766          * @type RegExp
6767          */
6768         'alphanumMask' : /[a-z0-9_]/i
6769     };
6770 }();/*
6771  * - LGPL
6772  *
6773  * Input
6774  * 
6775  */
6776
6777 /**
6778  * @class Roo.bootstrap.Input
6779  * @extends Roo.bootstrap.Component
6780  * Bootstrap Input class
6781  * @cfg {Boolean} disabled is it disabled
6782  * @cfg {String} fieldLabel - the label associated
6783  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6784  * @cfg {String} name name of the input
6785  * @cfg {string} fieldLabel - the label associated
6786  * @cfg {string}  inputType - input / file submit ...
6787  * @cfg {string} placeholder - placeholder to put in text.
6788  * @cfg {string}  before - input group add on before
6789  * @cfg {string} after - input group add on after
6790  * @cfg {string} size - (lg|sm) or leave empty..
6791  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6792  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6793  * @cfg {Number} md colspan out of 12 for computer-sized screens
6794  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6795  * @cfg {string} value default value of the input
6796  * @cfg {Number} labelWidth set the width of label (0-12)
6797  * @cfg {String} labelAlign (top|left)
6798  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6799  * @cfg {String} align (left|center|right) Default left
6800  * 
6801  * 
6802  * @constructor
6803  * Create a new Input
6804  * @param {Object} config The config object
6805  */
6806
6807 Roo.bootstrap.Input = function(config){
6808     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6809    
6810         this.addEvents({
6811             /**
6812              * @event focus
6813              * Fires when this field receives input focus.
6814              * @param {Roo.form.Field} this
6815              */
6816             focus : true,
6817             /**
6818              * @event blur
6819              * Fires when this field loses input focus.
6820              * @param {Roo.form.Field} this
6821              */
6822             blur : true,
6823             /**
6824              * @event specialkey
6825              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6826              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6827              * @param {Roo.form.Field} this
6828              * @param {Roo.EventObject} e The event object
6829              */
6830             specialkey : true,
6831             /**
6832              * @event change
6833              * Fires just before the field blurs if the field value has changed.
6834              * @param {Roo.form.Field} this
6835              * @param {Mixed} newValue The new value
6836              * @param {Mixed} oldValue The original value
6837              */
6838             change : true,
6839             /**
6840              * @event invalid
6841              * Fires after the field has been marked as invalid.
6842              * @param {Roo.form.Field} this
6843              * @param {String} msg The validation message
6844              */
6845             invalid : true,
6846             /**
6847              * @event valid
6848              * Fires after the field has been validated with no errors.
6849              * @param {Roo.form.Field} this
6850              */
6851             valid : true,
6852              /**
6853              * @event keyup
6854              * Fires after the key up
6855              * @param {Roo.form.Field} this
6856              * @param {Roo.EventObject}  e The event Object
6857              */
6858             keyup : true
6859         });
6860 };
6861
6862 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6863      /**
6864      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6865       automatic validation (defaults to "keyup").
6866      */
6867     validationEvent : "keyup",
6868      /**
6869      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6870      */
6871     validateOnBlur : true,
6872     /**
6873      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6874      */
6875     validationDelay : 250,
6876      /**
6877      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6878      */
6879     focusClass : "x-form-focus",  // not needed???
6880     
6881        
6882     /**
6883      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6884      */
6885     invalidClass : "has-error",
6886     
6887     /**
6888      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6889      */
6890     selectOnFocus : false,
6891     
6892      /**
6893      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6894      */
6895     maskRe : null,
6896        /**
6897      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6898      */
6899     vtype : null,
6900     
6901       /**
6902      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6903      */
6904     disableKeyFilter : false,
6905     
6906        /**
6907      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6908      */
6909     disabled : false,
6910      /**
6911      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6912      */
6913     allowBlank : true,
6914     /**
6915      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6916      */
6917     blankText : "This field is required",
6918     
6919      /**
6920      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6921      */
6922     minLength : 0,
6923     /**
6924      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6925      */
6926     maxLength : Number.MAX_VALUE,
6927     /**
6928      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6929      */
6930     minLengthText : "The minimum length for this field is {0}",
6931     /**
6932      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6933      */
6934     maxLengthText : "The maximum length for this field is {0}",
6935   
6936     
6937     /**
6938      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6939      * If available, this function will be called only after the basic validators all return true, and will be passed the
6940      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6941      */
6942     validator : null,
6943     /**
6944      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6945      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6946      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6947      */
6948     regex : null,
6949     /**
6950      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6951      */
6952     regexText : "",
6953     
6954     
6955     
6956     fieldLabel : '',
6957     inputType : 'text',
6958     
6959     name : false,
6960     placeholder: false,
6961     before : false,
6962     after : false,
6963     size : false,
6964     // private
6965     hasFocus : false,
6966     preventMark: false,
6967     isFormField : true,
6968     value : '',
6969     labelWidth : 2,
6970     labelAlign : false,
6971     readOnly : false,
6972     align : false,
6973     formatedValue : false,
6974     
6975     parentLabelAlign : function()
6976     {
6977         var parent = this;
6978         while (parent.parent()) {
6979             parent = parent.parent();
6980             if (typeof(parent.labelAlign) !='undefined') {
6981                 return parent.labelAlign;
6982             }
6983         }
6984         return 'left';
6985         
6986     },
6987     
6988     getAutoCreate : function(){
6989         
6990         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6991         
6992         var id = Roo.id();
6993         
6994         var cfg = {};
6995         
6996         if(this.inputType != 'hidden'){
6997             cfg.cls = 'form-group' //input-group
6998         }
6999         
7000         var input =  {
7001             tag: 'input',
7002             id : id,
7003             type : this.inputType,
7004             value : this.value,
7005             cls : 'form-control',
7006             placeholder : this.placeholder || ''
7007             
7008         };
7009         
7010         if(this.align){
7011             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7012         }
7013         
7014         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7015             input.maxLength = this.maxLength;
7016         }
7017         
7018         if (this.disabled) {
7019             input.disabled=true;
7020         }
7021         
7022         if (this.readOnly) {
7023             input.readonly=true;
7024         }
7025         
7026         if (this.name) {
7027             input.name = this.name;
7028         }
7029         if (this.size) {
7030             input.cls += ' input-' + this.size;
7031         }
7032         var settings=this;
7033         ['xs','sm','md','lg'].map(function(size){
7034             if (settings[size]) {
7035                 cfg.cls += ' col-' + size + '-' + settings[size];
7036             }
7037         });
7038         
7039         var inputblock = input;
7040         
7041         if (this.before || this.after) {
7042             
7043             inputblock = {
7044                 cls : 'input-group',
7045                 cn :  [] 
7046             };
7047             if (this.before && typeof(this.before) == 'string') {
7048                 
7049                 inputblock.cn.push({
7050                     tag :'span',
7051                     cls : 'roo-input-before input-group-addon',
7052                     html : this.before
7053                 });
7054             }
7055             if (this.before && typeof(this.before) == 'object') {
7056                 this.before = Roo.factory(this.before);
7057                 Roo.log(this.before);
7058                 inputblock.cn.push({
7059                     tag :'span',
7060                     cls : 'roo-input-before input-group-' +
7061                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7062                 });
7063             }
7064             
7065             inputblock.cn.push(input);
7066             
7067             if (this.after && typeof(this.after) == 'string') {
7068                 inputblock.cn.push({
7069                     tag :'span',
7070                     cls : 'roo-input-after input-group-addon',
7071                     html : this.after
7072                 });
7073             }
7074             if (this.after && typeof(this.after) == 'object') {
7075                 this.after = Roo.factory(this.after);
7076                 Roo.log(this.after);
7077                 inputblock.cn.push({
7078                     tag :'span',
7079                     cls : 'roo-input-after input-group-' +
7080                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7081                 });
7082             }
7083         };
7084         
7085         if (align ==='left' && this.fieldLabel.length) {
7086                 Roo.log("left and has label");
7087                 cfg.cn = [
7088                     
7089                     {
7090                         tag: 'label',
7091                         'for' :  id,
7092                         cls : 'control-label col-sm-' + this.labelWidth,
7093                         html : this.fieldLabel
7094                         
7095                     },
7096                     {
7097                         cls : "col-sm-" + (12 - this.labelWidth), 
7098                         cn: [
7099                             inputblock
7100                         ]
7101                     }
7102                     
7103                 ];
7104         } else if ( this.fieldLabel.length) {
7105                 Roo.log(" label");
7106                  cfg.cn = [
7107                    
7108                     {
7109                         tag: 'label',
7110                         //cls : 'input-group-addon',
7111                         html : this.fieldLabel
7112                         
7113                     },
7114                     
7115                     inputblock
7116                     
7117                 ];
7118
7119         } else {
7120             
7121                 Roo.log(" no label && no align");
7122                 cfg.cn = [
7123                     
7124                         inputblock
7125                     
7126                 ];
7127                 
7128                 
7129         };
7130         Roo.log('input-parentType: ' + this.parentType);
7131         
7132         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7133            cfg.cls += ' navbar-form';
7134            Roo.log(cfg);
7135         }
7136         
7137         return cfg;
7138         
7139     },
7140     /**
7141      * return the real input element.
7142      */
7143     inputEl: function ()
7144     {
7145         return this.el.select('input.form-control',true).first();
7146     },
7147     setDisabled : function(v)
7148     {
7149         var i  = this.inputEl().dom;
7150         if (!v) {
7151             i.removeAttribute('disabled');
7152             return;
7153             
7154         }
7155         i.setAttribute('disabled','true');
7156     },
7157     initEvents : function()
7158     {
7159         
7160         this.inputEl().on("keydown" , this.fireKey,  this);
7161         this.inputEl().on("focus", this.onFocus,  this);
7162         this.inputEl().on("blur", this.onBlur,  this);
7163         
7164         this.inputEl().relayEvent('keyup', this);
7165
7166         // reference to original value for reset
7167         this.originalValue = this.getValue();
7168         //Roo.form.TextField.superclass.initEvents.call(this);
7169         if(this.validationEvent == 'keyup'){
7170             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7171             this.inputEl().on('keyup', this.filterValidation, this);
7172         }
7173         else if(this.validationEvent !== false){
7174             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7175         }
7176         
7177         if(this.selectOnFocus){
7178             this.on("focus", this.preFocus, this);
7179             
7180         }
7181         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7182             this.inputEl().on("keypress", this.filterKeys, this);
7183         }
7184        /* if(this.grow){
7185             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7186             this.el.on("click", this.autoSize,  this);
7187         }
7188         */
7189         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7190             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7191         }
7192         
7193         if (typeof(this.before) == 'object') {
7194             this.before.render(this.el.select('.roo-input-before',true).first());
7195         }
7196         if (typeof(this.after) == 'object') {
7197             this.after.render(this.el.select('.roo-input-after',true).first());
7198         }
7199         
7200         
7201     },
7202     filterValidation : function(e){
7203         if(!e.isNavKeyPress()){
7204             this.validationTask.delay(this.validationDelay);
7205         }
7206     },
7207      /**
7208      * Validates the field value
7209      * @return {Boolean} True if the value is valid, else false
7210      */
7211     validate : function(){
7212         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7213         if(this.disabled || this.validateValue(this.getRawValue())){
7214             this.clearInvalid();
7215             return true;
7216         }
7217         return false;
7218     },
7219     
7220     
7221     /**
7222      * Validates a value according to the field's validation rules and marks the field as invalid
7223      * if the validation fails
7224      * @param {Mixed} value The value to validate
7225      * @return {Boolean} True if the value is valid, else false
7226      */
7227     validateValue : function(value){
7228         if(value.length < 1)  { // if it's blank
7229              if(this.allowBlank){
7230                 this.clearInvalid();
7231                 return true;
7232              }else{
7233                 this.markInvalid(this.blankText);
7234                 return false;
7235              }
7236         }
7237         if(value.length < this.minLength){
7238             this.markInvalid(String.format(this.minLengthText, this.minLength));
7239             return false;
7240         }
7241         if(value.length > this.maxLength){
7242             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7243             return false;
7244         }
7245         if(this.vtype){
7246             var vt = Roo.form.VTypes;
7247             if(!vt[this.vtype](value, this)){
7248                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7249                 return false;
7250             }
7251         }
7252         if(typeof this.validator == "function"){
7253             var msg = this.validator(value);
7254             if(msg !== true){
7255                 this.markInvalid(msg);
7256                 return false;
7257             }
7258         }
7259         if(this.regex && !this.regex.test(value)){
7260             this.markInvalid(this.regexText);
7261             return false;
7262         }
7263         return true;
7264     },
7265
7266     
7267     
7268      // private
7269     fireKey : function(e){
7270         //Roo.log('field ' + e.getKey());
7271         if(e.isNavKeyPress()){
7272             this.fireEvent("specialkey", this, e);
7273         }
7274     },
7275     focus : function (selectText){
7276         if(this.rendered){
7277             this.inputEl().focus();
7278             if(selectText === true){
7279                 this.inputEl().dom.select();
7280             }
7281         }
7282         return this;
7283     } ,
7284     
7285     onFocus : function(){
7286         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7287            // this.el.addClass(this.focusClass);
7288         }
7289         if(!this.hasFocus){
7290             this.hasFocus = true;
7291             this.startValue = this.getValue();
7292             this.fireEvent("focus", this);
7293         }
7294     },
7295     
7296     beforeBlur : Roo.emptyFn,
7297
7298     
7299     // private
7300     onBlur : function(){
7301         this.beforeBlur();
7302         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7303             //this.el.removeClass(this.focusClass);
7304         }
7305         this.hasFocus = false;
7306         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7307             this.validate();
7308         }
7309         var v = this.getValue();
7310         if(String(v) !== String(this.startValue)){
7311             this.fireEvent('change', this, v, this.startValue);
7312         }
7313         this.fireEvent("blur", this);
7314     },
7315     
7316     /**
7317      * Resets the current field value to the originally loaded value and clears any validation messages
7318      */
7319     reset : function(){
7320         this.setValue(this.originalValue);
7321         this.clearInvalid();
7322     },
7323      /**
7324      * Returns the name of the field
7325      * @return {Mixed} name The name field
7326      */
7327     getName: function(){
7328         return this.name;
7329     },
7330      /**
7331      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7332      * @return {Mixed} value The field value
7333      */
7334     getValue : function(){
7335         
7336         var v = this.inputEl().getValue();
7337         
7338         return v;
7339     },
7340     /**
7341      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7342      * @return {Mixed} value The field value
7343      */
7344     getRawValue : function(){
7345         var v = this.inputEl().getValue();
7346         
7347         return v;
7348     },
7349     
7350     /**
7351      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7352      * @param {Mixed} value The value to set
7353      */
7354     setRawValue : function(v){
7355         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7356     },
7357     
7358     selectText : function(start, end){
7359         var v = this.getRawValue();
7360         if(v.length > 0){
7361             start = start === undefined ? 0 : start;
7362             end = end === undefined ? v.length : end;
7363             var d = this.inputEl().dom;
7364             if(d.setSelectionRange){
7365                 d.setSelectionRange(start, end);
7366             }else if(d.createTextRange){
7367                 var range = d.createTextRange();
7368                 range.moveStart("character", start);
7369                 range.moveEnd("character", v.length-end);
7370                 range.select();
7371             }
7372         }
7373     },
7374     
7375     /**
7376      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7377      * @param {Mixed} value The value to set
7378      */
7379     setValue : function(v){
7380         this.value = v;
7381         if(this.rendered){
7382             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7383             this.validate();
7384         }
7385     },
7386     
7387     /*
7388     processValue : function(value){
7389         if(this.stripCharsRe){
7390             var newValue = value.replace(this.stripCharsRe, '');
7391             if(newValue !== value){
7392                 this.setRawValue(newValue);
7393                 return newValue;
7394             }
7395         }
7396         return value;
7397     },
7398   */
7399     preFocus : function(){
7400         
7401         if(this.selectOnFocus){
7402             this.inputEl().dom.select();
7403         }
7404     },
7405     filterKeys : function(e){
7406         var k = e.getKey();
7407         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7408             return;
7409         }
7410         var c = e.getCharCode(), cc = String.fromCharCode(c);
7411         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7412             return;
7413         }
7414         if(!this.maskRe.test(cc)){
7415             e.stopEvent();
7416         }
7417     },
7418      /**
7419      * Clear any invalid styles/messages for this field
7420      */
7421     clearInvalid : function(){
7422         
7423         if(!this.el || this.preventMark){ // not rendered
7424             return;
7425         }
7426         this.el.removeClass(this.invalidClass);
7427         /*
7428         switch(this.msgTarget){
7429             case 'qtip':
7430                 this.el.dom.qtip = '';
7431                 break;
7432             case 'title':
7433                 this.el.dom.title = '';
7434                 break;
7435             case 'under':
7436                 if(this.errorEl){
7437                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7438                 }
7439                 break;
7440             case 'side':
7441                 if(this.errorIcon){
7442                     this.errorIcon.dom.qtip = '';
7443                     this.errorIcon.hide();
7444                     this.un('resize', this.alignErrorIcon, this);
7445                 }
7446                 break;
7447             default:
7448                 var t = Roo.getDom(this.msgTarget);
7449                 t.innerHTML = '';
7450                 t.style.display = 'none';
7451                 break;
7452         }
7453         */
7454         this.fireEvent('valid', this);
7455     },
7456      /**
7457      * Mark this field as invalid
7458      * @param {String} msg The validation message
7459      */
7460     markInvalid : function(msg){
7461         if(!this.el  || this.preventMark){ // not rendered
7462             return;
7463         }
7464         this.el.addClass(this.invalidClass);
7465         /*
7466         msg = msg || this.invalidText;
7467         switch(this.msgTarget){
7468             case 'qtip':
7469                 this.el.dom.qtip = msg;
7470                 this.el.dom.qclass = 'x-form-invalid-tip';
7471                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7472                     Roo.QuickTips.enable();
7473                 }
7474                 break;
7475             case 'title':
7476                 this.el.dom.title = msg;
7477                 break;
7478             case 'under':
7479                 if(!this.errorEl){
7480                     var elp = this.el.findParent('.x-form-element', 5, true);
7481                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7482                     this.errorEl.setWidth(elp.getWidth(true)-20);
7483                 }
7484                 this.errorEl.update(msg);
7485                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7486                 break;
7487             case 'side':
7488                 if(!this.errorIcon){
7489                     var elp = this.el.findParent('.x-form-element', 5, true);
7490                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7491                 }
7492                 this.alignErrorIcon();
7493                 this.errorIcon.dom.qtip = msg;
7494                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7495                 this.errorIcon.show();
7496                 this.on('resize', this.alignErrorIcon, this);
7497                 break;
7498             default:
7499                 var t = Roo.getDom(this.msgTarget);
7500                 t.innerHTML = msg;
7501                 t.style.display = this.msgDisplay;
7502                 break;
7503         }
7504         */
7505         this.fireEvent('invalid', this, msg);
7506     },
7507     // private
7508     SafariOnKeyDown : function(event)
7509     {
7510         // this is a workaround for a password hang bug on chrome/ webkit.
7511         
7512         var isSelectAll = false;
7513         
7514         if(this.inputEl().dom.selectionEnd > 0){
7515             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7516         }
7517         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7518             event.preventDefault();
7519             this.setValue('');
7520             return;
7521         }
7522         
7523         if(isSelectAll){ // backspace and delete key
7524             
7525             event.preventDefault();
7526             // this is very hacky as keydown always get's upper case.
7527             //
7528             var cc = String.fromCharCode(event.getCharCode());
7529             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7530             
7531         }
7532     },
7533     adjustWidth : function(tag, w){
7534         tag = tag.toLowerCase();
7535         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7536             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7537                 if(tag == 'input'){
7538                     return w + 2;
7539                 }
7540                 if(tag == 'textarea'){
7541                     return w-2;
7542                 }
7543             }else if(Roo.isOpera){
7544                 if(tag == 'input'){
7545                     return w + 2;
7546                 }
7547                 if(tag == 'textarea'){
7548                     return w-2;
7549                 }
7550             }
7551         }
7552         return w;
7553     }
7554     
7555 });
7556
7557  
7558 /*
7559  * - LGPL
7560  *
7561  * Input
7562  * 
7563  */
7564
7565 /**
7566  * @class Roo.bootstrap.TextArea
7567  * @extends Roo.bootstrap.Input
7568  * Bootstrap TextArea class
7569  * @cfg {Number} cols Specifies the visible width of a text area
7570  * @cfg {Number} rows Specifies the visible number of lines in a text area
7571  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7572  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7573  * @cfg {string} html text
7574  * 
7575  * @constructor
7576  * Create a new TextArea
7577  * @param {Object} config The config object
7578  */
7579
7580 Roo.bootstrap.TextArea = function(config){
7581     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7582    
7583 };
7584
7585 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7586      
7587     cols : false,
7588     rows : 5,
7589     readOnly : false,
7590     warp : 'soft',
7591     resize : false,
7592     value: false,
7593     html: false,
7594     
7595     getAutoCreate : function(){
7596         
7597         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7598         
7599         var id = Roo.id();
7600         
7601         var cfg = {};
7602         
7603         var input =  {
7604             tag: 'textarea',
7605             id : id,
7606             warp : this.warp,
7607             rows : this.rows,
7608             value : this.value || '',
7609             html: this.html || '',
7610             cls : 'form-control',
7611             placeholder : this.placeholder || '' 
7612             
7613         };
7614         
7615         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7616             input.maxLength = this.maxLength;
7617         }
7618         
7619         if(this.resize){
7620             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7621         }
7622         
7623         if(this.cols){
7624             input.cols = this.cols;
7625         }
7626         
7627         if (this.readOnly) {
7628             input.readonly = true;
7629         }
7630         
7631         if (this.name) {
7632             input.name = this.name;
7633         }
7634         
7635         if (this.size) {
7636             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7637         }
7638         
7639         var settings=this;
7640         ['xs','sm','md','lg'].map(function(size){
7641             if (settings[size]) {
7642                 cfg.cls += ' col-' + size + '-' + settings[size];
7643             }
7644         });
7645         
7646         var inputblock = input;
7647         
7648         if (this.before || this.after) {
7649             
7650             inputblock = {
7651                 cls : 'input-group',
7652                 cn :  [] 
7653             };
7654             if (this.before) {
7655                 inputblock.cn.push({
7656                     tag :'span',
7657                     cls : 'input-group-addon',
7658                     html : this.before
7659                 });
7660             }
7661             inputblock.cn.push(input);
7662             if (this.after) {
7663                 inputblock.cn.push({
7664                     tag :'span',
7665                     cls : 'input-group-addon',
7666                     html : this.after
7667                 });
7668             }
7669             
7670         }
7671         
7672         if (align ==='left' && this.fieldLabel.length) {
7673                 Roo.log("left and has label");
7674                 cfg.cn = [
7675                     
7676                     {
7677                         tag: 'label',
7678                         'for' :  id,
7679                         cls : 'control-label col-sm-' + this.labelWidth,
7680                         html : this.fieldLabel
7681                         
7682                     },
7683                     {
7684                         cls : "col-sm-" + (12 - this.labelWidth), 
7685                         cn: [
7686                             inputblock
7687                         ]
7688                     }
7689                     
7690                 ];
7691         } else if ( this.fieldLabel.length) {
7692                 Roo.log(" label");
7693                  cfg.cn = [
7694                    
7695                     {
7696                         tag: 'label',
7697                         //cls : 'input-group-addon',
7698                         html : this.fieldLabel
7699                         
7700                     },
7701                     
7702                     inputblock
7703                     
7704                 ];
7705
7706         } else {
7707             
7708                    Roo.log(" no label && no align");
7709                 cfg.cn = [
7710                     
7711                         inputblock
7712                     
7713                 ];
7714                 
7715                 
7716         }
7717         
7718         if (this.disabled) {
7719             input.disabled=true;
7720         }
7721         
7722         return cfg;
7723         
7724     },
7725     /**
7726      * return the real textarea element.
7727      */
7728     inputEl: function ()
7729     {
7730         return this.el.select('textarea.form-control',true).first();
7731     }
7732 });
7733
7734  
7735 /*
7736  * - LGPL
7737  *
7738  * trigger field - base class for combo..
7739  * 
7740  */
7741  
7742 /**
7743  * @class Roo.bootstrap.TriggerField
7744  * @extends Roo.bootstrap.Input
7745  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7746  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7747  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7748  * for which you can provide a custom implementation.  For example:
7749  * <pre><code>
7750 var trigger = new Roo.bootstrap.TriggerField();
7751 trigger.onTriggerClick = myTriggerFn;
7752 trigger.applyTo('my-field');
7753 </code></pre>
7754  *
7755  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7756  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7757  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7758  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7759  * @constructor
7760  * Create a new TriggerField.
7761  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7762  * to the base TextField)
7763  */
7764 Roo.bootstrap.TriggerField = function(config){
7765     this.mimicing = false;
7766     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7767 };
7768
7769 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7770     /**
7771      * @cfg {String} triggerClass A CSS class to apply to the trigger
7772      */
7773      /**
7774      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7775      */
7776     hideTrigger:false,
7777
7778     /** @cfg {Boolean} grow @hide */
7779     /** @cfg {Number} growMin @hide */
7780     /** @cfg {Number} growMax @hide */
7781
7782     /**
7783      * @hide 
7784      * @method
7785      */
7786     autoSize: Roo.emptyFn,
7787     // private
7788     monitorTab : true,
7789     // private
7790     deferHeight : true,
7791
7792     
7793     actionMode : 'wrap',
7794     
7795     
7796     
7797     getAutoCreate : function(){
7798        
7799         var align = this.labelAlign || this.parentLabelAlign();
7800         
7801         var id = Roo.id();
7802         
7803         var cfg = {
7804             cls: 'form-group' //input-group
7805         };
7806         
7807         
7808         var input =  {
7809             tag: 'input',
7810             id : id,
7811             type : this.inputType,
7812             cls : 'form-control',
7813             autocomplete: 'off',
7814             placeholder : this.placeholder || '' 
7815             
7816         };
7817         if (this.name) {
7818             input.name = this.name;
7819         }
7820         if (this.size) {
7821             input.cls += ' input-' + this.size;
7822         }
7823         
7824         if (this.disabled) {
7825             input.disabled=true;
7826         }
7827         
7828         var inputblock = input;
7829         
7830         if (this.before || this.after) {
7831             
7832             inputblock = {
7833                 cls : 'input-group',
7834                 cn :  [] 
7835             };
7836             if (this.before) {
7837                 inputblock.cn.push({
7838                     tag :'span',
7839                     cls : 'input-group-addon',
7840                     html : this.before
7841                 });
7842             }
7843             inputblock.cn.push(input);
7844             if (this.after) {
7845                 inputblock.cn.push({
7846                     tag :'span',
7847                     cls : 'input-group-addon',
7848                     html : this.after
7849                 });
7850             }
7851             
7852         };
7853         
7854         var box = {
7855             tag: 'div',
7856             cn: [
7857                 {
7858                     tag: 'input',
7859                     type : 'hidden',
7860                     cls: 'form-hidden-field'
7861                 },
7862                 inputblock
7863             ]
7864             
7865         };
7866         
7867         if(this.multiple){
7868             Roo.log('multiple');
7869             
7870             box = {
7871                 tag: 'div',
7872                 cn: [
7873                     {
7874                         tag: 'input',
7875                         type : 'hidden',
7876                         cls: 'form-hidden-field'
7877                     },
7878                     {
7879                         tag: 'ul',
7880                         cls: 'select2-choices',
7881                         cn:[
7882                             {
7883                                 tag: 'li',
7884                                 cls: 'select2-search-field',
7885                                 cn: [
7886
7887                                     inputblock
7888                                 ]
7889                             }
7890                         ]
7891                     }
7892                 ]
7893             }
7894         };
7895         
7896         var combobox = {
7897             cls: 'select2-container input-group',
7898             cn: [
7899                 box
7900 //                {
7901 //                    tag: 'ul',
7902 //                    cls: 'typeahead typeahead-long dropdown-menu',
7903 //                    style: 'display:none'
7904 //                }
7905             ]
7906         };
7907         
7908         if(!this.multiple && this.showToggleBtn){
7909             combobox.cn.push({
7910                 tag :'span',
7911                 cls : 'input-group-addon btn dropdown-toggle',
7912                 cn : [
7913                     {
7914                         tag: 'span',
7915                         cls: 'caret'
7916                     },
7917                     {
7918                         tag: 'span',
7919                         cls: 'combobox-clear',
7920                         cn  : [
7921                             {
7922                                 tag : 'i',
7923                                 cls: 'icon-remove'
7924                             }
7925                         ]
7926                     }
7927                 ]
7928
7929             })
7930         }
7931         
7932         if(this.multiple){
7933             combobox.cls += ' select2-container-multi';
7934         }
7935         
7936         if (align ==='left' && this.fieldLabel.length) {
7937             
7938                 Roo.log("left and has label");
7939                 cfg.cn = [
7940                     
7941                     {
7942                         tag: 'label',
7943                         'for' :  id,
7944                         cls : 'control-label col-sm-' + this.labelWidth,
7945                         html : this.fieldLabel
7946                         
7947                     },
7948                     {
7949                         cls : "col-sm-" + (12 - this.labelWidth), 
7950                         cn: [
7951                             combobox
7952                         ]
7953                     }
7954                     
7955                 ];
7956         } else if ( this.fieldLabel.length) {
7957                 Roo.log(" label");
7958                  cfg.cn = [
7959                    
7960                     {
7961                         tag: 'label',
7962                         //cls : 'input-group-addon',
7963                         html : this.fieldLabel
7964                         
7965                     },
7966                     
7967                     combobox
7968                     
7969                 ];
7970
7971         } else {
7972             
7973                 Roo.log(" no label && no align");
7974                 cfg = combobox
7975                      
7976                 
7977         }
7978          
7979         var settings=this;
7980         ['xs','sm','md','lg'].map(function(size){
7981             if (settings[size]) {
7982                 cfg.cls += ' col-' + size + '-' + settings[size];
7983             }
7984         });
7985         
7986         return cfg;
7987         
7988     },
7989     
7990     
7991     
7992     // private
7993     onResize : function(w, h){
7994 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7995 //        if(typeof w == 'number'){
7996 //            var x = w - this.trigger.getWidth();
7997 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7998 //            this.trigger.setStyle('left', x+'px');
7999 //        }
8000     },
8001
8002     // private
8003     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8004
8005     // private
8006     getResizeEl : function(){
8007         return this.inputEl();
8008     },
8009
8010     // private
8011     getPositionEl : function(){
8012         return this.inputEl();
8013     },
8014
8015     // private
8016     alignErrorIcon : function(){
8017         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8018     },
8019
8020     // private
8021     initEvents : function(){
8022         
8023         this.createList();
8024         
8025         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8026         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8027         if(!this.multiple && this.showToggleBtn){
8028             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8029             if(this.hideTrigger){
8030                 this.trigger.setDisplayed(false);
8031             }
8032             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8033         }
8034         
8035         if(this.multiple){
8036             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8037         }
8038         
8039         //this.trigger.addClassOnOver('x-form-trigger-over');
8040         //this.trigger.addClassOnClick('x-form-trigger-click');
8041         
8042         //if(!this.width){
8043         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8044         //}
8045     },
8046     
8047     createList : function()
8048     {
8049         this.list = Roo.get(document.body).createChild({
8050             tag: 'ul',
8051             cls: 'typeahead typeahead-long dropdown-menu',
8052             style: 'display:none'
8053         });
8054         
8055         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8056         
8057     },
8058
8059     // private
8060     initTrigger : function(){
8061        
8062     },
8063
8064     // private
8065     onDestroy : function(){
8066         if(this.trigger){
8067             this.trigger.removeAllListeners();
8068           //  this.trigger.remove();
8069         }
8070         //if(this.wrap){
8071         //    this.wrap.remove();
8072         //}
8073         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8074     },
8075
8076     // private
8077     onFocus : function(){
8078         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8079         /*
8080         if(!this.mimicing){
8081             this.wrap.addClass('x-trigger-wrap-focus');
8082             this.mimicing = true;
8083             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8084             if(this.monitorTab){
8085                 this.el.on("keydown", this.checkTab, this);
8086             }
8087         }
8088         */
8089     },
8090
8091     // private
8092     checkTab : function(e){
8093         if(e.getKey() == e.TAB){
8094             this.triggerBlur();
8095         }
8096     },
8097
8098     // private
8099     onBlur : function(){
8100         // do nothing
8101     },
8102
8103     // private
8104     mimicBlur : function(e, t){
8105         /*
8106         if(!this.wrap.contains(t) && this.validateBlur()){
8107             this.triggerBlur();
8108         }
8109         */
8110     },
8111
8112     // private
8113     triggerBlur : function(){
8114         this.mimicing = false;
8115         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8116         if(this.monitorTab){
8117             this.el.un("keydown", this.checkTab, this);
8118         }
8119         //this.wrap.removeClass('x-trigger-wrap-focus');
8120         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8121     },
8122
8123     // private
8124     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8125     validateBlur : function(e, t){
8126         return true;
8127     },
8128
8129     // private
8130     onDisable : function(){
8131         this.inputEl().dom.disabled = true;
8132         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8133         //if(this.wrap){
8134         //    this.wrap.addClass('x-item-disabled');
8135         //}
8136     },
8137
8138     // private
8139     onEnable : function(){
8140         this.inputEl().dom.disabled = false;
8141         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8142         //if(this.wrap){
8143         //    this.el.removeClass('x-item-disabled');
8144         //}
8145     },
8146
8147     // private
8148     onShow : function(){
8149         var ae = this.getActionEl();
8150         
8151         if(ae){
8152             ae.dom.style.display = '';
8153             ae.dom.style.visibility = 'visible';
8154         }
8155     },
8156
8157     // private
8158     
8159     onHide : function(){
8160         var ae = this.getActionEl();
8161         ae.dom.style.display = 'none';
8162     },
8163
8164     /**
8165      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8166      * by an implementing function.
8167      * @method
8168      * @param {EventObject} e
8169      */
8170     onTriggerClick : Roo.emptyFn
8171 });
8172  /*
8173  * Based on:
8174  * Ext JS Library 1.1.1
8175  * Copyright(c) 2006-2007, Ext JS, LLC.
8176  *
8177  * Originally Released Under LGPL - original licence link has changed is not relivant.
8178  *
8179  * Fork - LGPL
8180  * <script type="text/javascript">
8181  */
8182
8183
8184 /**
8185  * @class Roo.data.SortTypes
8186  * @singleton
8187  * Defines the default sorting (casting?) comparison functions used when sorting data.
8188  */
8189 Roo.data.SortTypes = {
8190     /**
8191      * Default sort that does nothing
8192      * @param {Mixed} s The value being converted
8193      * @return {Mixed} The comparison value
8194      */
8195     none : function(s){
8196         return s;
8197     },
8198     
8199     /**
8200      * The regular expression used to strip tags
8201      * @type {RegExp}
8202      * @property
8203      */
8204     stripTagsRE : /<\/?[^>]+>/gi,
8205     
8206     /**
8207      * Strips all HTML tags to sort on text only
8208      * @param {Mixed} s The value being converted
8209      * @return {String} The comparison value
8210      */
8211     asText : function(s){
8212         return String(s).replace(this.stripTagsRE, "");
8213     },
8214     
8215     /**
8216      * Strips all HTML tags to sort on text only - Case insensitive
8217      * @param {Mixed} s The value being converted
8218      * @return {String} The comparison value
8219      */
8220     asUCText : function(s){
8221         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8222     },
8223     
8224     /**
8225      * Case insensitive string
8226      * @param {Mixed} s The value being converted
8227      * @return {String} The comparison value
8228      */
8229     asUCString : function(s) {
8230         return String(s).toUpperCase();
8231     },
8232     
8233     /**
8234      * Date sorting
8235      * @param {Mixed} s The value being converted
8236      * @return {Number} The comparison value
8237      */
8238     asDate : function(s) {
8239         if(!s){
8240             return 0;
8241         }
8242         if(s instanceof Date){
8243             return s.getTime();
8244         }
8245         return Date.parse(String(s));
8246     },
8247     
8248     /**
8249      * Float sorting
8250      * @param {Mixed} s The value being converted
8251      * @return {Float} The comparison value
8252      */
8253     asFloat : function(s) {
8254         var val = parseFloat(String(s).replace(/,/g, ""));
8255         if(isNaN(val)) val = 0;
8256         return val;
8257     },
8258     
8259     /**
8260      * Integer sorting
8261      * @param {Mixed} s The value being converted
8262      * @return {Number} The comparison value
8263      */
8264     asInt : function(s) {
8265         var val = parseInt(String(s).replace(/,/g, ""));
8266         if(isNaN(val)) val = 0;
8267         return val;
8268     }
8269 };/*
8270  * Based on:
8271  * Ext JS Library 1.1.1
8272  * Copyright(c) 2006-2007, Ext JS, LLC.
8273  *
8274  * Originally Released Under LGPL - original licence link has changed is not relivant.
8275  *
8276  * Fork - LGPL
8277  * <script type="text/javascript">
8278  */
8279
8280 /**
8281 * @class Roo.data.Record
8282  * Instances of this class encapsulate both record <em>definition</em> information, and record
8283  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8284  * to access Records cached in an {@link Roo.data.Store} object.<br>
8285  * <p>
8286  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8287  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8288  * objects.<br>
8289  * <p>
8290  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8291  * @constructor
8292  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8293  * {@link #create}. The parameters are the same.
8294  * @param {Array} data An associative Array of data values keyed by the field name.
8295  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8296  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8297  * not specified an integer id is generated.
8298  */
8299 Roo.data.Record = function(data, id){
8300     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8301     this.data = data;
8302 };
8303
8304 /**
8305  * Generate a constructor for a specific record layout.
8306  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8307  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8308  * Each field definition object may contain the following properties: <ul>
8309  * <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,
8310  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8311  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8312  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8313  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8314  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8315  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8316  * this may be omitted.</p></li>
8317  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8318  * <ul><li>auto (Default, implies no conversion)</li>
8319  * <li>string</li>
8320  * <li>int</li>
8321  * <li>float</li>
8322  * <li>boolean</li>
8323  * <li>date</li></ul></p></li>
8324  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8325  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8326  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8327  * by the Reader into an object that will be stored in the Record. It is passed the
8328  * following parameters:<ul>
8329  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8330  * </ul></p></li>
8331  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8332  * </ul>
8333  * <br>usage:<br><pre><code>
8334 var TopicRecord = Roo.data.Record.create(
8335     {name: 'title', mapping: 'topic_title'},
8336     {name: 'author', mapping: 'username'},
8337     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8338     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8339     {name: 'lastPoster', mapping: 'user2'},
8340     {name: 'excerpt', mapping: 'post_text'}
8341 );
8342
8343 var myNewRecord = new TopicRecord({
8344     title: 'Do my job please',
8345     author: 'noobie',
8346     totalPosts: 1,
8347     lastPost: new Date(),
8348     lastPoster: 'Animal',
8349     excerpt: 'No way dude!'
8350 });
8351 myStore.add(myNewRecord);
8352 </code></pre>
8353  * @method create
8354  * @static
8355  */
8356 Roo.data.Record.create = function(o){
8357     var f = function(){
8358         f.superclass.constructor.apply(this, arguments);
8359     };
8360     Roo.extend(f, Roo.data.Record);
8361     var p = f.prototype;
8362     p.fields = new Roo.util.MixedCollection(false, function(field){
8363         return field.name;
8364     });
8365     for(var i = 0, len = o.length; i < len; i++){
8366         p.fields.add(new Roo.data.Field(o[i]));
8367     }
8368     f.getField = function(name){
8369         return p.fields.get(name);  
8370     };
8371     return f;
8372 };
8373
8374 Roo.data.Record.AUTO_ID = 1000;
8375 Roo.data.Record.EDIT = 'edit';
8376 Roo.data.Record.REJECT = 'reject';
8377 Roo.data.Record.COMMIT = 'commit';
8378
8379 Roo.data.Record.prototype = {
8380     /**
8381      * Readonly flag - true if this record has been modified.
8382      * @type Boolean
8383      */
8384     dirty : false,
8385     editing : false,
8386     error: null,
8387     modified: null,
8388
8389     // private
8390     join : function(store){
8391         this.store = store;
8392     },
8393
8394     /**
8395      * Set the named field to the specified value.
8396      * @param {String} name The name of the field to set.
8397      * @param {Object} value The value to set the field to.
8398      */
8399     set : function(name, value){
8400         if(this.data[name] == value){
8401             return;
8402         }
8403         this.dirty = true;
8404         if(!this.modified){
8405             this.modified = {};
8406         }
8407         if(typeof this.modified[name] == 'undefined'){
8408             this.modified[name] = this.data[name];
8409         }
8410         this.data[name] = value;
8411         if(!this.editing && this.store){
8412             this.store.afterEdit(this);
8413         }       
8414     },
8415
8416     /**
8417      * Get the value of the named field.
8418      * @param {String} name The name of the field to get the value of.
8419      * @return {Object} The value of the field.
8420      */
8421     get : function(name){
8422         return this.data[name]; 
8423     },
8424
8425     // private
8426     beginEdit : function(){
8427         this.editing = true;
8428         this.modified = {}; 
8429     },
8430
8431     // private
8432     cancelEdit : function(){
8433         this.editing = false;
8434         delete this.modified;
8435     },
8436
8437     // private
8438     endEdit : function(){
8439         this.editing = false;
8440         if(this.dirty && this.store){
8441             this.store.afterEdit(this);
8442         }
8443     },
8444
8445     /**
8446      * Usually called by the {@link Roo.data.Store} which owns the Record.
8447      * Rejects all changes made to the Record since either creation, or the last commit operation.
8448      * Modified fields are reverted to their original values.
8449      * <p>
8450      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8451      * of reject operations.
8452      */
8453     reject : function(){
8454         var m = this.modified;
8455         for(var n in m){
8456             if(typeof m[n] != "function"){
8457                 this.data[n] = m[n];
8458             }
8459         }
8460         this.dirty = false;
8461         delete this.modified;
8462         this.editing = false;
8463         if(this.store){
8464             this.store.afterReject(this);
8465         }
8466     },
8467
8468     /**
8469      * Usually called by the {@link Roo.data.Store} which owns the Record.
8470      * Commits all changes made to the Record since either creation, or the last commit operation.
8471      * <p>
8472      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8473      * of commit operations.
8474      */
8475     commit : function(){
8476         this.dirty = false;
8477         delete this.modified;
8478         this.editing = false;
8479         if(this.store){
8480             this.store.afterCommit(this);
8481         }
8482     },
8483
8484     // private
8485     hasError : function(){
8486         return this.error != null;
8487     },
8488
8489     // private
8490     clearError : function(){
8491         this.error = null;
8492     },
8493
8494     /**
8495      * Creates a copy of this record.
8496      * @param {String} id (optional) A new record id if you don't want to use this record's id
8497      * @return {Record}
8498      */
8499     copy : function(newId) {
8500         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8501     }
8502 };/*
8503  * Based on:
8504  * Ext JS Library 1.1.1
8505  * Copyright(c) 2006-2007, Ext JS, LLC.
8506  *
8507  * Originally Released Under LGPL - original licence link has changed is not relivant.
8508  *
8509  * Fork - LGPL
8510  * <script type="text/javascript">
8511  */
8512
8513
8514
8515 /**
8516  * @class Roo.data.Store
8517  * @extends Roo.util.Observable
8518  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8519  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8520  * <p>
8521  * 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
8522  * has no knowledge of the format of the data returned by the Proxy.<br>
8523  * <p>
8524  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8525  * instances from the data object. These records are cached and made available through accessor functions.
8526  * @constructor
8527  * Creates a new Store.
8528  * @param {Object} config A config object containing the objects needed for the Store to access data,
8529  * and read the data into Records.
8530  */
8531 Roo.data.Store = function(config){
8532     this.data = new Roo.util.MixedCollection(false);
8533     this.data.getKey = function(o){
8534         return o.id;
8535     };
8536     this.baseParams = {};
8537     // private
8538     this.paramNames = {
8539         "start" : "start",
8540         "limit" : "limit",
8541         "sort" : "sort",
8542         "dir" : "dir",
8543         "multisort" : "_multisort"
8544     };
8545
8546     if(config && config.data){
8547         this.inlineData = config.data;
8548         delete config.data;
8549     }
8550
8551     Roo.apply(this, config);
8552     
8553     if(this.reader){ // reader passed
8554         this.reader = Roo.factory(this.reader, Roo.data);
8555         this.reader.xmodule = this.xmodule || false;
8556         if(!this.recordType){
8557             this.recordType = this.reader.recordType;
8558         }
8559         if(this.reader.onMetaChange){
8560             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8561         }
8562     }
8563
8564     if(this.recordType){
8565         this.fields = this.recordType.prototype.fields;
8566     }
8567     this.modified = [];
8568
8569     this.addEvents({
8570         /**
8571          * @event datachanged
8572          * Fires when the data cache has changed, and a widget which is using this Store
8573          * as a Record cache should refresh its view.
8574          * @param {Store} this
8575          */
8576         datachanged : true,
8577         /**
8578          * @event metachange
8579          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8580          * @param {Store} this
8581          * @param {Object} meta The JSON metadata
8582          */
8583         metachange : true,
8584         /**
8585          * @event add
8586          * Fires when Records have been added to the Store
8587          * @param {Store} this
8588          * @param {Roo.data.Record[]} records The array of Records added
8589          * @param {Number} index The index at which the record(s) were added
8590          */
8591         add : true,
8592         /**
8593          * @event remove
8594          * Fires when a Record has been removed from the Store
8595          * @param {Store} this
8596          * @param {Roo.data.Record} record The Record that was removed
8597          * @param {Number} index The index at which the record was removed
8598          */
8599         remove : true,
8600         /**
8601          * @event update
8602          * Fires when a Record has been updated
8603          * @param {Store} this
8604          * @param {Roo.data.Record} record The Record that was updated
8605          * @param {String} operation The update operation being performed.  Value may be one of:
8606          * <pre><code>
8607  Roo.data.Record.EDIT
8608  Roo.data.Record.REJECT
8609  Roo.data.Record.COMMIT
8610          * </code></pre>
8611          */
8612         update : true,
8613         /**
8614          * @event clear
8615          * Fires when the data cache has been cleared.
8616          * @param {Store} this
8617          */
8618         clear : true,
8619         /**
8620          * @event beforeload
8621          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8622          * the load action will be canceled.
8623          * @param {Store} this
8624          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8625          */
8626         beforeload : true,
8627         /**
8628          * @event beforeloadadd
8629          * Fires after a new set of Records has been loaded.
8630          * @param {Store} this
8631          * @param {Roo.data.Record[]} records The Records that were loaded
8632          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8633          */
8634         beforeloadadd : true,
8635         /**
8636          * @event load
8637          * Fires after a new set of Records has been loaded, before they are added to the store.
8638          * @param {Store} this
8639          * @param {Roo.data.Record[]} records The Records that were loaded
8640          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8641          * @params {Object} return from reader
8642          */
8643         load : true,
8644         /**
8645          * @event loadexception
8646          * Fires if an exception occurs in the Proxy during loading.
8647          * Called with the signature of the Proxy's "loadexception" event.
8648          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8649          * 
8650          * @param {Proxy} 
8651          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8652          * @param {Object} load options 
8653          * @param {Object} jsonData from your request (normally this contains the Exception)
8654          */
8655         loadexception : true
8656     });
8657     
8658     if(this.proxy){
8659         this.proxy = Roo.factory(this.proxy, Roo.data);
8660         this.proxy.xmodule = this.xmodule || false;
8661         this.relayEvents(this.proxy,  ["loadexception"]);
8662     }
8663     this.sortToggle = {};
8664     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8665
8666     Roo.data.Store.superclass.constructor.call(this);
8667
8668     if(this.inlineData){
8669         this.loadData(this.inlineData);
8670         delete this.inlineData;
8671     }
8672 };
8673
8674 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8675      /**
8676     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8677     * without a remote query - used by combo/forms at present.
8678     */
8679     
8680     /**
8681     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8682     */
8683     /**
8684     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8685     */
8686     /**
8687     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8688     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8689     */
8690     /**
8691     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8692     * on any HTTP request
8693     */
8694     /**
8695     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8696     */
8697     /**
8698     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8699     */
8700     multiSort: false,
8701     /**
8702     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8703     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8704     */
8705     remoteSort : false,
8706
8707     /**
8708     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8709      * loaded or when a record is removed. (defaults to false).
8710     */
8711     pruneModifiedRecords : false,
8712
8713     // private
8714     lastOptions : null,
8715
8716     /**
8717      * Add Records to the Store and fires the add event.
8718      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8719      */
8720     add : function(records){
8721         records = [].concat(records);
8722         for(var i = 0, len = records.length; i < len; i++){
8723             records[i].join(this);
8724         }
8725         var index = this.data.length;
8726         this.data.addAll(records);
8727         this.fireEvent("add", this, records, index);
8728     },
8729
8730     /**
8731      * Remove a Record from the Store and fires the remove event.
8732      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8733      */
8734     remove : function(record){
8735         var index = this.data.indexOf(record);
8736         this.data.removeAt(index);
8737         if(this.pruneModifiedRecords){
8738             this.modified.remove(record);
8739         }
8740         this.fireEvent("remove", this, record, index);
8741     },
8742
8743     /**
8744      * Remove all Records from the Store and fires the clear event.
8745      */
8746     removeAll : function(){
8747         this.data.clear();
8748         if(this.pruneModifiedRecords){
8749             this.modified = [];
8750         }
8751         this.fireEvent("clear", this);
8752     },
8753
8754     /**
8755      * Inserts Records to the Store at the given index and fires the add event.
8756      * @param {Number} index The start index at which to insert the passed Records.
8757      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8758      */
8759     insert : function(index, records){
8760         records = [].concat(records);
8761         for(var i = 0, len = records.length; i < len; i++){
8762             this.data.insert(index, records[i]);
8763             records[i].join(this);
8764         }
8765         this.fireEvent("add", this, records, index);
8766     },
8767
8768     /**
8769      * Get the index within the cache of the passed Record.
8770      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8771      * @return {Number} The index of the passed Record. Returns -1 if not found.
8772      */
8773     indexOf : function(record){
8774         return this.data.indexOf(record);
8775     },
8776
8777     /**
8778      * Get the index within the cache of the Record with the passed id.
8779      * @param {String} id The id of the Record to find.
8780      * @return {Number} The index of the Record. Returns -1 if not found.
8781      */
8782     indexOfId : function(id){
8783         return this.data.indexOfKey(id);
8784     },
8785
8786     /**
8787      * Get the Record with the specified id.
8788      * @param {String} id The id of the Record to find.
8789      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8790      */
8791     getById : function(id){
8792         return this.data.key(id);
8793     },
8794
8795     /**
8796      * Get the Record at the specified index.
8797      * @param {Number} index The index of the Record to find.
8798      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8799      */
8800     getAt : function(index){
8801         return this.data.itemAt(index);
8802     },
8803
8804     /**
8805      * Returns a range of Records between specified indices.
8806      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8807      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8808      * @return {Roo.data.Record[]} An array of Records
8809      */
8810     getRange : function(start, end){
8811         return this.data.getRange(start, end);
8812     },
8813
8814     // private
8815     storeOptions : function(o){
8816         o = Roo.apply({}, o);
8817         delete o.callback;
8818         delete o.scope;
8819         this.lastOptions = o;
8820     },
8821
8822     /**
8823      * Loads the Record cache from the configured Proxy using the configured Reader.
8824      * <p>
8825      * If using remote paging, then the first load call must specify the <em>start</em>
8826      * and <em>limit</em> properties in the options.params property to establish the initial
8827      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8828      * <p>
8829      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8830      * and this call will return before the new data has been loaded. Perform any post-processing
8831      * in a callback function, or in a "load" event handler.</strong>
8832      * <p>
8833      * @param {Object} options An object containing properties which control loading options:<ul>
8834      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8835      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8836      * passed the following arguments:<ul>
8837      * <li>r : Roo.data.Record[]</li>
8838      * <li>options: Options object from the load call</li>
8839      * <li>success: Boolean success indicator</li></ul></li>
8840      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8841      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8842      * </ul>
8843      */
8844     load : function(options){
8845         options = options || {};
8846         if(this.fireEvent("beforeload", this, options) !== false){
8847             this.storeOptions(options);
8848             var p = Roo.apply(options.params || {}, this.baseParams);
8849             // if meta was not loaded from remote source.. try requesting it.
8850             if (!this.reader.metaFromRemote) {
8851                 p._requestMeta = 1;
8852             }
8853             if(this.sortInfo && this.remoteSort){
8854                 var pn = this.paramNames;
8855                 p[pn["sort"]] = this.sortInfo.field;
8856                 p[pn["dir"]] = this.sortInfo.direction;
8857             }
8858             if (this.multiSort) {
8859                 var pn = this.paramNames;
8860                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8861             }
8862             
8863             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8864         }
8865     },
8866
8867     /**
8868      * Reloads the Record cache from the configured Proxy using the configured Reader and
8869      * the options from the last load operation performed.
8870      * @param {Object} options (optional) An object containing properties which may override the options
8871      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8872      * the most recently used options are reused).
8873      */
8874     reload : function(options){
8875         this.load(Roo.applyIf(options||{}, this.lastOptions));
8876     },
8877
8878     // private
8879     // Called as a callback by the Reader during a load operation.
8880     loadRecords : function(o, options, success){
8881         if(!o || success === false){
8882             if(success !== false){
8883                 this.fireEvent("load", this, [], options, o);
8884             }
8885             if(options.callback){
8886                 options.callback.call(options.scope || this, [], options, false);
8887             }
8888             return;
8889         }
8890         // if data returned failure - throw an exception.
8891         if (o.success === false) {
8892             // show a message if no listener is registered.
8893             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8894                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8895             }
8896             // loadmask wil be hooked into this..
8897             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8898             return;
8899         }
8900         var r = o.records, t = o.totalRecords || r.length;
8901         
8902         this.fireEvent("beforeloadadd", this, r, options, o);
8903         
8904         if(!options || options.add !== true){
8905             if(this.pruneModifiedRecords){
8906                 this.modified = [];
8907             }
8908             for(var i = 0, len = r.length; i < len; i++){
8909                 r[i].join(this);
8910             }
8911             if(this.snapshot){
8912                 this.data = this.snapshot;
8913                 delete this.snapshot;
8914             }
8915             this.data.clear();
8916             this.data.addAll(r);
8917             this.totalLength = t;
8918             this.applySort();
8919             this.fireEvent("datachanged", this);
8920         }else{
8921             this.totalLength = Math.max(t, this.data.length+r.length);
8922             this.add(r);
8923         }
8924         this.fireEvent("load", this, r, options, o);
8925         if(options.callback){
8926             options.callback.call(options.scope || this, r, options, true);
8927         }
8928     },
8929
8930
8931     /**
8932      * Loads data from a passed data block. A Reader which understands the format of the data
8933      * must have been configured in the constructor.
8934      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8935      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8936      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8937      */
8938     loadData : function(o, append){
8939         var r = this.reader.readRecords(o);
8940         this.loadRecords(r, {add: append}, true);
8941     },
8942
8943     /**
8944      * Gets the number of cached records.
8945      * <p>
8946      * <em>If using paging, this may not be the total size of the dataset. If the data object
8947      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8948      * the data set size</em>
8949      */
8950     getCount : function(){
8951         return this.data.length || 0;
8952     },
8953
8954     /**
8955      * Gets the total number of records in the dataset as returned by the server.
8956      * <p>
8957      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8958      * the dataset size</em>
8959      */
8960     getTotalCount : function(){
8961         return this.totalLength || 0;
8962     },
8963
8964     /**
8965      * Returns the sort state of the Store as an object with two properties:
8966      * <pre><code>
8967  field {String} The name of the field by which the Records are sorted
8968  direction {String} The sort order, "ASC" or "DESC"
8969      * </code></pre>
8970      */
8971     getSortState : function(){
8972         return this.sortInfo;
8973     },
8974
8975     // private
8976     applySort : function(){
8977         if(this.sortInfo && !this.remoteSort){
8978             var s = this.sortInfo, f = s.field;
8979             var st = this.fields.get(f).sortType;
8980             var fn = function(r1, r2){
8981                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8982                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8983             };
8984             this.data.sort(s.direction, fn);
8985             if(this.snapshot && this.snapshot != this.data){
8986                 this.snapshot.sort(s.direction, fn);
8987             }
8988         }
8989     },
8990
8991     /**
8992      * Sets the default sort column and order to be used by the next load operation.
8993      * @param {String} fieldName The name of the field to sort by.
8994      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8995      */
8996     setDefaultSort : function(field, dir){
8997         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8998     },
8999
9000     /**
9001      * Sort the Records.
9002      * If remote sorting is used, the sort is performed on the server, and the cache is
9003      * reloaded. If local sorting is used, the cache is sorted internally.
9004      * @param {String} fieldName The name of the field to sort by.
9005      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9006      */
9007     sort : function(fieldName, dir){
9008         var f = this.fields.get(fieldName);
9009         if(!dir){
9010             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9011             
9012             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9013                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9014             }else{
9015                 dir = f.sortDir;
9016             }
9017         }
9018         this.sortToggle[f.name] = dir;
9019         this.sortInfo = {field: f.name, direction: dir};
9020         if(!this.remoteSort){
9021             this.applySort();
9022             this.fireEvent("datachanged", this);
9023         }else{
9024             this.load(this.lastOptions);
9025         }
9026     },
9027
9028     /**
9029      * Calls the specified function for each of the Records in the cache.
9030      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9031      * Returning <em>false</em> aborts and exits the iteration.
9032      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9033      */
9034     each : function(fn, scope){
9035         this.data.each(fn, scope);
9036     },
9037
9038     /**
9039      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9040      * (e.g., during paging).
9041      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9042      */
9043     getModifiedRecords : function(){
9044         return this.modified;
9045     },
9046
9047     // private
9048     createFilterFn : function(property, value, anyMatch){
9049         if(!value.exec){ // not a regex
9050             value = String(value);
9051             if(value.length == 0){
9052                 return false;
9053             }
9054             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9055         }
9056         return function(r){
9057             return value.test(r.data[property]);
9058         };
9059     },
9060
9061     /**
9062      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9063      * @param {String} property A field on your records
9064      * @param {Number} start The record index to start at (defaults to 0)
9065      * @param {Number} end The last record index to include (defaults to length - 1)
9066      * @return {Number} The sum
9067      */
9068     sum : function(property, start, end){
9069         var rs = this.data.items, v = 0;
9070         start = start || 0;
9071         end = (end || end === 0) ? end : rs.length-1;
9072
9073         for(var i = start; i <= end; i++){
9074             v += (rs[i].data[property] || 0);
9075         }
9076         return v;
9077     },
9078
9079     /**
9080      * Filter the records by a specified property.
9081      * @param {String} field A field on your records
9082      * @param {String/RegExp} value Either a string that the field
9083      * should start with or a RegExp to test against the field
9084      * @param {Boolean} anyMatch True to match any part not just the beginning
9085      */
9086     filter : function(property, value, anyMatch){
9087         var fn = this.createFilterFn(property, value, anyMatch);
9088         return fn ? this.filterBy(fn) : this.clearFilter();
9089     },
9090
9091     /**
9092      * Filter by a function. The specified function will be called with each
9093      * record in this data source. If the function returns true the record is included,
9094      * otherwise it is filtered.
9095      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9096      * @param {Object} scope (optional) The scope of the function (defaults to this)
9097      */
9098     filterBy : function(fn, scope){
9099         this.snapshot = this.snapshot || this.data;
9100         this.data = this.queryBy(fn, scope||this);
9101         this.fireEvent("datachanged", this);
9102     },
9103
9104     /**
9105      * Query the records by a specified property.
9106      * @param {String} field A field on your records
9107      * @param {String/RegExp} value Either a string that the field
9108      * should start with or a RegExp to test against the field
9109      * @param {Boolean} anyMatch True to match any part not just the beginning
9110      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9111      */
9112     query : function(property, value, anyMatch){
9113         var fn = this.createFilterFn(property, value, anyMatch);
9114         return fn ? this.queryBy(fn) : this.data.clone();
9115     },
9116
9117     /**
9118      * Query by a function. The specified function will be called with each
9119      * record in this data source. If the function returns true the record is included
9120      * in the results.
9121      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9122      * @param {Object} scope (optional) The scope of the function (defaults to this)
9123       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9124      **/
9125     queryBy : function(fn, scope){
9126         var data = this.snapshot || this.data;
9127         return data.filterBy(fn, scope||this);
9128     },
9129
9130     /**
9131      * Collects unique values for a particular dataIndex from this store.
9132      * @param {String} dataIndex The property to collect
9133      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9134      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9135      * @return {Array} An array of the unique values
9136      **/
9137     collect : function(dataIndex, allowNull, bypassFilter){
9138         var d = (bypassFilter === true && this.snapshot) ?
9139                 this.snapshot.items : this.data.items;
9140         var v, sv, r = [], l = {};
9141         for(var i = 0, len = d.length; i < len; i++){
9142             v = d[i].data[dataIndex];
9143             sv = String(v);
9144             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9145                 l[sv] = true;
9146                 r[r.length] = v;
9147             }
9148         }
9149         return r;
9150     },
9151
9152     /**
9153      * Revert to a view of the Record cache with no filtering applied.
9154      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9155      */
9156     clearFilter : function(suppressEvent){
9157         if(this.snapshot && this.snapshot != this.data){
9158             this.data = this.snapshot;
9159             delete this.snapshot;
9160             if(suppressEvent !== true){
9161                 this.fireEvent("datachanged", this);
9162             }
9163         }
9164     },
9165
9166     // private
9167     afterEdit : function(record){
9168         if(this.modified.indexOf(record) == -1){
9169             this.modified.push(record);
9170         }
9171         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9172     },
9173     
9174     // private
9175     afterReject : function(record){
9176         this.modified.remove(record);
9177         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9178     },
9179
9180     // private
9181     afterCommit : function(record){
9182         this.modified.remove(record);
9183         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9184     },
9185
9186     /**
9187      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9188      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9189      */
9190     commitChanges : function(){
9191         var m = this.modified.slice(0);
9192         this.modified = [];
9193         for(var i = 0, len = m.length; i < len; i++){
9194             m[i].commit();
9195         }
9196     },
9197
9198     /**
9199      * Cancel outstanding changes on all changed records.
9200      */
9201     rejectChanges : function(){
9202         var m = this.modified.slice(0);
9203         this.modified = [];
9204         for(var i = 0, len = m.length; i < len; i++){
9205             m[i].reject();
9206         }
9207     },
9208
9209     onMetaChange : function(meta, rtype, o){
9210         this.recordType = rtype;
9211         this.fields = rtype.prototype.fields;
9212         delete this.snapshot;
9213         this.sortInfo = meta.sortInfo || this.sortInfo;
9214         this.modified = [];
9215         this.fireEvent('metachange', this, this.reader.meta);
9216     },
9217     
9218     moveIndex : function(data, type)
9219     {
9220         var index = this.indexOf(data);
9221         
9222         var newIndex = index + type;
9223         
9224         this.remove(data);
9225         
9226         this.insert(newIndex, data);
9227         
9228     }
9229 });/*
9230  * Based on:
9231  * Ext JS Library 1.1.1
9232  * Copyright(c) 2006-2007, Ext JS, LLC.
9233  *
9234  * Originally Released Under LGPL - original licence link has changed is not relivant.
9235  *
9236  * Fork - LGPL
9237  * <script type="text/javascript">
9238  */
9239
9240 /**
9241  * @class Roo.data.SimpleStore
9242  * @extends Roo.data.Store
9243  * Small helper class to make creating Stores from Array data easier.
9244  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9245  * @cfg {Array} fields An array of field definition objects, or field name strings.
9246  * @cfg {Array} data The multi-dimensional array of data
9247  * @constructor
9248  * @param {Object} config
9249  */
9250 Roo.data.SimpleStore = function(config){
9251     Roo.data.SimpleStore.superclass.constructor.call(this, {
9252         isLocal : true,
9253         reader: new Roo.data.ArrayReader({
9254                 id: config.id
9255             },
9256             Roo.data.Record.create(config.fields)
9257         ),
9258         proxy : new Roo.data.MemoryProxy(config.data)
9259     });
9260     this.load();
9261 };
9262 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9263  * Based on:
9264  * Ext JS Library 1.1.1
9265  * Copyright(c) 2006-2007, Ext JS, LLC.
9266  *
9267  * Originally Released Under LGPL - original licence link has changed is not relivant.
9268  *
9269  * Fork - LGPL
9270  * <script type="text/javascript">
9271  */
9272
9273 /**
9274 /**
9275  * @extends Roo.data.Store
9276  * @class Roo.data.JsonStore
9277  * Small helper class to make creating Stores for JSON data easier. <br/>
9278 <pre><code>
9279 var store = new Roo.data.JsonStore({
9280     url: 'get-images.php',
9281     root: 'images',
9282     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9283 });
9284 </code></pre>
9285  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9286  * JsonReader and HttpProxy (unless inline data is provided).</b>
9287  * @cfg {Array} fields An array of field definition objects, or field name strings.
9288  * @constructor
9289  * @param {Object} config
9290  */
9291 Roo.data.JsonStore = function(c){
9292     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9293         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9294         reader: new Roo.data.JsonReader(c, c.fields)
9295     }));
9296 };
9297 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9298  * Based on:
9299  * Ext JS Library 1.1.1
9300  * Copyright(c) 2006-2007, Ext JS, LLC.
9301  *
9302  * Originally Released Under LGPL - original licence link has changed is not relivant.
9303  *
9304  * Fork - LGPL
9305  * <script type="text/javascript">
9306  */
9307
9308  
9309 Roo.data.Field = function(config){
9310     if(typeof config == "string"){
9311         config = {name: config};
9312     }
9313     Roo.apply(this, config);
9314     
9315     if(!this.type){
9316         this.type = "auto";
9317     }
9318     
9319     var st = Roo.data.SortTypes;
9320     // named sortTypes are supported, here we look them up
9321     if(typeof this.sortType == "string"){
9322         this.sortType = st[this.sortType];
9323     }
9324     
9325     // set default sortType for strings and dates
9326     if(!this.sortType){
9327         switch(this.type){
9328             case "string":
9329                 this.sortType = st.asUCString;
9330                 break;
9331             case "date":
9332                 this.sortType = st.asDate;
9333                 break;
9334             default:
9335                 this.sortType = st.none;
9336         }
9337     }
9338
9339     // define once
9340     var stripRe = /[\$,%]/g;
9341
9342     // prebuilt conversion function for this field, instead of
9343     // switching every time we're reading a value
9344     if(!this.convert){
9345         var cv, dateFormat = this.dateFormat;
9346         switch(this.type){
9347             case "":
9348             case "auto":
9349             case undefined:
9350                 cv = function(v){ return v; };
9351                 break;
9352             case "string":
9353                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9354                 break;
9355             case "int":
9356                 cv = function(v){
9357                     return v !== undefined && v !== null && v !== '' ?
9358                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9359                     };
9360                 break;
9361             case "float":
9362                 cv = function(v){
9363                     return v !== undefined && v !== null && v !== '' ?
9364                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9365                     };
9366                 break;
9367             case "bool":
9368             case "boolean":
9369                 cv = function(v){ return v === true || v === "true" || v == 1; };
9370                 break;
9371             case "date":
9372                 cv = function(v){
9373                     if(!v){
9374                         return '';
9375                     }
9376                     if(v instanceof Date){
9377                         return v;
9378                     }
9379                     if(dateFormat){
9380                         if(dateFormat == "timestamp"){
9381                             return new Date(v*1000);
9382                         }
9383                         return Date.parseDate(v, dateFormat);
9384                     }
9385                     var parsed = Date.parse(v);
9386                     return parsed ? new Date(parsed) : null;
9387                 };
9388              break;
9389             
9390         }
9391         this.convert = cv;
9392     }
9393 };
9394
9395 Roo.data.Field.prototype = {
9396     dateFormat: null,
9397     defaultValue: "",
9398     mapping: null,
9399     sortType : null,
9400     sortDir : "ASC"
9401 };/*
9402  * Based on:
9403  * Ext JS Library 1.1.1
9404  * Copyright(c) 2006-2007, Ext JS, LLC.
9405  *
9406  * Originally Released Under LGPL - original licence link has changed is not relivant.
9407  *
9408  * Fork - LGPL
9409  * <script type="text/javascript">
9410  */
9411  
9412 // Base class for reading structured data from a data source.  This class is intended to be
9413 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9414
9415 /**
9416  * @class Roo.data.DataReader
9417  * Base class for reading structured data from a data source.  This class is intended to be
9418  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9419  */
9420
9421 Roo.data.DataReader = function(meta, recordType){
9422     
9423     this.meta = meta;
9424     
9425     this.recordType = recordType instanceof Array ? 
9426         Roo.data.Record.create(recordType) : recordType;
9427 };
9428
9429 Roo.data.DataReader.prototype = {
9430      /**
9431      * Create an empty record
9432      * @param {Object} data (optional) - overlay some values
9433      * @return {Roo.data.Record} record created.
9434      */
9435     newRow :  function(d) {
9436         var da =  {};
9437         this.recordType.prototype.fields.each(function(c) {
9438             switch( c.type) {
9439                 case 'int' : da[c.name] = 0; break;
9440                 case 'date' : da[c.name] = new Date(); break;
9441                 case 'float' : da[c.name] = 0.0; break;
9442                 case 'boolean' : da[c.name] = false; break;
9443                 default : da[c.name] = ""; break;
9444             }
9445             
9446         });
9447         return new this.recordType(Roo.apply(da, d));
9448     }
9449     
9450 };/*
9451  * Based on:
9452  * Ext JS Library 1.1.1
9453  * Copyright(c) 2006-2007, Ext JS, LLC.
9454  *
9455  * Originally Released Under LGPL - original licence link has changed is not relivant.
9456  *
9457  * Fork - LGPL
9458  * <script type="text/javascript">
9459  */
9460
9461 /**
9462  * @class Roo.data.DataProxy
9463  * @extends Roo.data.Observable
9464  * This class is an abstract base class for implementations which provide retrieval of
9465  * unformatted data objects.<br>
9466  * <p>
9467  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9468  * (of the appropriate type which knows how to parse the data object) to provide a block of
9469  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9470  * <p>
9471  * Custom implementations must implement the load method as described in
9472  * {@link Roo.data.HttpProxy#load}.
9473  */
9474 Roo.data.DataProxy = function(){
9475     this.addEvents({
9476         /**
9477          * @event beforeload
9478          * Fires before a network request is made to retrieve a data object.
9479          * @param {Object} This DataProxy object.
9480          * @param {Object} params The params parameter to the load function.
9481          */
9482         beforeload : true,
9483         /**
9484          * @event load
9485          * Fires before the load method's callback is called.
9486          * @param {Object} This DataProxy object.
9487          * @param {Object} o The data object.
9488          * @param {Object} arg The callback argument object passed to the load function.
9489          */
9490         load : true,
9491         /**
9492          * @event loadexception
9493          * Fires if an Exception occurs during data retrieval.
9494          * @param {Object} This DataProxy object.
9495          * @param {Object} o The data object.
9496          * @param {Object} arg The callback argument object passed to the load function.
9497          * @param {Object} e The Exception.
9498          */
9499         loadexception : true
9500     });
9501     Roo.data.DataProxy.superclass.constructor.call(this);
9502 };
9503
9504 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9505
9506     /**
9507      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9508      */
9509 /*
9510  * Based on:
9511  * Ext JS Library 1.1.1
9512  * Copyright(c) 2006-2007, Ext JS, LLC.
9513  *
9514  * Originally Released Under LGPL - original licence link has changed is not relivant.
9515  *
9516  * Fork - LGPL
9517  * <script type="text/javascript">
9518  */
9519 /**
9520  * @class Roo.data.MemoryProxy
9521  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9522  * to the Reader when its load method is called.
9523  * @constructor
9524  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9525  */
9526 Roo.data.MemoryProxy = function(data){
9527     if (data.data) {
9528         data = data.data;
9529     }
9530     Roo.data.MemoryProxy.superclass.constructor.call(this);
9531     this.data = data;
9532 };
9533
9534 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9535     /**
9536      * Load data from the requested source (in this case an in-memory
9537      * data object passed to the constructor), read the data object into
9538      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9539      * process that block using the passed callback.
9540      * @param {Object} params This parameter is not used by the MemoryProxy class.
9541      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9542      * object into a block of Roo.data.Records.
9543      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9544      * The function must be passed <ul>
9545      * <li>The Record block object</li>
9546      * <li>The "arg" argument from the load function</li>
9547      * <li>A boolean success indicator</li>
9548      * </ul>
9549      * @param {Object} scope The scope in which to call the callback
9550      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9551      */
9552     load : function(params, reader, callback, scope, arg){
9553         params = params || {};
9554         var result;
9555         try {
9556             result = reader.readRecords(this.data);
9557         }catch(e){
9558             this.fireEvent("loadexception", this, arg, null, e);
9559             callback.call(scope, null, arg, false);
9560             return;
9561         }
9562         callback.call(scope, result, arg, true);
9563     },
9564     
9565     // private
9566     update : function(params, records){
9567         
9568     }
9569 });/*
9570  * Based on:
9571  * Ext JS Library 1.1.1
9572  * Copyright(c) 2006-2007, Ext JS, LLC.
9573  *
9574  * Originally Released Under LGPL - original licence link has changed is not relivant.
9575  *
9576  * Fork - LGPL
9577  * <script type="text/javascript">
9578  */
9579 /**
9580  * @class Roo.data.HttpProxy
9581  * @extends Roo.data.DataProxy
9582  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9583  * configured to reference a certain URL.<br><br>
9584  * <p>
9585  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9586  * from which the running page was served.<br><br>
9587  * <p>
9588  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9589  * <p>
9590  * Be aware that to enable the browser to parse an XML document, the server must set
9591  * the Content-Type header in the HTTP response to "text/xml".
9592  * @constructor
9593  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9594  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9595  * will be used to make the request.
9596  */
9597 Roo.data.HttpProxy = function(conn){
9598     Roo.data.HttpProxy.superclass.constructor.call(this);
9599     // is conn a conn config or a real conn?
9600     this.conn = conn;
9601     this.useAjax = !conn || !conn.events;
9602   
9603 };
9604
9605 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9606     // thse are take from connection...
9607     
9608     /**
9609      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9610      */
9611     /**
9612      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9613      * extra parameters to each request made by this object. (defaults to undefined)
9614      */
9615     /**
9616      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9617      *  to each request made by this object. (defaults to undefined)
9618      */
9619     /**
9620      * @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)
9621      */
9622     /**
9623      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9624      */
9625      /**
9626      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9627      * @type Boolean
9628      */
9629   
9630
9631     /**
9632      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9633      * @type Boolean
9634      */
9635     /**
9636      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9637      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9638      * a finer-grained basis than the DataProxy events.
9639      */
9640     getConnection : function(){
9641         return this.useAjax ? Roo.Ajax : this.conn;
9642     },
9643
9644     /**
9645      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9646      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9647      * process that block using the passed callback.
9648      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9649      * for the request to the remote server.
9650      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9651      * object into a block of Roo.data.Records.
9652      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9653      * The function must be passed <ul>
9654      * <li>The Record block object</li>
9655      * <li>The "arg" argument from the load function</li>
9656      * <li>A boolean success indicator</li>
9657      * </ul>
9658      * @param {Object} scope The scope in which to call the callback
9659      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9660      */
9661     load : function(params, reader, callback, scope, arg){
9662         if(this.fireEvent("beforeload", this, params) !== false){
9663             var  o = {
9664                 params : params || {},
9665                 request: {
9666                     callback : callback,
9667                     scope : scope,
9668                     arg : arg
9669                 },
9670                 reader: reader,
9671                 callback : this.loadResponse,
9672                 scope: this
9673             };
9674             if(this.useAjax){
9675                 Roo.applyIf(o, this.conn);
9676                 if(this.activeRequest){
9677                     Roo.Ajax.abort(this.activeRequest);
9678                 }
9679                 this.activeRequest = Roo.Ajax.request(o);
9680             }else{
9681                 this.conn.request(o);
9682             }
9683         }else{
9684             callback.call(scope||this, null, arg, false);
9685         }
9686     },
9687
9688     // private
9689     loadResponse : function(o, success, response){
9690         delete this.activeRequest;
9691         if(!success){
9692             this.fireEvent("loadexception", this, o, response);
9693             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9694             return;
9695         }
9696         var result;
9697         try {
9698             result = o.reader.read(response);
9699         }catch(e){
9700             this.fireEvent("loadexception", this, o, response, e);
9701             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9702             return;
9703         }
9704         
9705         this.fireEvent("load", this, o, o.request.arg);
9706         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9707     },
9708
9709     // private
9710     update : function(dataSet){
9711
9712     },
9713
9714     // private
9715     updateResponse : function(dataSet){
9716
9717     }
9718 });/*
9719  * Based on:
9720  * Ext JS Library 1.1.1
9721  * Copyright(c) 2006-2007, Ext JS, LLC.
9722  *
9723  * Originally Released Under LGPL - original licence link has changed is not relivant.
9724  *
9725  * Fork - LGPL
9726  * <script type="text/javascript">
9727  */
9728
9729 /**
9730  * @class Roo.data.ScriptTagProxy
9731  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9732  * other than the originating domain of the running page.<br><br>
9733  * <p>
9734  * <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
9735  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9736  * <p>
9737  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9738  * source code that is used as the source inside a &lt;script> tag.<br><br>
9739  * <p>
9740  * In order for the browser to process the returned data, the server must wrap the data object
9741  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9742  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9743  * depending on whether the callback name was passed:
9744  * <p>
9745  * <pre><code>
9746 boolean scriptTag = false;
9747 String cb = request.getParameter("callback");
9748 if (cb != null) {
9749     scriptTag = true;
9750     response.setContentType("text/javascript");
9751 } else {
9752     response.setContentType("application/x-json");
9753 }
9754 Writer out = response.getWriter();
9755 if (scriptTag) {
9756     out.write(cb + "(");
9757 }
9758 out.print(dataBlock.toJsonString());
9759 if (scriptTag) {
9760     out.write(");");
9761 }
9762 </pre></code>
9763  *
9764  * @constructor
9765  * @param {Object} config A configuration object.
9766  */
9767 Roo.data.ScriptTagProxy = function(config){
9768     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9769     Roo.apply(this, config);
9770     this.head = document.getElementsByTagName("head")[0];
9771 };
9772
9773 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9774
9775 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9776     /**
9777      * @cfg {String} url The URL from which to request the data object.
9778      */
9779     /**
9780      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9781      */
9782     timeout : 30000,
9783     /**
9784      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9785      * the server the name of the callback function set up by the load call to process the returned data object.
9786      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9787      * javascript output which calls this named function passing the data object as its only parameter.
9788      */
9789     callbackParam : "callback",
9790     /**
9791      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9792      * name to the request.
9793      */
9794     nocache : true,
9795
9796     /**
9797      * Load data from the configured URL, read the data object into
9798      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9799      * process that block using the passed callback.
9800      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9801      * for the request to the remote server.
9802      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9803      * object into a block of Roo.data.Records.
9804      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9805      * The function must be passed <ul>
9806      * <li>The Record block object</li>
9807      * <li>The "arg" argument from the load function</li>
9808      * <li>A boolean success indicator</li>
9809      * </ul>
9810      * @param {Object} scope The scope in which to call the callback
9811      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9812      */
9813     load : function(params, reader, callback, scope, arg){
9814         if(this.fireEvent("beforeload", this, params) !== false){
9815
9816             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9817
9818             var url = this.url;
9819             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9820             if(this.nocache){
9821                 url += "&_dc=" + (new Date().getTime());
9822             }
9823             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9824             var trans = {
9825                 id : transId,
9826                 cb : "stcCallback"+transId,
9827                 scriptId : "stcScript"+transId,
9828                 params : params,
9829                 arg : arg,
9830                 url : url,
9831                 callback : callback,
9832                 scope : scope,
9833                 reader : reader
9834             };
9835             var conn = this;
9836
9837             window[trans.cb] = function(o){
9838                 conn.handleResponse(o, trans);
9839             };
9840
9841             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9842
9843             if(this.autoAbort !== false){
9844                 this.abort();
9845             }
9846
9847             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9848
9849             var script = document.createElement("script");
9850             script.setAttribute("src", url);
9851             script.setAttribute("type", "text/javascript");
9852             script.setAttribute("id", trans.scriptId);
9853             this.head.appendChild(script);
9854
9855             this.trans = trans;
9856         }else{
9857             callback.call(scope||this, null, arg, false);
9858         }
9859     },
9860
9861     // private
9862     isLoading : function(){
9863         return this.trans ? true : false;
9864     },
9865
9866     /**
9867      * Abort the current server request.
9868      */
9869     abort : function(){
9870         if(this.isLoading()){
9871             this.destroyTrans(this.trans);
9872         }
9873     },
9874
9875     // private
9876     destroyTrans : function(trans, isLoaded){
9877         this.head.removeChild(document.getElementById(trans.scriptId));
9878         clearTimeout(trans.timeoutId);
9879         if(isLoaded){
9880             window[trans.cb] = undefined;
9881             try{
9882                 delete window[trans.cb];
9883             }catch(e){}
9884         }else{
9885             // if hasn't been loaded, wait for load to remove it to prevent script error
9886             window[trans.cb] = function(){
9887                 window[trans.cb] = undefined;
9888                 try{
9889                     delete window[trans.cb];
9890                 }catch(e){}
9891             };
9892         }
9893     },
9894
9895     // private
9896     handleResponse : function(o, trans){
9897         this.trans = false;
9898         this.destroyTrans(trans, true);
9899         var result;
9900         try {
9901             result = trans.reader.readRecords(o);
9902         }catch(e){
9903             this.fireEvent("loadexception", this, o, trans.arg, e);
9904             trans.callback.call(trans.scope||window, null, trans.arg, false);
9905             return;
9906         }
9907         this.fireEvent("load", this, o, trans.arg);
9908         trans.callback.call(trans.scope||window, result, trans.arg, true);
9909     },
9910
9911     // private
9912     handleFailure : function(trans){
9913         this.trans = false;
9914         this.destroyTrans(trans, false);
9915         this.fireEvent("loadexception", this, null, trans.arg);
9916         trans.callback.call(trans.scope||window, null, trans.arg, false);
9917     }
9918 });/*
9919  * Based on:
9920  * Ext JS Library 1.1.1
9921  * Copyright(c) 2006-2007, Ext JS, LLC.
9922  *
9923  * Originally Released Under LGPL - original licence link has changed is not relivant.
9924  *
9925  * Fork - LGPL
9926  * <script type="text/javascript">
9927  */
9928
9929 /**
9930  * @class Roo.data.JsonReader
9931  * @extends Roo.data.DataReader
9932  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9933  * based on mappings in a provided Roo.data.Record constructor.
9934  * 
9935  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9936  * in the reply previously. 
9937  * 
9938  * <p>
9939  * Example code:
9940  * <pre><code>
9941 var RecordDef = Roo.data.Record.create([
9942     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9943     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9944 ]);
9945 var myReader = new Roo.data.JsonReader({
9946     totalProperty: "results",    // The property which contains the total dataset size (optional)
9947     root: "rows",                // The property which contains an Array of row objects
9948     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9949 }, RecordDef);
9950 </code></pre>
9951  * <p>
9952  * This would consume a JSON file like this:
9953  * <pre><code>
9954 { 'results': 2, 'rows': [
9955     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9956     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9957 }
9958 </code></pre>
9959  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9960  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9961  * paged from the remote server.
9962  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9963  * @cfg {String} root name of the property which contains the Array of row objects.
9964  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9965  * @constructor
9966  * Create a new JsonReader
9967  * @param {Object} meta Metadata configuration options
9968  * @param {Object} recordType Either an Array of field definition objects,
9969  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9970  */
9971 Roo.data.JsonReader = function(meta, recordType){
9972     
9973     meta = meta || {};
9974     // set some defaults:
9975     Roo.applyIf(meta, {
9976         totalProperty: 'total',
9977         successProperty : 'success',
9978         root : 'data',
9979         id : 'id'
9980     });
9981     
9982     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9983 };
9984 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9985     
9986     /**
9987      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9988      * Used by Store query builder to append _requestMeta to params.
9989      * 
9990      */
9991     metaFromRemote : false,
9992     /**
9993      * This method is only used by a DataProxy which has retrieved data from a remote server.
9994      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9995      * @return {Object} data A data block which is used by an Roo.data.Store object as
9996      * a cache of Roo.data.Records.
9997      */
9998     read : function(response){
9999         var json = response.responseText;
10000        
10001         var o = /* eval:var:o */ eval("("+json+")");
10002         if(!o) {
10003             throw {message: "JsonReader.read: Json object not found"};
10004         }
10005         
10006         if(o.metaData){
10007             
10008             delete this.ef;
10009             this.metaFromRemote = true;
10010             this.meta = o.metaData;
10011             this.recordType = Roo.data.Record.create(o.metaData.fields);
10012             this.onMetaChange(this.meta, this.recordType, o);
10013         }
10014         return this.readRecords(o);
10015     },
10016
10017     // private function a store will implement
10018     onMetaChange : function(meta, recordType, o){
10019
10020     },
10021
10022     /**
10023          * @ignore
10024          */
10025     simpleAccess: function(obj, subsc) {
10026         return obj[subsc];
10027     },
10028
10029         /**
10030          * @ignore
10031          */
10032     getJsonAccessor: function(){
10033         var re = /[\[\.]/;
10034         return function(expr) {
10035             try {
10036                 return(re.test(expr))
10037                     ? new Function("obj", "return obj." + expr)
10038                     : function(obj){
10039                         return obj[expr];
10040                     };
10041             } catch(e){}
10042             return Roo.emptyFn;
10043         };
10044     }(),
10045
10046     /**
10047      * Create a data block containing Roo.data.Records from an XML document.
10048      * @param {Object} o An object which contains an Array of row objects in the property specified
10049      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10050      * which contains the total size of the dataset.
10051      * @return {Object} data A data block which is used by an Roo.data.Store object as
10052      * a cache of Roo.data.Records.
10053      */
10054     readRecords : function(o){
10055         /**
10056          * After any data loads, the raw JSON data is available for further custom processing.
10057          * @type Object
10058          */
10059         this.o = o;
10060         var s = this.meta, Record = this.recordType,
10061             f = Record.prototype.fields, fi = f.items, fl = f.length;
10062
10063 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10064         if (!this.ef) {
10065             if(s.totalProperty) {
10066                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10067                 }
10068                 if(s.successProperty) {
10069                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10070                 }
10071                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10072                 if (s.id) {
10073                         var g = this.getJsonAccessor(s.id);
10074                         this.getId = function(rec) {
10075                                 var r = g(rec);
10076                                 return (r === undefined || r === "") ? null : r;
10077                         };
10078                 } else {
10079                         this.getId = function(){return null;};
10080                 }
10081             this.ef = [];
10082             for(var jj = 0; jj < fl; jj++){
10083                 f = fi[jj];
10084                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10085                 this.ef[jj] = this.getJsonAccessor(map);
10086             }
10087         }
10088
10089         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10090         if(s.totalProperty){
10091             var vt = parseInt(this.getTotal(o), 10);
10092             if(!isNaN(vt)){
10093                 totalRecords = vt;
10094             }
10095         }
10096         if(s.successProperty){
10097             var vs = this.getSuccess(o);
10098             if(vs === false || vs === 'false'){
10099                 success = false;
10100             }
10101         }
10102         var records = [];
10103             for(var i = 0; i < c; i++){
10104                     var n = root[i];
10105                 var values = {};
10106                 var id = this.getId(n);
10107                 for(var j = 0; j < fl; j++){
10108                     f = fi[j];
10109                 var v = this.ef[j](n);
10110                 if (!f.convert) {
10111                     Roo.log('missing convert for ' + f.name);
10112                     Roo.log(f);
10113                     continue;
10114                 }
10115                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10116                 }
10117                 var record = new Record(values, id);
10118                 record.json = n;
10119                 records[i] = record;
10120             }
10121             return {
10122             raw : o,
10123                 success : success,
10124                 records : records,
10125                 totalRecords : totalRecords
10126             };
10127     }
10128 });/*
10129  * Based on:
10130  * Ext JS Library 1.1.1
10131  * Copyright(c) 2006-2007, Ext JS, LLC.
10132  *
10133  * Originally Released Under LGPL - original licence link has changed is not relivant.
10134  *
10135  * Fork - LGPL
10136  * <script type="text/javascript">
10137  */
10138
10139 /**
10140  * @class Roo.data.ArrayReader
10141  * @extends Roo.data.DataReader
10142  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10143  * Each element of that Array represents a row of data fields. The
10144  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10145  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10146  * <p>
10147  * Example code:.
10148  * <pre><code>
10149 var RecordDef = Roo.data.Record.create([
10150     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10151     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10152 ]);
10153 var myReader = new Roo.data.ArrayReader({
10154     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10155 }, RecordDef);
10156 </code></pre>
10157  * <p>
10158  * This would consume an Array like this:
10159  * <pre><code>
10160 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10161   </code></pre>
10162  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10163  * @constructor
10164  * Create a new JsonReader
10165  * @param {Object} meta Metadata configuration options.
10166  * @param {Object} recordType Either an Array of field definition objects
10167  * as specified to {@link Roo.data.Record#create},
10168  * or an {@link Roo.data.Record} object
10169  * created using {@link Roo.data.Record#create}.
10170  */
10171 Roo.data.ArrayReader = function(meta, recordType){
10172     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10173 };
10174
10175 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10176     /**
10177      * Create a data block containing Roo.data.Records from an XML document.
10178      * @param {Object} o An Array of row objects which represents the dataset.
10179      * @return {Object} data A data block which is used by an Roo.data.Store object as
10180      * a cache of Roo.data.Records.
10181      */
10182     readRecords : function(o){
10183         var sid = this.meta ? this.meta.id : null;
10184         var recordType = this.recordType, fields = recordType.prototype.fields;
10185         var records = [];
10186         var root = o;
10187             for(var i = 0; i < root.length; i++){
10188                     var n = root[i];
10189                 var values = {};
10190                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10191                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10192                 var f = fields.items[j];
10193                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10194                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10195                 v = f.convert(v);
10196                 values[f.name] = v;
10197             }
10198                 var record = new recordType(values, id);
10199                 record.json = n;
10200                 records[records.length] = record;
10201             }
10202             return {
10203                 records : records,
10204                 totalRecords : records.length
10205             };
10206     }
10207 });/*
10208  * - LGPL
10209  * * 
10210  */
10211
10212 /**
10213  * @class Roo.bootstrap.ComboBox
10214  * @extends Roo.bootstrap.TriggerField
10215  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10216  * @cfg {Boolean} append (true|false) default false
10217  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10218  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10219  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10220  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10221  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10222  * @constructor
10223  * Create a new ComboBox.
10224  * @param {Object} config Configuration options
10225  */
10226 Roo.bootstrap.ComboBox = function(config){
10227     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10228     this.addEvents({
10229         /**
10230          * @event expand
10231          * Fires when the dropdown list is expanded
10232              * @param {Roo.bootstrap.ComboBox} combo This combo box
10233              */
10234         'expand' : true,
10235         /**
10236          * @event collapse
10237          * Fires when the dropdown list is collapsed
10238              * @param {Roo.bootstrap.ComboBox} combo This combo box
10239              */
10240         'collapse' : true,
10241         /**
10242          * @event beforeselect
10243          * Fires before a list item is selected. Return false to cancel the selection.
10244              * @param {Roo.bootstrap.ComboBox} combo This combo box
10245              * @param {Roo.data.Record} record The data record returned from the underlying store
10246              * @param {Number} index The index of the selected item in the dropdown list
10247              */
10248         'beforeselect' : true,
10249         /**
10250          * @event select
10251          * Fires when a list item is selected
10252              * @param {Roo.bootstrap.ComboBox} combo This combo box
10253              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10254              * @param {Number} index The index of the selected item in the dropdown list
10255              */
10256         'select' : true,
10257         /**
10258          * @event beforequery
10259          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10260          * The event object passed has these properties:
10261              * @param {Roo.bootstrap.ComboBox} combo This combo box
10262              * @param {String} query The query
10263              * @param {Boolean} forceAll true to force "all" query
10264              * @param {Boolean} cancel true to cancel the query
10265              * @param {Object} e The query event object
10266              */
10267         'beforequery': true,
10268          /**
10269          * @event add
10270          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10271              * @param {Roo.bootstrap.ComboBox} combo This combo box
10272              */
10273         'add' : true,
10274         /**
10275          * @event edit
10276          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10277              * @param {Roo.bootstrap.ComboBox} combo This combo box
10278              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10279              */
10280         'edit' : true,
10281         /**
10282          * @event remove
10283          * Fires when the remove value from the combobox array
10284              * @param {Roo.bootstrap.ComboBox} combo This combo box
10285              */
10286         'remove' : true
10287         
10288     });
10289     
10290     this.item = [];
10291     this.tickItems = [];
10292     
10293     this.selectedIndex = -1;
10294     if(this.mode == 'local'){
10295         if(config.queryDelay === undefined){
10296             this.queryDelay = 10;
10297         }
10298         if(config.minChars === undefined){
10299             this.minChars = 0;
10300         }
10301     }
10302 };
10303
10304 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10305      
10306     /**
10307      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10308      * rendering into an Roo.Editor, defaults to false)
10309      */
10310     /**
10311      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10312      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10313      */
10314     /**
10315      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10316      */
10317     /**
10318      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10319      * the dropdown list (defaults to undefined, with no header element)
10320      */
10321
10322      /**
10323      * @cfg {String/Roo.Template} tpl The template to use to render the output
10324      */
10325      
10326      /**
10327      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10328      */
10329     listWidth: undefined,
10330     /**
10331      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10332      * mode = 'remote' or 'text' if mode = 'local')
10333      */
10334     displayField: undefined,
10335     /**
10336      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10337      * mode = 'remote' or 'value' if mode = 'local'). 
10338      * Note: use of a valueField requires the user make a selection
10339      * in order for a value to be mapped.
10340      */
10341     valueField: undefined,
10342     
10343     
10344     /**
10345      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10346      * field's data value (defaults to the underlying DOM element's name)
10347      */
10348     hiddenName: undefined,
10349     /**
10350      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10351      */
10352     listClass: '',
10353     /**
10354      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10355      */
10356     selectedClass: 'active',
10357     
10358     /**
10359      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10360      */
10361     shadow:'sides',
10362     /**
10363      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10364      * anchor positions (defaults to 'tl-bl')
10365      */
10366     listAlign: 'tl-bl?',
10367     /**
10368      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10369      */
10370     maxHeight: 300,
10371     /**
10372      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10373      * query specified by the allQuery config option (defaults to 'query')
10374      */
10375     triggerAction: 'query',
10376     /**
10377      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10378      * (defaults to 4, does not apply if editable = false)
10379      */
10380     minChars : 4,
10381     /**
10382      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10383      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10384      */
10385     typeAhead: false,
10386     /**
10387      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10388      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10389      */
10390     queryDelay: 500,
10391     /**
10392      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10393      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10394      */
10395     pageSize: 0,
10396     /**
10397      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10398      * when editable = true (defaults to false)
10399      */
10400     selectOnFocus:false,
10401     /**
10402      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10403      */
10404     queryParam: 'query',
10405     /**
10406      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10407      * when mode = 'remote' (defaults to 'Loading...')
10408      */
10409     loadingText: 'Loading...',
10410     /**
10411      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10412      */
10413     resizable: false,
10414     /**
10415      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10416      */
10417     handleHeight : 8,
10418     /**
10419      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10420      * traditional select (defaults to true)
10421      */
10422     editable: true,
10423     /**
10424      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10425      */
10426     allQuery: '',
10427     /**
10428      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10429      */
10430     mode: 'remote',
10431     /**
10432      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10433      * listWidth has a higher value)
10434      */
10435     minListWidth : 70,
10436     /**
10437      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10438      * allow the user to set arbitrary text into the field (defaults to false)
10439      */
10440     forceSelection:false,
10441     /**
10442      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10443      * if typeAhead = true (defaults to 250)
10444      */
10445     typeAheadDelay : 250,
10446     /**
10447      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10448      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10449      */
10450     valueNotFoundText : undefined,
10451     /**
10452      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10453      */
10454     blockFocus : false,
10455     
10456     /**
10457      * @cfg {Boolean} disableClear Disable showing of clear button.
10458      */
10459     disableClear : false,
10460     /**
10461      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10462      */
10463     alwaysQuery : false,
10464     
10465     /**
10466      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10467      */
10468     multiple : false,
10469     
10470     //private
10471     addicon : false,
10472     editicon: false,
10473     
10474     page: 0,
10475     hasQuery: false,
10476     append: false,
10477     loadNext: false,
10478     autoFocus : true,
10479     tickable : false,
10480     btnPosition : 'right',
10481     triggerList : true,
10482     showToggleBtn : true,
10483     // element that contains real text value.. (when hidden is used..)
10484     
10485     getAutoCreate : function()
10486     {
10487         var cfg = false;
10488         
10489         /*
10490          *  Normal ComboBox
10491          */
10492         if(!this.tickable){
10493             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10494             return cfg;
10495         }
10496         
10497         /*
10498          *  ComboBox with tickable selections
10499          */
10500              
10501         var align = this.labelAlign || this.parentLabelAlign();
10502         
10503         cfg = {
10504             cls : 'form-group roo-combobox-tickable' //input-group
10505         };
10506         
10507         
10508         var buttons = {
10509             tag : 'div',
10510             cls : 'tickable-buttons',
10511             cn : [
10512                 {
10513                     tag : 'button',
10514                     type : 'button',
10515                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10516                     html : 'Edit'
10517                 },
10518                 {
10519                     tag : 'button',
10520                     type : 'button',
10521                     name : 'ok',
10522                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10523                     html : 'Done'
10524                 },
10525                 {
10526                     tag : 'button',
10527                     type : 'button',
10528                     name : 'cancel',
10529                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10530                     html : 'Cancel'
10531                 }
10532             ]
10533         };
10534         
10535         var _this = this;
10536         Roo.each(buttons.cn, function(c){
10537             if (_this.size) {
10538                 c.cls += ' btn-' + _this.size;
10539             }
10540
10541             if (_this.disabled) {
10542                 c.disabled = true;
10543             }
10544         });
10545         
10546         var box = {
10547             tag: 'div',
10548             cn: [
10549                 {
10550                     tag: 'input',
10551                     type : 'hidden',
10552                     cls: 'form-hidden-field'
10553                 },
10554                 {
10555                     tag: 'ul',
10556                     cls: 'select2-choices',
10557                     cn:[
10558                         {
10559                             tag: 'li',
10560                             cls: 'select2-search-field',
10561                             cn: [
10562
10563                                 buttons
10564                             ]
10565                         }
10566                     ]
10567                 }
10568             ]
10569         }
10570         
10571         var combobox = {
10572             cls: 'select2-container input-group select2-container-multi',
10573             cn: [
10574                 box
10575 //                {
10576 //                    tag: 'ul',
10577 //                    cls: 'typeahead typeahead-long dropdown-menu',
10578 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10579 //                }
10580             ]
10581         };
10582         
10583         if (align ==='left' && this.fieldLabel.length) {
10584             
10585                 Roo.log("left and has label");
10586                 cfg.cn = [
10587                     
10588                     {
10589                         tag: 'label',
10590                         'for' :  id,
10591                         cls : 'control-label col-sm-' + this.labelWidth,
10592                         html : this.fieldLabel
10593                         
10594                     },
10595                     {
10596                         cls : "col-sm-" + (12 - this.labelWidth), 
10597                         cn: [
10598                             combobox
10599                         ]
10600                     }
10601                     
10602                 ];
10603         } else if ( this.fieldLabel.length) {
10604                 Roo.log(" label");
10605                  cfg.cn = [
10606                    
10607                     {
10608                         tag: 'label',
10609                         //cls : 'input-group-addon',
10610                         html : this.fieldLabel
10611                         
10612                     },
10613                     
10614                     combobox
10615                     
10616                 ];
10617
10618         } else {
10619             
10620                 Roo.log(" no label && no align");
10621                 cfg = combobox
10622                      
10623                 
10624         }
10625          
10626         var settings=this;
10627         ['xs','sm','md','lg'].map(function(size){
10628             if (settings[size]) {
10629                 cfg.cls += ' col-' + size + '-' + settings[size];
10630             }
10631         });
10632         
10633         return cfg;
10634         
10635     },
10636     
10637     // private
10638     initEvents: function()
10639     {
10640         
10641         if (!this.store) {
10642             throw "can not find store for combo";
10643         }
10644         this.store = Roo.factory(this.store, Roo.data);
10645         
10646         if(this.tickable){
10647             this.initTickableEvents();
10648             return;
10649         }
10650         
10651         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10652         
10653         if(this.hiddenName){
10654             
10655             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10656             
10657             this.hiddenField.dom.value =
10658                 this.hiddenValue !== undefined ? this.hiddenValue :
10659                 this.value !== undefined ? this.value : '';
10660
10661             // prevent input submission
10662             this.el.dom.removeAttribute('name');
10663             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10664              
10665              
10666         }
10667         //if(Roo.isGecko){
10668         //    this.el.dom.setAttribute('autocomplete', 'off');
10669         //}
10670         
10671         var cls = 'x-combo-list';
10672         
10673         //this.list = new Roo.Layer({
10674         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10675         //});
10676         
10677         var _this = this;
10678         
10679         (function(){
10680             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10681             _this.list.setWidth(lw);
10682         }).defer(100);
10683         
10684         this.list.on('mouseover', this.onViewOver, this);
10685         this.list.on('mousemove', this.onViewMove, this);
10686         
10687         this.list.on('scroll', this.onViewScroll, this);
10688         
10689         /*
10690         this.list.swallowEvent('mousewheel');
10691         this.assetHeight = 0;
10692
10693         if(this.title){
10694             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10695             this.assetHeight += this.header.getHeight();
10696         }
10697
10698         this.innerList = this.list.createChild({cls:cls+'-inner'});
10699         this.innerList.on('mouseover', this.onViewOver, this);
10700         this.innerList.on('mousemove', this.onViewMove, this);
10701         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10702         
10703         if(this.allowBlank && !this.pageSize && !this.disableClear){
10704             this.footer = this.list.createChild({cls:cls+'-ft'});
10705             this.pageTb = new Roo.Toolbar(this.footer);
10706            
10707         }
10708         if(this.pageSize){
10709             this.footer = this.list.createChild({cls:cls+'-ft'});
10710             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10711                     {pageSize: this.pageSize});
10712             
10713         }
10714         
10715         if (this.pageTb && this.allowBlank && !this.disableClear) {
10716             var _this = this;
10717             this.pageTb.add(new Roo.Toolbar.Fill(), {
10718                 cls: 'x-btn-icon x-btn-clear',
10719                 text: '&#160;',
10720                 handler: function()
10721                 {
10722                     _this.collapse();
10723                     _this.clearValue();
10724                     _this.onSelect(false, -1);
10725                 }
10726             });
10727         }
10728         if (this.footer) {
10729             this.assetHeight += this.footer.getHeight();
10730         }
10731         */
10732             
10733         if(!this.tpl){
10734             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10735         }
10736
10737         this.view = new Roo.View(this.list, this.tpl, {
10738             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10739         });
10740         //this.view.wrapEl.setDisplayed(false);
10741         this.view.on('click', this.onViewClick, this);
10742         
10743         
10744         
10745         this.store.on('beforeload', this.onBeforeLoad, this);
10746         this.store.on('load', this.onLoad, this);
10747         this.store.on('loadexception', this.onLoadException, this);
10748         /*
10749         if(this.resizable){
10750             this.resizer = new Roo.Resizable(this.list,  {
10751                pinned:true, handles:'se'
10752             });
10753             this.resizer.on('resize', function(r, w, h){
10754                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10755                 this.listWidth = w;
10756                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10757                 this.restrictHeight();
10758             }, this);
10759             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10760         }
10761         */
10762         if(!this.editable){
10763             this.editable = true;
10764             this.setEditable(false);
10765         }
10766         
10767         /*
10768         
10769         if (typeof(this.events.add.listeners) != 'undefined') {
10770             
10771             this.addicon = this.wrap.createChild(
10772                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10773        
10774             this.addicon.on('click', function(e) {
10775                 this.fireEvent('add', this);
10776             }, this);
10777         }
10778         if (typeof(this.events.edit.listeners) != 'undefined') {
10779             
10780             this.editicon = this.wrap.createChild(
10781                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10782             if (this.addicon) {
10783                 this.editicon.setStyle('margin-left', '40px');
10784             }
10785             this.editicon.on('click', function(e) {
10786                 
10787                 // we fire even  if inothing is selected..
10788                 this.fireEvent('edit', this, this.lastData );
10789                 
10790             }, this);
10791         }
10792         */
10793         
10794         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10795             "up" : function(e){
10796                 this.inKeyMode = true;
10797                 this.selectPrev();
10798             },
10799
10800             "down" : function(e){
10801                 if(!this.isExpanded()){
10802                     this.onTriggerClick();
10803                 }else{
10804                     this.inKeyMode = true;
10805                     this.selectNext();
10806                 }
10807             },
10808
10809             "enter" : function(e){
10810 //                this.onViewClick();
10811                 //return true;
10812                 this.collapse();
10813                 
10814                 if(this.fireEvent("specialkey", this, e)){
10815                     this.onViewClick(false);
10816                 }
10817                 
10818                 return true;
10819             },
10820
10821             "esc" : function(e){
10822                 this.collapse();
10823             },
10824
10825             "tab" : function(e){
10826                 this.collapse();
10827                 
10828                 if(this.fireEvent("specialkey", this, e)){
10829                     this.onViewClick(false);
10830                 }
10831                 
10832                 return true;
10833             },
10834
10835             scope : this,
10836
10837             doRelay : function(foo, bar, hname){
10838                 if(hname == 'down' || this.scope.isExpanded()){
10839                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10840                 }
10841                 return true;
10842             },
10843
10844             forceKeyDown: true
10845         });
10846         
10847         
10848         this.queryDelay = Math.max(this.queryDelay || 10,
10849                 this.mode == 'local' ? 10 : 250);
10850         
10851         
10852         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10853         
10854         if(this.typeAhead){
10855             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10856         }
10857         if(this.editable !== false){
10858             this.inputEl().on("keyup", this.onKeyUp, this);
10859         }
10860         if(this.forceSelection){
10861             this.inputEl().on('blur', this.doForce, this);
10862         }
10863         
10864         if(this.multiple){
10865             this.choices = this.el.select('ul.select2-choices', true).first();
10866             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10867         }
10868     },
10869     
10870     initTickableEvents: function()
10871     {   
10872         this.createList();
10873         
10874         if(this.hiddenName){
10875             
10876             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10877             
10878             this.hiddenField.dom.value =
10879                 this.hiddenValue !== undefined ? this.hiddenValue :
10880                 this.value !== undefined ? this.value : '';
10881
10882             // prevent input submission
10883             this.el.dom.removeAttribute('name');
10884             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10885              
10886              
10887         }
10888         
10889 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10890         
10891         this.choices = this.el.select('ul.select2-choices', true).first();
10892         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10893         if(this.triggerList){
10894             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10895         }
10896          
10897         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10898         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10899         
10900         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10901         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10902         
10903         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10904         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10905         
10906         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10907         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10908         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10909         
10910         this.okBtn.hide();
10911         this.cancelBtn.hide();
10912         
10913         var _this = this;
10914         
10915         (function(){
10916             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10917             _this.list.setWidth(lw);
10918         }).defer(100);
10919         
10920         this.list.on('mouseover', this.onViewOver, this);
10921         this.list.on('mousemove', this.onViewMove, this);
10922         
10923         this.list.on('scroll', this.onViewScroll, this);
10924         
10925         if(!this.tpl){
10926             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>';
10927         }
10928
10929         this.view = new Roo.View(this.list, this.tpl, {
10930             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10931         });
10932         
10933         //this.view.wrapEl.setDisplayed(false);
10934         this.view.on('click', this.onViewClick, this);
10935         
10936         
10937         
10938         this.store.on('beforeload', this.onBeforeLoad, this);
10939         this.store.on('load', this.onLoad, this);
10940         this.store.on('loadexception', this.onLoadException, this);
10941         
10942 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10943 //            "up" : function(e){
10944 //                this.inKeyMode = true;
10945 //                this.selectPrev();
10946 //            },
10947 //
10948 //            "down" : function(e){
10949 //                if(!this.isExpanded()){
10950 //                    this.onTriggerClick();
10951 //                }else{
10952 //                    this.inKeyMode = true;
10953 //                    this.selectNext();
10954 //                }
10955 //            },
10956 //
10957 //            "enter" : function(e){
10958 ////                this.onViewClick();
10959 //                //return true;
10960 //                this.collapse();
10961 //                
10962 //                if(this.fireEvent("specialkey", this, e)){
10963 //                    this.onViewClick(false);
10964 //                }
10965 //                
10966 //                return true;
10967 //            },
10968 //
10969 //            "esc" : function(e){
10970 //                this.collapse();
10971 //            },
10972 //
10973 //            "tab" : function(e){
10974 //                this.collapse();
10975 //                
10976 //                if(this.fireEvent("specialkey", this, e)){
10977 //                    this.onViewClick(false);
10978 //                }
10979 //                
10980 //                return true;
10981 //            },
10982 //
10983 //            scope : this,
10984 //
10985 //            doRelay : function(foo, bar, hname){
10986 //                if(hname == 'down' || this.scope.isExpanded()){
10987 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10988 //                }
10989 //                return true;
10990 //            },
10991 //
10992 //            forceKeyDown: true
10993 //        });
10994         
10995         
10996         this.queryDelay = Math.max(this.queryDelay || 10,
10997                 this.mode == 'local' ? 10 : 250);
10998         
10999         
11000         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11001         
11002         if(this.typeAhead){
11003             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11004         }
11005     },
11006
11007     onDestroy : function(){
11008         if(this.view){
11009             this.view.setStore(null);
11010             this.view.el.removeAllListeners();
11011             this.view.el.remove();
11012             this.view.purgeListeners();
11013         }
11014         if(this.list){
11015             this.list.dom.innerHTML  = '';
11016         }
11017         
11018         if(this.store){
11019             this.store.un('beforeload', this.onBeforeLoad, this);
11020             this.store.un('load', this.onLoad, this);
11021             this.store.un('loadexception', this.onLoadException, this);
11022         }
11023         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11024     },
11025
11026     // private
11027     fireKey : function(e){
11028         if(e.isNavKeyPress() && !this.list.isVisible()){
11029             this.fireEvent("specialkey", this, e);
11030         }
11031     },
11032
11033     // private
11034     onResize: function(w, h){
11035 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11036 //        
11037 //        if(typeof w != 'number'){
11038 //            // we do not handle it!?!?
11039 //            return;
11040 //        }
11041 //        var tw = this.trigger.getWidth();
11042 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11043 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11044 //        var x = w - tw;
11045 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11046 //            
11047 //        //this.trigger.setStyle('left', x+'px');
11048 //        
11049 //        if(this.list && this.listWidth === undefined){
11050 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11051 //            this.list.setWidth(lw);
11052 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11053 //        }
11054         
11055     
11056         
11057     },
11058
11059     /**
11060      * Allow or prevent the user from directly editing the field text.  If false is passed,
11061      * the user will only be able to select from the items defined in the dropdown list.  This method
11062      * is the runtime equivalent of setting the 'editable' config option at config time.
11063      * @param {Boolean} value True to allow the user to directly edit the field text
11064      */
11065     setEditable : function(value){
11066         if(value == this.editable){
11067             return;
11068         }
11069         this.editable = value;
11070         if(!value){
11071             this.inputEl().dom.setAttribute('readOnly', true);
11072             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11073             this.inputEl().addClass('x-combo-noedit');
11074         }else{
11075             this.inputEl().dom.setAttribute('readOnly', false);
11076             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11077             this.inputEl().removeClass('x-combo-noedit');
11078         }
11079     },
11080
11081     // private
11082     
11083     onBeforeLoad : function(combo,opts){
11084         if(!this.hasFocus){
11085             return;
11086         }
11087          if (!opts.add) {
11088             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11089          }
11090 //        this.restrictHeight();
11091         this.selectedIndex = -1;
11092     },
11093
11094     // private
11095     onLoad : function(){
11096         
11097         this.hasQuery = false;
11098         
11099         if(!this.hasFocus){
11100             return;
11101         }
11102         
11103         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11104             this.loading.hide();
11105         }
11106         
11107         if(this.store.getCount() > 0){
11108             this.expand();
11109 //            this.restrictHeight();
11110             if(this.lastQuery == this.allQuery){
11111                 if(this.editable && !this.tickable){
11112                     this.inputEl().dom.select();
11113                 }
11114                 
11115                 if(
11116                     !this.selectByValue(this.value, true) &&
11117                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11118                     this.store.lastOptions.add != true)
11119                 ){
11120                     this.select(0, true);
11121                 }
11122             }else{
11123                 if(this.autoFocus){
11124                     this.selectNext();
11125                 }
11126                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11127                     this.taTask.delay(this.typeAheadDelay);
11128                 }
11129             }
11130         }else{
11131             this.onEmptyResults();
11132         }
11133         
11134         //this.el.focus();
11135     },
11136     // private
11137     onLoadException : function()
11138     {
11139         this.hasQuery = false;
11140         
11141         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11142             this.loading.hide();
11143         }
11144         
11145         this.collapse();
11146         Roo.log(this.store.reader.jsonData);
11147         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11148             // fixme
11149             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11150         }
11151         
11152         
11153     },
11154     // private
11155     onTypeAhead : function(){
11156         if(this.store.getCount() > 0){
11157             var r = this.store.getAt(0);
11158             var newValue = r.data[this.displayField];
11159             var len = newValue.length;
11160             var selStart = this.getRawValue().length;
11161             
11162             if(selStart != len){
11163                 this.setRawValue(newValue);
11164                 this.selectText(selStart, newValue.length);
11165             }
11166         }
11167     },
11168
11169     // private
11170     onSelect : function(record, index){
11171         
11172         if(this.fireEvent('beforeselect', this, record, index) !== false){
11173         
11174             this.setFromData(index > -1 ? record.data : false);
11175             
11176             this.collapse();
11177             this.fireEvent('select', this, record, index);
11178         }
11179     },
11180
11181     /**
11182      * Returns the currently selected field value or empty string if no value is set.
11183      * @return {String} value The selected value
11184      */
11185     getValue : function(){
11186         
11187         if(this.multiple){
11188             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11189         }
11190         
11191         if(this.valueField){
11192             return typeof this.value != 'undefined' ? this.value : '';
11193         }else{
11194             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11195         }
11196     },
11197
11198     /**
11199      * Clears any text/value currently set in the field
11200      */
11201     clearValue : function(){
11202         if(this.hiddenField){
11203             this.hiddenField.dom.value = '';
11204         }
11205         this.value = '';
11206         this.setRawValue('');
11207         this.lastSelectionText = '';
11208         
11209     },
11210
11211     /**
11212      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11213      * will be displayed in the field.  If the value does not match the data value of an existing item,
11214      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11215      * Otherwise the field will be blank (although the value will still be set).
11216      * @param {String} value The value to match
11217      */
11218     setValue : function(v){
11219         if(this.multiple){
11220             this.syncValue();
11221             return;
11222         }
11223         
11224         var text = v;
11225         if(this.valueField){
11226             var r = this.findRecord(this.valueField, v);
11227             if(r){
11228                 text = r.data[this.displayField];
11229             }else if(this.valueNotFoundText !== undefined){
11230                 text = this.valueNotFoundText;
11231             }
11232         }
11233         this.lastSelectionText = text;
11234         if(this.hiddenField){
11235             this.hiddenField.dom.value = v;
11236         }
11237         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11238         this.value = v;
11239     },
11240     /**
11241      * @property {Object} the last set data for the element
11242      */
11243     
11244     lastData : false,
11245     /**
11246      * Sets the value of the field based on a object which is related to the record format for the store.
11247      * @param {Object} value the value to set as. or false on reset?
11248      */
11249     setFromData : function(o){
11250         
11251         if(this.multiple){
11252             if(typeof o.display_name !== 'string'){
11253                 for(var i=0;i<o.display_name.length;i++){
11254                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11255                 }
11256                 return;
11257             }
11258             this.addItem(o);
11259             return;
11260         }
11261             
11262         var dv = ''; // display value
11263         var vv = ''; // value value..
11264         this.lastData = o;
11265         if (this.displayField) {
11266             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11267         } else {
11268             // this is an error condition!!!
11269             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11270         }
11271         
11272         if(this.valueField){
11273             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11274         }
11275         
11276         if(this.hiddenField){
11277             this.hiddenField.dom.value = vv;
11278             
11279             this.lastSelectionText = dv;
11280             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11281             this.value = vv;
11282             return;
11283         }
11284         // no hidden field.. - we store the value in 'value', but still display
11285         // display field!!!!
11286         this.lastSelectionText = dv;
11287         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11288         this.value = vv;
11289         
11290         
11291     },
11292     // private
11293     reset : function(){
11294         // overridden so that last data is reset..
11295         this.setValue(this.originalValue);
11296         this.clearInvalid();
11297         this.lastData = false;
11298         if (this.view) {
11299             this.view.clearSelections();
11300         }
11301     },
11302     // private
11303     findRecord : function(prop, value){
11304         var record;
11305         if(this.store.getCount() > 0){
11306             this.store.each(function(r){
11307                 if(r.data[prop] == value){
11308                     record = r;
11309                     return false;
11310                 }
11311                 return true;
11312             });
11313         }
11314         return record;
11315     },
11316     
11317     getName: function()
11318     {
11319         // returns hidden if it's set..
11320         if (!this.rendered) {return ''};
11321         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11322         
11323     },
11324     // private
11325     onViewMove : function(e, t){
11326         this.inKeyMode = false;
11327     },
11328
11329     // private
11330     onViewOver : function(e, t){
11331         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11332             return;
11333         }
11334         var item = this.view.findItemFromChild(t);
11335         
11336         if(item){
11337             var index = this.view.indexOf(item);
11338             this.select(index, false);
11339         }
11340     },
11341
11342     // private
11343     onViewClick : function(view, doFocus, el, e)
11344     {
11345         var index = this.view.getSelectedIndexes()[0];
11346         
11347         var r = this.store.getAt(index);
11348         
11349         if(this.tickable){
11350             
11351             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11352                 return;
11353             }
11354             
11355             var rm = false;
11356             var _this = this;
11357             
11358             Roo.each(this.tickItems, function(v,k){
11359                 
11360                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11361                     _this.tickItems.splice(k, 1);
11362                     rm = true;
11363                     return;
11364                 }
11365             })
11366             
11367             if(rm){
11368                 return;
11369             }
11370             
11371             this.tickItems.push(r.data);
11372             return;
11373         }
11374         
11375         if(r){
11376             this.onSelect(r, index);
11377         }
11378         if(doFocus !== false && !this.blockFocus){
11379             this.inputEl().focus();
11380         }
11381     },
11382
11383     // private
11384     restrictHeight : function(){
11385         //this.innerList.dom.style.height = '';
11386         //var inner = this.innerList.dom;
11387         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11388         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11389         //this.list.beginUpdate();
11390         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11391         this.list.alignTo(this.inputEl(), this.listAlign);
11392         this.list.alignTo(this.inputEl(), this.listAlign);
11393         //this.list.endUpdate();
11394     },
11395
11396     // private
11397     onEmptyResults : function(){
11398         this.collapse();
11399     },
11400
11401     /**
11402      * Returns true if the dropdown list is expanded, else false.
11403      */
11404     isExpanded : function(){
11405         return this.list.isVisible();
11406     },
11407
11408     /**
11409      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11410      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11411      * @param {String} value The data value of the item to select
11412      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11413      * selected item if it is not currently in view (defaults to true)
11414      * @return {Boolean} True if the value matched an item in the list, else false
11415      */
11416     selectByValue : function(v, scrollIntoView){
11417         if(v !== undefined && v !== null){
11418             var r = this.findRecord(this.valueField || this.displayField, v);
11419             if(r){
11420                 this.select(this.store.indexOf(r), scrollIntoView);
11421                 return true;
11422             }
11423         }
11424         return false;
11425     },
11426
11427     /**
11428      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11429      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11430      * @param {Number} index The zero-based index of the list item to select
11431      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11432      * selected item if it is not currently in view (defaults to true)
11433      */
11434     select : function(index, scrollIntoView){
11435         this.selectedIndex = index;
11436         this.view.select(index);
11437         if(scrollIntoView !== false){
11438             var el = this.view.getNode(index);
11439             if(el && !this.multiple && !this.tickable){
11440                 this.list.scrollChildIntoView(el, false);
11441             }
11442         }
11443     },
11444
11445     // private
11446     selectNext : function(){
11447         var ct = this.store.getCount();
11448         if(ct > 0){
11449             if(this.selectedIndex == -1){
11450                 this.select(0);
11451             }else if(this.selectedIndex < ct-1){
11452                 this.select(this.selectedIndex+1);
11453             }
11454         }
11455     },
11456
11457     // private
11458     selectPrev : function(){
11459         var ct = this.store.getCount();
11460         if(ct > 0){
11461             if(this.selectedIndex == -1){
11462                 this.select(0);
11463             }else if(this.selectedIndex != 0){
11464                 this.select(this.selectedIndex-1);
11465             }
11466         }
11467     },
11468
11469     // private
11470     onKeyUp : function(e){
11471         if(this.editable !== false && !e.isSpecialKey()){
11472             this.lastKey = e.getKey();
11473             this.dqTask.delay(this.queryDelay);
11474         }
11475     },
11476
11477     // private
11478     validateBlur : function(){
11479         return !this.list || !this.list.isVisible();   
11480     },
11481
11482     // private
11483     initQuery : function(){
11484         this.doQuery(this.getRawValue());
11485     },
11486
11487     // private
11488     doForce : function(){
11489         if(this.inputEl().dom.value.length > 0){
11490             this.inputEl().dom.value =
11491                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11492              
11493         }
11494     },
11495
11496     /**
11497      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11498      * query allowing the query action to be canceled if needed.
11499      * @param {String} query The SQL query to execute
11500      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11501      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11502      * saved in the current store (defaults to false)
11503      */
11504     doQuery : function(q, forceAll){
11505         
11506         if(q === undefined || q === null){
11507             q = '';
11508         }
11509         var qe = {
11510             query: q,
11511             forceAll: forceAll,
11512             combo: this,
11513             cancel:false
11514         };
11515         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11516             return false;
11517         }
11518         q = qe.query;
11519         
11520         forceAll = qe.forceAll;
11521         if(forceAll === true || (q.length >= this.minChars)){
11522             
11523             this.hasQuery = true;
11524             
11525             if(this.lastQuery != q || this.alwaysQuery){
11526                 this.lastQuery = q;
11527                 if(this.mode == 'local'){
11528                     this.selectedIndex = -1;
11529                     if(forceAll){
11530                         this.store.clearFilter();
11531                     }else{
11532                         this.store.filter(this.displayField, q);
11533                     }
11534                     this.onLoad();
11535                 }else{
11536                     this.store.baseParams[this.queryParam] = q;
11537                     
11538                     var options = {params : this.getParams(q)};
11539                     
11540                     if(this.loadNext){
11541                         options.add = true;
11542                         options.params.start = this.page * this.pageSize;
11543                     }
11544                     
11545                     this.store.load(options);
11546                     /*
11547                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11548                      *  we should expand the list on onLoad
11549                      *  so command out it
11550                      */
11551 //                    this.expand();
11552                 }
11553             }else{
11554                 this.selectedIndex = -1;
11555                 this.onLoad();   
11556             }
11557         }
11558         
11559         this.loadNext = false;
11560     },
11561
11562     // private
11563     getParams : function(q){
11564         var p = {};
11565         //p[this.queryParam] = q;
11566         
11567         if(this.pageSize){
11568             p.start = 0;
11569             p.limit = this.pageSize;
11570         }
11571         return p;
11572     },
11573
11574     /**
11575      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11576      */
11577     collapse : function(){
11578         if(!this.isExpanded()){
11579             return;
11580         }
11581         
11582         this.list.hide();
11583         
11584         if(this.tickable){
11585             this.okBtn.hide();
11586             this.cancelBtn.hide();
11587             this.trigger.show();
11588         }
11589         
11590         Roo.get(document).un('mousedown', this.collapseIf, this);
11591         Roo.get(document).un('mousewheel', this.collapseIf, this);
11592         if (!this.editable) {
11593             Roo.get(document).un('keydown', this.listKeyPress, this);
11594         }
11595         this.fireEvent('collapse', this);
11596     },
11597
11598     // private
11599     collapseIf : function(e){
11600         var in_combo  = e.within(this.el);
11601         var in_list =  e.within(this.list);
11602         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11603         
11604         if (in_combo || in_list || is_list) {
11605             //e.stopPropagation();
11606             return;
11607         }
11608         
11609         if(this.tickable){
11610             this.onTickableFooterButtonClick(e, false, false);
11611         }
11612
11613         this.collapse();
11614         
11615     },
11616
11617     /**
11618      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11619      */
11620     expand : function(){
11621        
11622         if(this.isExpanded() || !this.hasFocus){
11623             return;
11624         }
11625         
11626         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11627         this.list.setWidth(lw);
11628         
11629         
11630          Roo.log('expand');
11631         
11632         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11633         this.list.setWidth(lw);
11634             
11635         this.list.show();
11636         
11637         this.restrictHeight();
11638         
11639         if(this.tickable){
11640             
11641             this.tickItems = Roo.apply([], this.item);
11642             
11643             this.okBtn.show();
11644             this.cancelBtn.show();
11645             this.trigger.hide();
11646             
11647         }
11648         
11649         Roo.get(document).on('mousedown', this.collapseIf, this);
11650         Roo.get(document).on('mousewheel', this.collapseIf, this);
11651         if (!this.editable) {
11652             Roo.get(document).on('keydown', this.listKeyPress, this);
11653         }
11654         
11655         this.fireEvent('expand', this);
11656     },
11657
11658     // private
11659     // Implements the default empty TriggerField.onTriggerClick function
11660     onTriggerClick : function(e)
11661     {
11662         Roo.log('trigger click');
11663         
11664         if(this.disabled || !this.triggerList){
11665             return;
11666         }
11667         
11668         this.page = 0;
11669         this.loadNext = false;
11670         
11671         if(this.isExpanded()){
11672             this.collapse();
11673             if (!this.blockFocus) {
11674                 this.inputEl().focus();
11675             }
11676             
11677         }else {
11678             this.hasFocus = true;
11679             if(this.triggerAction == 'all') {
11680                 this.doQuery(this.allQuery, true);
11681             } else {
11682                 this.doQuery(this.getRawValue());
11683             }
11684             if (!this.blockFocus) {
11685                 this.inputEl().focus();
11686             }
11687         }
11688     },
11689     
11690     onTickableTriggerClick : function(e)
11691     {
11692         if(this.disabled){
11693             return;
11694         }
11695         
11696         this.page = 0;
11697         this.loadNext = false;
11698         this.hasFocus = true;
11699         
11700         if(this.triggerAction == 'all') {
11701             this.doQuery(this.allQuery, true);
11702         } else {
11703             this.doQuery(this.getRawValue());
11704         }
11705     },
11706     
11707     onSearchFieldClick : function(e)
11708     {
11709         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11710             return;
11711         }
11712         
11713         this.page = 0;
11714         this.loadNext = false;
11715         this.hasFocus = true;
11716         
11717         if(this.triggerAction == 'all') {
11718             this.doQuery(this.allQuery, true);
11719         } else {
11720             this.doQuery(this.getRawValue());
11721         }
11722     },
11723     
11724     listKeyPress : function(e)
11725     {
11726         //Roo.log('listkeypress');
11727         // scroll to first matching element based on key pres..
11728         if (e.isSpecialKey()) {
11729             return false;
11730         }
11731         var k = String.fromCharCode(e.getKey()).toUpperCase();
11732         //Roo.log(k);
11733         var match  = false;
11734         var csel = this.view.getSelectedNodes();
11735         var cselitem = false;
11736         if (csel.length) {
11737             var ix = this.view.indexOf(csel[0]);
11738             cselitem  = this.store.getAt(ix);
11739             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11740                 cselitem = false;
11741             }
11742             
11743         }
11744         
11745         this.store.each(function(v) { 
11746             if (cselitem) {
11747                 // start at existing selection.
11748                 if (cselitem.id == v.id) {
11749                     cselitem = false;
11750                 }
11751                 return true;
11752             }
11753                 
11754             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11755                 match = this.store.indexOf(v);
11756                 return false;
11757             }
11758             return true;
11759         }, this);
11760         
11761         if (match === false) {
11762             return true; // no more action?
11763         }
11764         // scroll to?
11765         this.view.select(match);
11766         var sn = Roo.get(this.view.getSelectedNodes()[0])
11767         //sn.scrollIntoView(sn.dom.parentNode, false);
11768     },
11769     
11770     onViewScroll : function(e, t){
11771         
11772         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){
11773             return;
11774         }
11775         
11776         this.hasQuery = true;
11777         
11778         this.loading = this.list.select('.loading', true).first();
11779         
11780         if(this.loading === null){
11781             this.list.createChild({
11782                 tag: 'div',
11783                 cls: 'loading select2-more-results select2-active',
11784                 html: 'Loading more results...'
11785             })
11786             
11787             this.loading = this.list.select('.loading', true).first();
11788             
11789             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11790             
11791             this.loading.hide();
11792         }
11793         
11794         this.loading.show();
11795         
11796         var _combo = this;
11797         
11798         this.page++;
11799         this.loadNext = true;
11800         
11801         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11802         
11803         return;
11804     },
11805     
11806     addItem : function(o)
11807     {   
11808         var dv = ''; // display value
11809         
11810         if (this.displayField) {
11811             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11812         } else {
11813             // this is an error condition!!!
11814             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11815         }
11816         
11817         if(!dv.length){
11818             return;
11819         }
11820         
11821         var choice = this.choices.createChild({
11822             tag: 'li',
11823             cls: 'select2-search-choice',
11824             cn: [
11825                 {
11826                     tag: 'div',
11827                     html: dv
11828                 },
11829                 {
11830                     tag: 'a',
11831                     href: '#',
11832                     cls: 'select2-search-choice-close',
11833                     tabindex: '-1'
11834                 }
11835             ]
11836             
11837         }, this.searchField);
11838         
11839         var close = choice.select('a.select2-search-choice-close', true).first()
11840         
11841         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11842         
11843         this.item.push(o);
11844         
11845         this.lastData = o;
11846         
11847         this.syncValue();
11848         
11849         this.inputEl().dom.value = '';
11850         
11851     },
11852     
11853     onRemoveItem : function(e, _self, o)
11854     {
11855         e.preventDefault();
11856         var index = this.item.indexOf(o.data) * 1;
11857         
11858         if( index < 0){
11859             Roo.log('not this item?!');
11860             return;
11861         }
11862         
11863         this.item.splice(index, 1);
11864         o.item.remove();
11865         
11866         this.syncValue();
11867         
11868         this.fireEvent('remove', this, e);
11869         
11870     },
11871     
11872     syncValue : function()
11873     {
11874         if(!this.item.length){
11875             this.clearValue();
11876             return;
11877         }
11878             
11879         var value = [];
11880         var _this = this;
11881         Roo.each(this.item, function(i){
11882             if(_this.valueField){
11883                 value.push(i[_this.valueField]);
11884                 return;
11885             }
11886
11887             value.push(i);
11888         });
11889
11890         this.value = value.join(',');
11891
11892         if(this.hiddenField){
11893             this.hiddenField.dom.value = this.value;
11894         }
11895     },
11896     
11897     clearItem : function()
11898     {
11899         if(!this.multiple){
11900             return;
11901         }
11902         
11903         this.item = [];
11904         
11905         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11906            c.remove();
11907         });
11908         
11909         this.syncValue();
11910     },
11911     
11912     inputEl: function ()
11913     {
11914         if(this.tickable){
11915             return this.searchField;
11916         }
11917         return this.el.select('input.form-control',true).first();
11918     },
11919     
11920     
11921     onTickableFooterButtonClick : function(e, btn, el)
11922     {
11923         e.preventDefault();
11924         
11925         if(btn && btn.name == 'cancel'){
11926             this.tickItems = Roo.apply([], this.item);
11927             this.collapse();
11928             return;
11929         }
11930         
11931         this.clearItem();
11932         
11933         var _this = this;
11934         
11935         Roo.each(this.tickItems, function(o){
11936             _this.addItem(o);
11937         });
11938         
11939         this.collapse();
11940         
11941     }
11942     
11943     
11944
11945     /** 
11946     * @cfg {Boolean} grow 
11947     * @hide 
11948     */
11949     /** 
11950     * @cfg {Number} growMin 
11951     * @hide 
11952     */
11953     /** 
11954     * @cfg {Number} growMax 
11955     * @hide 
11956     */
11957     /**
11958      * @hide
11959      * @method autoSize
11960      */
11961 });
11962 /*
11963  * Based on:
11964  * Ext JS Library 1.1.1
11965  * Copyright(c) 2006-2007, Ext JS, LLC.
11966  *
11967  * Originally Released Under LGPL - original licence link has changed is not relivant.
11968  *
11969  * Fork - LGPL
11970  * <script type="text/javascript">
11971  */
11972
11973 /**
11974  * @class Roo.View
11975  * @extends Roo.util.Observable
11976  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11977  * This class also supports single and multi selection modes. <br>
11978  * Create a data model bound view:
11979  <pre><code>
11980  var store = new Roo.data.Store(...);
11981
11982  var view = new Roo.View({
11983     el : "my-element",
11984     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11985  
11986     singleSelect: true,
11987     selectedClass: "ydataview-selected",
11988     store: store
11989  });
11990
11991  // listen for node click?
11992  view.on("click", function(vw, index, node, e){
11993  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11994  });
11995
11996  // load XML data
11997  dataModel.load("foobar.xml");
11998  </code></pre>
11999  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12000  * <br><br>
12001  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12002  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12003  * 
12004  * Note: old style constructor is still suported (container, template, config)
12005  * 
12006  * @constructor
12007  * Create a new View
12008  * @param {Object} config The config object
12009  * 
12010  */
12011 Roo.View = function(config, depreciated_tpl, depreciated_config){
12012     
12013     this.parent = false;
12014     
12015     if (typeof(depreciated_tpl) == 'undefined') {
12016         // new way.. - universal constructor.
12017         Roo.apply(this, config);
12018         this.el  = Roo.get(this.el);
12019     } else {
12020         // old format..
12021         this.el  = Roo.get(config);
12022         this.tpl = depreciated_tpl;
12023         Roo.apply(this, depreciated_config);
12024     }
12025     this.wrapEl  = this.el.wrap().wrap();
12026     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12027     
12028     
12029     if(typeof(this.tpl) == "string"){
12030         this.tpl = new Roo.Template(this.tpl);
12031     } else {
12032         // support xtype ctors..
12033         this.tpl = new Roo.factory(this.tpl, Roo);
12034     }
12035     
12036     
12037     this.tpl.compile();
12038     
12039     /** @private */
12040     this.addEvents({
12041         /**
12042          * @event beforeclick
12043          * Fires before a click is processed. Returns false to cancel the default action.
12044          * @param {Roo.View} this
12045          * @param {Number} index The index of the target node
12046          * @param {HTMLElement} node The target node
12047          * @param {Roo.EventObject} e The raw event object
12048          */
12049             "beforeclick" : true,
12050         /**
12051          * @event click
12052          * Fires when a template node is clicked.
12053          * @param {Roo.View} this
12054          * @param {Number} index The index of the target node
12055          * @param {HTMLElement} node The target node
12056          * @param {Roo.EventObject} e The raw event object
12057          */
12058             "click" : true,
12059         /**
12060          * @event dblclick
12061          * Fires when a template node is double clicked.
12062          * @param {Roo.View} this
12063          * @param {Number} index The index of the target node
12064          * @param {HTMLElement} node The target node
12065          * @param {Roo.EventObject} e The raw event object
12066          */
12067             "dblclick" : true,
12068         /**
12069          * @event contextmenu
12070          * Fires when a template node is right clicked.
12071          * @param {Roo.View} this
12072          * @param {Number} index The index of the target node
12073          * @param {HTMLElement} node The target node
12074          * @param {Roo.EventObject} e The raw event object
12075          */
12076             "contextmenu" : true,
12077         /**
12078          * @event selectionchange
12079          * Fires when the selected nodes change.
12080          * @param {Roo.View} this
12081          * @param {Array} selections Array of the selected nodes
12082          */
12083             "selectionchange" : true,
12084     
12085         /**
12086          * @event beforeselect
12087          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12088          * @param {Roo.View} this
12089          * @param {HTMLElement} node The node to be selected
12090          * @param {Array} selections Array of currently selected nodes
12091          */
12092             "beforeselect" : true,
12093         /**
12094          * @event preparedata
12095          * Fires on every row to render, to allow you to change the data.
12096          * @param {Roo.View} this
12097          * @param {Object} data to be rendered (change this)
12098          */
12099           "preparedata" : true
12100           
12101           
12102         });
12103
12104
12105
12106     this.el.on({
12107         "click": this.onClick,
12108         "dblclick": this.onDblClick,
12109         "contextmenu": this.onContextMenu,
12110         scope:this
12111     });
12112
12113     this.selections = [];
12114     this.nodes = [];
12115     this.cmp = new Roo.CompositeElementLite([]);
12116     if(this.store){
12117         this.store = Roo.factory(this.store, Roo.data);
12118         this.setStore(this.store, true);
12119     }
12120     
12121     if ( this.footer && this.footer.xtype) {
12122            
12123          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12124         
12125         this.footer.dataSource = this.store
12126         this.footer.container = fctr;
12127         this.footer = Roo.factory(this.footer, Roo);
12128         fctr.insertFirst(this.el);
12129         
12130         // this is a bit insane - as the paging toolbar seems to detach the el..
12131 //        dom.parentNode.parentNode.parentNode
12132          // they get detached?
12133     }
12134     
12135     
12136     Roo.View.superclass.constructor.call(this);
12137     
12138     
12139 };
12140
12141 Roo.extend(Roo.View, Roo.util.Observable, {
12142     
12143      /**
12144      * @cfg {Roo.data.Store} store Data store to load data from.
12145      */
12146     store : false,
12147     
12148     /**
12149      * @cfg {String|Roo.Element} el The container element.
12150      */
12151     el : '',
12152     
12153     /**
12154      * @cfg {String|Roo.Template} tpl The template used by this View 
12155      */
12156     tpl : false,
12157     /**
12158      * @cfg {String} dataName the named area of the template to use as the data area
12159      *                          Works with domtemplates roo-name="name"
12160      */
12161     dataName: false,
12162     /**
12163      * @cfg {String} selectedClass The css class to add to selected nodes
12164      */
12165     selectedClass : "x-view-selected",
12166      /**
12167      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12168      */
12169     emptyText : "",
12170     
12171     /**
12172      * @cfg {String} text to display on mask (default Loading)
12173      */
12174     mask : false,
12175     /**
12176      * @cfg {Boolean} multiSelect Allow multiple selection
12177      */
12178     multiSelect : false,
12179     /**
12180      * @cfg {Boolean} singleSelect Allow single selection
12181      */
12182     singleSelect:  false,
12183     
12184     /**
12185      * @cfg {Boolean} toggleSelect - selecting 
12186      */
12187     toggleSelect : false,
12188     
12189     /**
12190      * @cfg {Boolean} tickable - selecting 
12191      */
12192     tickable : false,
12193     
12194     /**
12195      * Returns the element this view is bound to.
12196      * @return {Roo.Element}
12197      */
12198     getEl : function(){
12199         return this.wrapEl;
12200     },
12201     
12202     
12203
12204     /**
12205      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12206      */
12207     refresh : function(){
12208         Roo.log('refresh');
12209         var t = this.tpl;
12210         
12211         // if we are using something like 'domtemplate', then
12212         // the what gets used is:
12213         // t.applySubtemplate(NAME, data, wrapping data..)
12214         // the outer template then get' applied with
12215         //     the store 'extra data'
12216         // and the body get's added to the
12217         //      roo-name="data" node?
12218         //      <span class='roo-tpl-{name}'></span> ?????
12219         
12220         
12221         
12222         this.clearSelections();
12223         this.el.update("");
12224         var html = [];
12225         var records = this.store.getRange();
12226         if(records.length < 1) {
12227             
12228             // is this valid??  = should it render a template??
12229             
12230             this.el.update(this.emptyText);
12231             return;
12232         }
12233         var el = this.el;
12234         if (this.dataName) {
12235             this.el.update(t.apply(this.store.meta)); //????
12236             el = this.el.child('.roo-tpl-' + this.dataName);
12237         }
12238         
12239         for(var i = 0, len = records.length; i < len; i++){
12240             var data = this.prepareData(records[i].data, i, records[i]);
12241             this.fireEvent("preparedata", this, data, i, records[i]);
12242             
12243             var d = Roo.apply({}, data);
12244             
12245             if(this.tickable){
12246                 Roo.apply(d, {'roo-id' : Roo.id()});
12247                 
12248                 var _this = this;
12249             
12250                 Roo.each(this.parent.item, function(item){
12251                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12252                         return;
12253                     }
12254                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12255                 });
12256             }
12257             
12258             html[html.length] = Roo.util.Format.trim(
12259                 this.dataName ?
12260                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12261                     t.apply(d)
12262             );
12263         }
12264         
12265         
12266         
12267         el.update(html.join(""));
12268         this.nodes = el.dom.childNodes;
12269         this.updateIndexes(0);
12270     },
12271     
12272
12273     /**
12274      * Function to override to reformat the data that is sent to
12275      * the template for each node.
12276      * DEPRICATED - use the preparedata event handler.
12277      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12278      * a JSON object for an UpdateManager bound view).
12279      */
12280     prepareData : function(data, index, record)
12281     {
12282         this.fireEvent("preparedata", this, data, index, record);
12283         return data;
12284     },
12285
12286     onUpdate : function(ds, record){
12287          Roo.log('on update');   
12288         this.clearSelections();
12289         var index = this.store.indexOf(record);
12290         var n = this.nodes[index];
12291         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12292         n.parentNode.removeChild(n);
12293         this.updateIndexes(index, index);
12294     },
12295
12296     
12297     
12298 // --------- FIXME     
12299     onAdd : function(ds, records, index)
12300     {
12301         Roo.log(['on Add', ds, records, index] );        
12302         this.clearSelections();
12303         if(this.nodes.length == 0){
12304             this.refresh();
12305             return;
12306         }
12307         var n = this.nodes[index];
12308         for(var i = 0, len = records.length; i < len; i++){
12309             var d = this.prepareData(records[i].data, i, records[i]);
12310             if(n){
12311                 this.tpl.insertBefore(n, d);
12312             }else{
12313                 
12314                 this.tpl.append(this.el, d);
12315             }
12316         }
12317         this.updateIndexes(index);
12318     },
12319
12320     onRemove : function(ds, record, index){
12321         Roo.log('onRemove');
12322         this.clearSelections();
12323         var el = this.dataName  ?
12324             this.el.child('.roo-tpl-' + this.dataName) :
12325             this.el; 
12326         
12327         el.dom.removeChild(this.nodes[index]);
12328         this.updateIndexes(index);
12329     },
12330
12331     /**
12332      * Refresh an individual node.
12333      * @param {Number} index
12334      */
12335     refreshNode : function(index){
12336         this.onUpdate(this.store, this.store.getAt(index));
12337     },
12338
12339     updateIndexes : function(startIndex, endIndex){
12340         var ns = this.nodes;
12341         startIndex = startIndex || 0;
12342         endIndex = endIndex || ns.length - 1;
12343         for(var i = startIndex; i <= endIndex; i++){
12344             ns[i].nodeIndex = i;
12345         }
12346     },
12347
12348     /**
12349      * Changes the data store this view uses and refresh the view.
12350      * @param {Store} store
12351      */
12352     setStore : function(store, initial){
12353         if(!initial && this.store){
12354             this.store.un("datachanged", this.refresh);
12355             this.store.un("add", this.onAdd);
12356             this.store.un("remove", this.onRemove);
12357             this.store.un("update", this.onUpdate);
12358             this.store.un("clear", this.refresh);
12359             this.store.un("beforeload", this.onBeforeLoad);
12360             this.store.un("load", this.onLoad);
12361             this.store.un("loadexception", this.onLoad);
12362         }
12363         if(store){
12364           
12365             store.on("datachanged", this.refresh, this);
12366             store.on("add", this.onAdd, this);
12367             store.on("remove", this.onRemove, this);
12368             store.on("update", this.onUpdate, this);
12369             store.on("clear", this.refresh, this);
12370             store.on("beforeload", this.onBeforeLoad, this);
12371             store.on("load", this.onLoad, this);
12372             store.on("loadexception", this.onLoad, this);
12373         }
12374         
12375         if(store){
12376             this.refresh();
12377         }
12378     },
12379     /**
12380      * onbeforeLoad - masks the loading area.
12381      *
12382      */
12383     onBeforeLoad : function(store,opts)
12384     {
12385          Roo.log('onBeforeLoad');   
12386         if (!opts.add) {
12387             this.el.update("");
12388         }
12389         this.el.mask(this.mask ? this.mask : "Loading" ); 
12390     },
12391     onLoad : function ()
12392     {
12393         this.el.unmask();
12394     },
12395     
12396
12397     /**
12398      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12399      * @param {HTMLElement} node
12400      * @return {HTMLElement} The template node
12401      */
12402     findItemFromChild : function(node){
12403         var el = this.dataName  ?
12404             this.el.child('.roo-tpl-' + this.dataName,true) :
12405             this.el.dom; 
12406         
12407         if(!node || node.parentNode == el){
12408                     return node;
12409             }
12410             var p = node.parentNode;
12411             while(p && p != el){
12412             if(p.parentNode == el){
12413                 return p;
12414             }
12415             p = p.parentNode;
12416         }
12417             return null;
12418     },
12419
12420     /** @ignore */
12421     onClick : function(e){
12422         var item = this.findItemFromChild(e.getTarget());
12423         if(item){
12424             var index = this.indexOf(item);
12425             if(this.onItemClick(item, index, e) !== false){
12426                 this.fireEvent("click", this, index, item, e);
12427             }
12428         }else{
12429             this.clearSelections();
12430         }
12431     },
12432
12433     /** @ignore */
12434     onContextMenu : function(e){
12435         var item = this.findItemFromChild(e.getTarget());
12436         if(item){
12437             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12438         }
12439     },
12440
12441     /** @ignore */
12442     onDblClick : function(e){
12443         var item = this.findItemFromChild(e.getTarget());
12444         if(item){
12445             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12446         }
12447     },
12448
12449     onItemClick : function(item, index, e)
12450     {
12451         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12452             return false;
12453         }
12454         if (this.toggleSelect) {
12455             var m = this.isSelected(item) ? 'unselect' : 'select';
12456             Roo.log(m);
12457             var _t = this;
12458             _t[m](item, true, false);
12459             return true;
12460         }
12461         if(this.multiSelect || this.singleSelect){
12462             if(this.multiSelect && e.shiftKey && this.lastSelection){
12463                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12464             }else{
12465                 this.select(item, this.multiSelect && e.ctrlKey);
12466                 this.lastSelection = item;
12467             }
12468             
12469             if(!this.tickable){
12470                 e.preventDefault();
12471             }
12472             
12473         }
12474         return true;
12475     },
12476
12477     /**
12478      * Get the number of selected nodes.
12479      * @return {Number}
12480      */
12481     getSelectionCount : function(){
12482         return this.selections.length;
12483     },
12484
12485     /**
12486      * Get the currently selected nodes.
12487      * @return {Array} An array of HTMLElements
12488      */
12489     getSelectedNodes : function(){
12490         return this.selections;
12491     },
12492
12493     /**
12494      * Get the indexes of the selected nodes.
12495      * @return {Array}
12496      */
12497     getSelectedIndexes : function(){
12498         var indexes = [], s = this.selections;
12499         for(var i = 0, len = s.length; i < len; i++){
12500             indexes.push(s[i].nodeIndex);
12501         }
12502         return indexes;
12503     },
12504
12505     /**
12506      * Clear all selections
12507      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12508      */
12509     clearSelections : function(suppressEvent){
12510         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12511             this.cmp.elements = this.selections;
12512             this.cmp.removeClass(this.selectedClass);
12513             this.selections = [];
12514             if(!suppressEvent){
12515                 this.fireEvent("selectionchange", this, this.selections);
12516             }
12517         }
12518     },
12519
12520     /**
12521      * Returns true if the passed node is selected
12522      * @param {HTMLElement/Number} node The node or node index
12523      * @return {Boolean}
12524      */
12525     isSelected : function(node){
12526         var s = this.selections;
12527         if(s.length < 1){
12528             return false;
12529         }
12530         node = this.getNode(node);
12531         return s.indexOf(node) !== -1;
12532     },
12533
12534     /**
12535      * Selects nodes.
12536      * @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
12537      * @param {Boolean} keepExisting (optional) true to keep existing selections
12538      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12539      */
12540     select : function(nodeInfo, keepExisting, suppressEvent){
12541         if(nodeInfo instanceof Array){
12542             if(!keepExisting){
12543                 this.clearSelections(true);
12544             }
12545             for(var i = 0, len = nodeInfo.length; i < len; i++){
12546                 this.select(nodeInfo[i], true, true);
12547             }
12548             return;
12549         } 
12550         var node = this.getNode(nodeInfo);
12551         if(!node || this.isSelected(node)){
12552             return; // already selected.
12553         }
12554         if(!keepExisting){
12555             this.clearSelections(true);
12556         }
12557         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12558             Roo.fly(node).addClass(this.selectedClass);
12559             this.selections.push(node);
12560             if(!suppressEvent){
12561                 this.fireEvent("selectionchange", this, this.selections);
12562             }
12563         }
12564         
12565         
12566     },
12567       /**
12568      * Unselects nodes.
12569      * @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
12570      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12571      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12572      */
12573     unselect : function(nodeInfo, keepExisting, suppressEvent)
12574     {
12575         if(nodeInfo instanceof Array){
12576             Roo.each(this.selections, function(s) {
12577                 this.unselect(s, nodeInfo);
12578             }, this);
12579             return;
12580         }
12581         var node = this.getNode(nodeInfo);
12582         if(!node || !this.isSelected(node)){
12583             Roo.log("not selected");
12584             return; // not selected.
12585         }
12586         // fireevent???
12587         var ns = [];
12588         Roo.each(this.selections, function(s) {
12589             if (s == node ) {
12590                 Roo.fly(node).removeClass(this.selectedClass);
12591
12592                 return;
12593             }
12594             ns.push(s);
12595         },this);
12596         
12597         this.selections= ns;
12598         this.fireEvent("selectionchange", this, this.selections);
12599     },
12600
12601     /**
12602      * Gets a template node.
12603      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12604      * @return {HTMLElement} The node or null if it wasn't found
12605      */
12606     getNode : function(nodeInfo){
12607         if(typeof nodeInfo == "string"){
12608             return document.getElementById(nodeInfo);
12609         }else if(typeof nodeInfo == "number"){
12610             return this.nodes[nodeInfo];
12611         }
12612         return nodeInfo;
12613     },
12614
12615     /**
12616      * Gets a range template nodes.
12617      * @param {Number} startIndex
12618      * @param {Number} endIndex
12619      * @return {Array} An array of nodes
12620      */
12621     getNodes : function(start, end){
12622         var ns = this.nodes;
12623         start = start || 0;
12624         end = typeof end == "undefined" ? ns.length - 1 : end;
12625         var nodes = [];
12626         if(start <= end){
12627             for(var i = start; i <= end; i++){
12628                 nodes.push(ns[i]);
12629             }
12630         } else{
12631             for(var i = start; i >= end; i--){
12632                 nodes.push(ns[i]);
12633             }
12634         }
12635         return nodes;
12636     },
12637
12638     /**
12639      * Finds the index of the passed node
12640      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12641      * @return {Number} The index of the node or -1
12642      */
12643     indexOf : function(node){
12644         node = this.getNode(node);
12645         if(typeof node.nodeIndex == "number"){
12646             return node.nodeIndex;
12647         }
12648         var ns = this.nodes;
12649         for(var i = 0, len = ns.length; i < len; i++){
12650             if(ns[i] == node){
12651                 return i;
12652             }
12653         }
12654         return -1;
12655     }
12656 });
12657 /*
12658  * - LGPL
12659  *
12660  * based on jquery fullcalendar
12661  * 
12662  */
12663
12664 Roo.bootstrap = Roo.bootstrap || {};
12665 /**
12666  * @class Roo.bootstrap.Calendar
12667  * @extends Roo.bootstrap.Component
12668  * Bootstrap Calendar class
12669  * @cfg {Boolean} loadMask (true|false) default false
12670  * @cfg {Object} header generate the user specific header of the calendar, default false
12671
12672  * @constructor
12673  * Create a new Container
12674  * @param {Object} config The config object
12675  */
12676
12677
12678
12679 Roo.bootstrap.Calendar = function(config){
12680     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12681      this.addEvents({
12682         /**
12683              * @event select
12684              * Fires when a date is selected
12685              * @param {DatePicker} this
12686              * @param {Date} date The selected date
12687              */
12688         'select': true,
12689         /**
12690              * @event monthchange
12691              * Fires when the displayed month changes 
12692              * @param {DatePicker} this
12693              * @param {Date} date The selected month
12694              */
12695         'monthchange': true,
12696         /**
12697              * @event evententer
12698              * Fires when mouse over an event
12699              * @param {Calendar} this
12700              * @param {event} Event
12701              */
12702         'evententer': true,
12703         /**
12704              * @event eventleave
12705              * Fires when the mouse leaves an
12706              * @param {Calendar} this
12707              * @param {event}
12708              */
12709         'eventleave': true,
12710         /**
12711              * @event eventclick
12712              * Fires when the mouse click an
12713              * @param {Calendar} this
12714              * @param {event}
12715              */
12716         'eventclick': true
12717         
12718     });
12719
12720 };
12721
12722 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12723     
12724      /**
12725      * @cfg {Number} startDay
12726      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12727      */
12728     startDay : 0,
12729     
12730     loadMask : false,
12731     
12732     header : false,
12733       
12734     getAutoCreate : function(){
12735         
12736         
12737         var fc_button = function(name, corner, style, content ) {
12738             return Roo.apply({},{
12739                 tag : 'span',
12740                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12741                          (corner.length ?
12742                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12743                             ''
12744                         ),
12745                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12746                 unselectable: 'on'
12747             });
12748         };
12749         
12750         var header = {};
12751         
12752         if(!this.header){
12753             header = {
12754                 tag : 'table',
12755                 cls : 'fc-header',
12756                 style : 'width:100%',
12757                 cn : [
12758                     {
12759                         tag: 'tr',
12760                         cn : [
12761                             {
12762                                 tag : 'td',
12763                                 cls : 'fc-header-left',
12764                                 cn : [
12765                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12766                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12767                                     { tag: 'span', cls: 'fc-header-space' },
12768                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12769
12770
12771                                 ]
12772                             },
12773
12774                             {
12775                                 tag : 'td',
12776                                 cls : 'fc-header-center',
12777                                 cn : [
12778                                     {
12779                                         tag: 'span',
12780                                         cls: 'fc-header-title',
12781                                         cn : {
12782                                             tag: 'H2',
12783                                             html : 'month / year'
12784                                         }
12785                                     }
12786
12787                                 ]
12788                             },
12789                             {
12790                                 tag : 'td',
12791                                 cls : 'fc-header-right',
12792                                 cn : [
12793                               /*      fc_button('month', 'left', '', 'month' ),
12794                                     fc_button('week', '', '', 'week' ),
12795                                     fc_button('day', 'right', '', 'day' )
12796                                 */    
12797
12798                                 ]
12799                             }
12800
12801                         ]
12802                     }
12803                 ]
12804             };
12805         }
12806         
12807         header = this.header;
12808         
12809        
12810         var cal_heads = function() {
12811             var ret = [];
12812             // fixme - handle this.
12813             
12814             for (var i =0; i < Date.dayNames.length; i++) {
12815                 var d = Date.dayNames[i];
12816                 ret.push({
12817                     tag: 'th',
12818                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12819                     html : d.substring(0,3)
12820                 });
12821                 
12822             }
12823             ret[0].cls += ' fc-first';
12824             ret[6].cls += ' fc-last';
12825             return ret;
12826         };
12827         var cal_cell = function(n) {
12828             return  {
12829                 tag: 'td',
12830                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12831                 cn : [
12832                     {
12833                         cn : [
12834                             {
12835                                 cls: 'fc-day-number',
12836                                 html: 'D'
12837                             },
12838                             {
12839                                 cls: 'fc-day-content',
12840                              
12841                                 cn : [
12842                                      {
12843                                         style: 'position: relative;' // height: 17px;
12844                                     }
12845                                 ]
12846                             }
12847                             
12848                             
12849                         ]
12850                     }
12851                 ]
12852                 
12853             }
12854         };
12855         var cal_rows = function() {
12856             
12857             var ret = []
12858             for (var r = 0; r < 6; r++) {
12859                 var row= {
12860                     tag : 'tr',
12861                     cls : 'fc-week',
12862                     cn : []
12863                 };
12864                 
12865                 for (var i =0; i < Date.dayNames.length; i++) {
12866                     var d = Date.dayNames[i];
12867                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12868
12869                 }
12870                 row.cn[0].cls+=' fc-first';
12871                 row.cn[0].cn[0].style = 'min-height:90px';
12872                 row.cn[6].cls+=' fc-last';
12873                 ret.push(row);
12874                 
12875             }
12876             ret[0].cls += ' fc-first';
12877             ret[4].cls += ' fc-prev-last';
12878             ret[5].cls += ' fc-last';
12879             return ret;
12880             
12881         };
12882         
12883         var cal_table = {
12884             tag: 'table',
12885             cls: 'fc-border-separate',
12886             style : 'width:100%',
12887             cellspacing  : 0,
12888             cn : [
12889                 { 
12890                     tag: 'thead',
12891                     cn : [
12892                         { 
12893                             tag: 'tr',
12894                             cls : 'fc-first fc-last',
12895                             cn : cal_heads()
12896                         }
12897                     ]
12898                 },
12899                 { 
12900                     tag: 'tbody',
12901                     cn : cal_rows()
12902                 }
12903                   
12904             ]
12905         };
12906          
12907          var cfg = {
12908             cls : 'fc fc-ltr',
12909             cn : [
12910                 header,
12911                 {
12912                     cls : 'fc-content',
12913                     style : "position: relative;",
12914                     cn : [
12915                         {
12916                             cls : 'fc-view fc-view-month fc-grid',
12917                             style : 'position: relative',
12918                             unselectable : 'on',
12919                             cn : [
12920                                 {
12921                                     cls : 'fc-event-container',
12922                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12923                                 },
12924                                 cal_table
12925                             ]
12926                         }
12927                     ]
12928     
12929                 }
12930            ] 
12931             
12932         };
12933         
12934          
12935         
12936         return cfg;
12937     },
12938     
12939     
12940     initEvents : function()
12941     {
12942         if(!this.store){
12943             throw "can not find store for calendar";
12944         }
12945         
12946         var mark = {
12947             tag: "div",
12948             cls:"x-dlg-mask",
12949             style: "text-align:center",
12950             cn: [
12951                 {
12952                     tag: "div",
12953                     style: "background-color:white;width:50%;margin:250 auto",
12954                     cn: [
12955                         {
12956                             tag: "img",
12957                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12958                         },
12959                         {
12960                             tag: "span",
12961                             html: "Loading"
12962                         }
12963                         
12964                     ]
12965                 }
12966             ]
12967         }
12968         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12969         
12970         var size = this.el.select('.fc-content', true).first().getSize();
12971         this.maskEl.setSize(size.width, size.height);
12972         this.maskEl.enableDisplayMode("block");
12973         if(!this.loadMask){
12974             this.maskEl.hide();
12975         }
12976         
12977         this.store = Roo.factory(this.store, Roo.data);
12978         this.store.on('load', this.onLoad, this);
12979         this.store.on('beforeload', this.onBeforeLoad, this);
12980         
12981         this.resize();
12982         
12983         this.cells = this.el.select('.fc-day',true);
12984         //Roo.log(this.cells);
12985         this.textNodes = this.el.query('.fc-day-number');
12986         this.cells.addClassOnOver('fc-state-hover');
12987         
12988         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12989         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12990         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12991         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12992         
12993         this.on('monthchange', this.onMonthChange, this);
12994         
12995         this.update(new Date().clearTime());
12996     },
12997     
12998     resize : function() {
12999         var sz  = this.el.getSize();
13000         
13001         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13002         this.el.select('.fc-day-content div',true).setHeight(34);
13003     },
13004     
13005     
13006     // private
13007     showPrevMonth : function(e){
13008         this.update(this.activeDate.add("mo", -1));
13009     },
13010     showToday : function(e){
13011         this.update(new Date().clearTime());
13012     },
13013     // private
13014     showNextMonth : function(e){
13015         this.update(this.activeDate.add("mo", 1));
13016     },
13017
13018     // private
13019     showPrevYear : function(){
13020         this.update(this.activeDate.add("y", -1));
13021     },
13022
13023     // private
13024     showNextYear : function(){
13025         this.update(this.activeDate.add("y", 1));
13026     },
13027
13028     
13029    // private
13030     update : function(date)
13031     {
13032         var vd = this.activeDate;
13033         this.activeDate = date;
13034 //        if(vd && this.el){
13035 //            var t = date.getTime();
13036 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13037 //                Roo.log('using add remove');
13038 //                
13039 //                this.fireEvent('monthchange', this, date);
13040 //                
13041 //                this.cells.removeClass("fc-state-highlight");
13042 //                this.cells.each(function(c){
13043 //                   if(c.dateValue == t){
13044 //                       c.addClass("fc-state-highlight");
13045 //                       setTimeout(function(){
13046 //                            try{c.dom.firstChild.focus();}catch(e){}
13047 //                       }, 50);
13048 //                       return false;
13049 //                   }
13050 //                   return true;
13051 //                });
13052 //                return;
13053 //            }
13054 //        }
13055         
13056         var days = date.getDaysInMonth();
13057         
13058         var firstOfMonth = date.getFirstDateOfMonth();
13059         var startingPos = firstOfMonth.getDay()-this.startDay;
13060         
13061         if(startingPos < this.startDay){
13062             startingPos += 7;
13063         }
13064         
13065         var pm = date.add(Date.MONTH, -1);
13066         var prevStart = pm.getDaysInMonth()-startingPos;
13067 //        
13068         this.cells = this.el.select('.fc-day',true);
13069         this.textNodes = this.el.query('.fc-day-number');
13070         this.cells.addClassOnOver('fc-state-hover');
13071         
13072         var cells = this.cells.elements;
13073         var textEls = this.textNodes;
13074         
13075         Roo.each(cells, function(cell){
13076             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13077         });
13078         
13079         days += startingPos;
13080
13081         // convert everything to numbers so it's fast
13082         var day = 86400000;
13083         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13084         //Roo.log(d);
13085         //Roo.log(pm);
13086         //Roo.log(prevStart);
13087         
13088         var today = new Date().clearTime().getTime();
13089         var sel = date.clearTime().getTime();
13090         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13091         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13092         var ddMatch = this.disabledDatesRE;
13093         var ddText = this.disabledDatesText;
13094         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13095         var ddaysText = this.disabledDaysText;
13096         var format = this.format;
13097         
13098         var setCellClass = function(cal, cell){
13099             cell.row = 0;
13100             cell.events = [];
13101             cell.more = [];
13102             //Roo.log('set Cell Class');
13103             cell.title = "";
13104             var t = d.getTime();
13105             
13106             //Roo.log(d);
13107             
13108             cell.dateValue = t;
13109             if(t == today){
13110                 cell.className += " fc-today";
13111                 cell.className += " fc-state-highlight";
13112                 cell.title = cal.todayText;
13113             }
13114             if(t == sel){
13115                 // disable highlight in other month..
13116                 //cell.className += " fc-state-highlight";
13117                 
13118             }
13119             // disabling
13120             if(t < min) {
13121                 cell.className = " fc-state-disabled";
13122                 cell.title = cal.minText;
13123                 return;
13124             }
13125             if(t > max) {
13126                 cell.className = " fc-state-disabled";
13127                 cell.title = cal.maxText;
13128                 return;
13129             }
13130             if(ddays){
13131                 if(ddays.indexOf(d.getDay()) != -1){
13132                     cell.title = ddaysText;
13133                     cell.className = " fc-state-disabled";
13134                 }
13135             }
13136             if(ddMatch && format){
13137                 var fvalue = d.dateFormat(format);
13138                 if(ddMatch.test(fvalue)){
13139                     cell.title = ddText.replace("%0", fvalue);
13140                     cell.className = " fc-state-disabled";
13141                 }
13142             }
13143             
13144             if (!cell.initialClassName) {
13145                 cell.initialClassName = cell.dom.className;
13146             }
13147             
13148             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13149         };
13150
13151         var i = 0;
13152         
13153         for(; i < startingPos; i++) {
13154             textEls[i].innerHTML = (++prevStart);
13155             d.setDate(d.getDate()+1);
13156             
13157             cells[i].className = "fc-past fc-other-month";
13158             setCellClass(this, cells[i]);
13159         }
13160         
13161         var intDay = 0;
13162         
13163         for(; i < days; i++){
13164             intDay = i - startingPos + 1;
13165             textEls[i].innerHTML = (intDay);
13166             d.setDate(d.getDate()+1);
13167             
13168             cells[i].className = ''; // "x-date-active";
13169             setCellClass(this, cells[i]);
13170         }
13171         var extraDays = 0;
13172         
13173         for(; i < 42; i++) {
13174             textEls[i].innerHTML = (++extraDays);
13175             d.setDate(d.getDate()+1);
13176             
13177             cells[i].className = "fc-future fc-other-month";
13178             setCellClass(this, cells[i]);
13179         }
13180         
13181         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13182         
13183         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13184         
13185         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13186         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13187         
13188         if(totalRows != 6){
13189             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13190             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13191         }
13192         
13193         this.fireEvent('monthchange', this, date);
13194         
13195         
13196         /*
13197         if(!this.internalRender){
13198             var main = this.el.dom.firstChild;
13199             var w = main.offsetWidth;
13200             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13201             Roo.fly(main).setWidth(w);
13202             this.internalRender = true;
13203             // opera does not respect the auto grow header center column
13204             // then, after it gets a width opera refuses to recalculate
13205             // without a second pass
13206             if(Roo.isOpera && !this.secondPass){
13207                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13208                 this.secondPass = true;
13209                 this.update.defer(10, this, [date]);
13210             }
13211         }
13212         */
13213         
13214     },
13215     
13216     findCell : function(dt) {
13217         dt = dt.clearTime().getTime();
13218         var ret = false;
13219         this.cells.each(function(c){
13220             //Roo.log("check " +c.dateValue + '?=' + dt);
13221             if(c.dateValue == dt){
13222                 ret = c;
13223                 return false;
13224             }
13225             return true;
13226         });
13227         
13228         return ret;
13229     },
13230     
13231     findCells : function(ev) {
13232         var s = ev.start.clone().clearTime().getTime();
13233        // Roo.log(s);
13234         var e= ev.end.clone().clearTime().getTime();
13235        // Roo.log(e);
13236         var ret = [];
13237         this.cells.each(function(c){
13238              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13239             
13240             if(c.dateValue > e){
13241                 return ;
13242             }
13243             if(c.dateValue < s){
13244                 return ;
13245             }
13246             ret.push(c);
13247         });
13248         
13249         return ret;    
13250     },
13251     
13252 //    findBestRow: function(cells)
13253 //    {
13254 //        var ret = 0;
13255 //        
13256 //        for (var i =0 ; i < cells.length;i++) {
13257 //            ret  = Math.max(cells[i].rows || 0,ret);
13258 //        }
13259 //        return ret;
13260 //        
13261 //    },
13262     
13263     
13264     addItem : function(ev)
13265     {
13266         // look for vertical location slot in
13267         var cells = this.findCells(ev);
13268         
13269 //        ev.row = this.findBestRow(cells);
13270         
13271         // work out the location.
13272         
13273         var crow = false;
13274         var rows = [];
13275         for(var i =0; i < cells.length; i++) {
13276             
13277             cells[i].row = cells[0].row;
13278             
13279             if(i == 0){
13280                 cells[i].row = cells[i].row + 1;
13281             }
13282             
13283             if (!crow) {
13284                 crow = {
13285                     start : cells[i],
13286                     end :  cells[i]
13287                 };
13288                 continue;
13289             }
13290             if (crow.start.getY() == cells[i].getY()) {
13291                 // on same row.
13292                 crow.end = cells[i];
13293                 continue;
13294             }
13295             // different row.
13296             rows.push(crow);
13297             crow = {
13298                 start: cells[i],
13299                 end : cells[i]
13300             };
13301             
13302         }
13303         
13304         rows.push(crow);
13305         ev.els = [];
13306         ev.rows = rows;
13307         ev.cells = cells;
13308         
13309         cells[0].events.push(ev);
13310         
13311         this.calevents.push(ev);
13312     },
13313     
13314     clearEvents: function() {
13315         
13316         if(!this.calevents){
13317             return;
13318         }
13319         
13320         Roo.each(this.cells.elements, function(c){
13321             c.row = 0;
13322             c.events = [];
13323             c.more = [];
13324         });
13325         
13326         Roo.each(this.calevents, function(e) {
13327             Roo.each(e.els, function(el) {
13328                 el.un('mouseenter' ,this.onEventEnter, this);
13329                 el.un('mouseleave' ,this.onEventLeave, this);
13330                 el.remove();
13331             },this);
13332         },this);
13333         
13334         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13335             e.remove();
13336         });
13337         
13338     },
13339     
13340     renderEvents: function()
13341     {   
13342         var _this = this;
13343         
13344         this.cells.each(function(c) {
13345             
13346             if(c.row < 5){
13347                 return;
13348             }
13349             
13350             var ev = c.events;
13351             
13352             var r = 4;
13353             if(c.row != c.events.length){
13354                 r = 4 - (4 - (c.row - c.events.length));
13355             }
13356             
13357             c.events = ev.slice(0, r);
13358             c.more = ev.slice(r);
13359             
13360             if(c.more.length && c.more.length == 1){
13361                 c.events.push(c.more.pop());
13362             }
13363             
13364             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13365             
13366         });
13367             
13368         this.cells.each(function(c) {
13369             
13370             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13371             
13372             
13373             for (var e = 0; e < c.events.length; e++){
13374                 var ev = c.events[e];
13375                 var rows = ev.rows;
13376                 
13377                 for(var i = 0; i < rows.length; i++) {
13378                 
13379                     // how many rows should it span..
13380
13381                     var  cfg = {
13382                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13383                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13384
13385                         unselectable : "on",
13386                         cn : [
13387                             {
13388                                 cls: 'fc-event-inner',
13389                                 cn : [
13390     //                                {
13391     //                                  tag:'span',
13392     //                                  cls: 'fc-event-time',
13393     //                                  html : cells.length > 1 ? '' : ev.time
13394     //                                },
13395                                     {
13396                                       tag:'span',
13397                                       cls: 'fc-event-title',
13398                                       html : String.format('{0}', ev.title)
13399                                     }
13400
13401
13402                                 ]
13403                             },
13404                             {
13405                                 cls: 'ui-resizable-handle ui-resizable-e',
13406                                 html : '&nbsp;&nbsp;&nbsp'
13407                             }
13408
13409                         ]
13410                     };
13411
13412                     if (i == 0) {
13413                         cfg.cls += ' fc-event-start';
13414                     }
13415                     if ((i+1) == rows.length) {
13416                         cfg.cls += ' fc-event-end';
13417                     }
13418
13419                     var ctr = _this.el.select('.fc-event-container',true).first();
13420                     var cg = ctr.createChild(cfg);
13421
13422                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13423                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13424
13425                     var r = (c.more.length) ? 1 : 0;
13426                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13427                     cg.setWidth(ebox.right - sbox.x -2);
13428
13429                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13430                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13431                     cg.on('click', _this.onEventClick, _this, ev);
13432
13433                     ev.els.push(cg);
13434                     
13435                 }
13436                 
13437             }
13438             
13439             
13440             if(c.more.length){
13441                 var  cfg = {
13442                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13443                     style : 'position: absolute',
13444                     unselectable : "on",
13445                     cn : [
13446                         {
13447                             cls: 'fc-event-inner',
13448                             cn : [
13449                                 {
13450                                   tag:'span',
13451                                   cls: 'fc-event-title',
13452                                   html : 'More'
13453                                 }
13454
13455
13456                             ]
13457                         },
13458                         {
13459                             cls: 'ui-resizable-handle ui-resizable-e',
13460                             html : '&nbsp;&nbsp;&nbsp'
13461                         }
13462
13463                     ]
13464                 };
13465
13466                 var ctr = _this.el.select('.fc-event-container',true).first();
13467                 var cg = ctr.createChild(cfg);
13468
13469                 var sbox = c.select('.fc-day-content',true).first().getBox();
13470                 var ebox = c.select('.fc-day-content',true).first().getBox();
13471                 //Roo.log(cg);
13472                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13473                 cg.setWidth(ebox.right - sbox.x -2);
13474
13475                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13476                 
13477             }
13478             
13479         });
13480         
13481         
13482         
13483     },
13484     
13485     onEventEnter: function (e, el,event,d) {
13486         this.fireEvent('evententer', this, el, event);
13487     },
13488     
13489     onEventLeave: function (e, el,event,d) {
13490         this.fireEvent('eventleave', this, el, event);
13491     },
13492     
13493     onEventClick: function (e, el,event,d) {
13494         this.fireEvent('eventclick', this, el, event);
13495     },
13496     
13497     onMonthChange: function () {
13498         this.store.load();
13499     },
13500     
13501     onMoreEventClick: function(e, el, more)
13502     {
13503         var _this = this;
13504         
13505         this.calpopover.placement = 'right';
13506         this.calpopover.setTitle('More');
13507         
13508         this.calpopover.setContent('');
13509         
13510         var ctr = this.calpopover.el.select('.popover-content', true).first();
13511         
13512         Roo.each(more, function(m){
13513             var cfg = {
13514                 cls : 'fc-event-hori fc-event-draggable',
13515                 html : m.title
13516             }
13517             var cg = ctr.createChild(cfg);
13518             
13519             cg.on('click', _this.onEventClick, _this, m);
13520         });
13521         
13522         this.calpopover.show(el);
13523         
13524         
13525     },
13526     
13527     onLoad: function () 
13528     {   
13529         this.calevents = [];
13530         var cal = this;
13531         
13532         if(this.store.getCount() > 0){
13533             this.store.data.each(function(d){
13534                cal.addItem({
13535                     id : d.data.id,
13536                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13537                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13538                     time : d.data.start_time,
13539                     title : d.data.title,
13540                     description : d.data.description,
13541                     venue : d.data.venue
13542                 });
13543             });
13544         }
13545         
13546         this.renderEvents();
13547         
13548         if(this.calevents.length && this.loadMask){
13549             this.maskEl.hide();
13550         }
13551     },
13552     
13553     onBeforeLoad: function()
13554     {
13555         this.clearEvents();
13556         if(this.loadMask){
13557             this.maskEl.show();
13558         }
13559     }
13560 });
13561
13562  
13563  /*
13564  * - LGPL
13565  *
13566  * element
13567  * 
13568  */
13569
13570 /**
13571  * @class Roo.bootstrap.Popover
13572  * @extends Roo.bootstrap.Component
13573  * Bootstrap Popover class
13574  * @cfg {String} html contents of the popover   (or false to use children..)
13575  * @cfg {String} title of popover (or false to hide)
13576  * @cfg {String} placement how it is placed
13577  * @cfg {String} trigger click || hover (or false to trigger manually)
13578  * @cfg {String} over what (parent or false to trigger manually.)
13579  * 
13580  * @constructor
13581  * Create a new Popover
13582  * @param {Object} config The config object
13583  */
13584
13585 Roo.bootstrap.Popover = function(config){
13586     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13587 };
13588
13589 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13590     
13591     title: 'Fill in a title',
13592     html: false,
13593     
13594     placement : 'right',
13595     trigger : 'hover', // hover
13596     
13597     over: 'parent',
13598     
13599     can_build_overlaid : false,
13600     
13601     getChildContainer : function()
13602     {
13603         return this.el.select('.popover-content',true).first();
13604     },
13605     
13606     getAutoCreate : function(){
13607          Roo.log('make popover?');
13608         var cfg = {
13609            cls : 'popover roo-dynamic',
13610            style: 'display:block',
13611            cn : [
13612                 {
13613                     cls : 'arrow'
13614                 },
13615                 {
13616                     cls : 'popover-inner',
13617                     cn : [
13618                         {
13619                             tag: 'h3',
13620                             cls: 'popover-title',
13621                             html : this.title
13622                         },
13623                         {
13624                             cls : 'popover-content',
13625                             html : this.html
13626                         }
13627                     ]
13628                     
13629                 }
13630            ]
13631         };
13632         
13633         return cfg;
13634     },
13635     setTitle: function(str)
13636     {
13637         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13638     },
13639     setContent: function(str)
13640     {
13641         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13642     },
13643     // as it get's added to the bottom of the page.
13644     onRender : function(ct, position)
13645     {
13646         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13647         if(!this.el){
13648             var cfg = Roo.apply({},  this.getAutoCreate());
13649             cfg.id = Roo.id();
13650             
13651             if (this.cls) {
13652                 cfg.cls += ' ' + this.cls;
13653             }
13654             if (this.style) {
13655                 cfg.style = this.style;
13656             }
13657             Roo.log("adding to ")
13658             this.el = Roo.get(document.body).createChild(cfg, position);
13659             Roo.log(this.el);
13660         }
13661         this.initEvents();
13662     },
13663     
13664     initEvents : function()
13665     {
13666         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13667         this.el.enableDisplayMode('block');
13668         this.el.hide();
13669         if (this.over === false) {
13670             return; 
13671         }
13672         if (this.triggers === false) {
13673             return;
13674         }
13675         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13676         var triggers = this.trigger ? this.trigger.split(' ') : [];
13677         Roo.each(triggers, function(trigger) {
13678         
13679             if (trigger == 'click') {
13680                 on_el.on('click', this.toggle, this);
13681             } else if (trigger != 'manual') {
13682                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13683                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13684       
13685                 on_el.on(eventIn  ,this.enter, this);
13686                 on_el.on(eventOut, this.leave, this);
13687             }
13688         }, this);
13689         
13690     },
13691     
13692     
13693     // private
13694     timeout : null,
13695     hoverState : null,
13696     
13697     toggle : function () {
13698         this.hoverState == 'in' ? this.leave() : this.enter();
13699     },
13700     
13701     enter : function () {
13702        
13703     
13704         clearTimeout(this.timeout);
13705     
13706         this.hoverState = 'in'
13707     
13708         if (!this.delay || !this.delay.show) {
13709             this.show();
13710             return 
13711         }
13712         var _t = this;
13713         this.timeout = setTimeout(function () {
13714             if (_t.hoverState == 'in') {
13715                 _t.show();
13716             }
13717         }, this.delay.show)
13718     },
13719     leave : function() {
13720         clearTimeout(this.timeout);
13721     
13722         this.hoverState = 'out'
13723     
13724         if (!this.delay || !this.delay.hide) {
13725             this.hide();
13726             return 
13727         }
13728         var _t = this;
13729         this.timeout = setTimeout(function () {
13730             if (_t.hoverState == 'out') {
13731                 _t.hide();
13732             }
13733         }, this.delay.hide)
13734     },
13735     
13736     show : function (on_el)
13737     {
13738         if (!on_el) {
13739             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13740         }
13741         // set content.
13742         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13743         if (this.html !== false) {
13744             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13745         }
13746         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13747         if (!this.title.length) {
13748             this.el.select('.popover-title',true).hide();
13749         }
13750         
13751         var placement = typeof this.placement == 'function' ?
13752             this.placement.call(this, this.el, on_el) :
13753             this.placement;
13754             
13755         var autoToken = /\s?auto?\s?/i;
13756         var autoPlace = autoToken.test(placement);
13757         if (autoPlace) {
13758             placement = placement.replace(autoToken, '') || 'top';
13759         }
13760         
13761         //this.el.detach()
13762         //this.el.setXY([0,0]);
13763         this.el.show();
13764         this.el.dom.style.display='block';
13765         this.el.addClass(placement);
13766         
13767         //this.el.appendTo(on_el);
13768         
13769         var p = this.getPosition();
13770         var box = this.el.getBox();
13771         
13772         if (autoPlace) {
13773             // fixme..
13774         }
13775         var align = Roo.bootstrap.Popover.alignment[placement]
13776         this.el.alignTo(on_el, align[0],align[1]);
13777         //var arrow = this.el.select('.arrow',true).first();
13778         //arrow.set(align[2], 
13779         
13780         this.el.addClass('in');
13781         this.hoverState = null;
13782         
13783         if (this.el.hasClass('fade')) {
13784             // fade it?
13785         }
13786         
13787     },
13788     hide : function()
13789     {
13790         this.el.setXY([0,0]);
13791         this.el.removeClass('in');
13792         this.el.hide();
13793         
13794     }
13795     
13796 });
13797
13798 Roo.bootstrap.Popover.alignment = {
13799     'left' : ['r-l', [-10,0], 'right'],
13800     'right' : ['l-r', [10,0], 'left'],
13801     'bottom' : ['t-b', [0,10], 'top'],
13802     'top' : [ 'b-t', [0,-10], 'bottom']
13803 };
13804
13805  /*
13806  * - LGPL
13807  *
13808  * Progress
13809  * 
13810  */
13811
13812 /**
13813  * @class Roo.bootstrap.Progress
13814  * @extends Roo.bootstrap.Component
13815  * Bootstrap Progress class
13816  * @cfg {Boolean} striped striped of the progress bar
13817  * @cfg {Boolean} active animated of the progress bar
13818  * 
13819  * 
13820  * @constructor
13821  * Create a new Progress
13822  * @param {Object} config The config object
13823  */
13824
13825 Roo.bootstrap.Progress = function(config){
13826     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13827 };
13828
13829 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13830     
13831     striped : false,
13832     active: false,
13833     
13834     getAutoCreate : function(){
13835         var cfg = {
13836             tag: 'div',
13837             cls: 'progress'
13838         };
13839         
13840         
13841         if(this.striped){
13842             cfg.cls += ' progress-striped';
13843         }
13844       
13845         if(this.active){
13846             cfg.cls += ' active';
13847         }
13848         
13849         
13850         return cfg;
13851     }
13852    
13853 });
13854
13855  
13856
13857  /*
13858  * - LGPL
13859  *
13860  * ProgressBar
13861  * 
13862  */
13863
13864 /**
13865  * @class Roo.bootstrap.ProgressBar
13866  * @extends Roo.bootstrap.Component
13867  * Bootstrap ProgressBar class
13868  * @cfg {Number} aria_valuenow aria-value now
13869  * @cfg {Number} aria_valuemin aria-value min
13870  * @cfg {Number} aria_valuemax aria-value max
13871  * @cfg {String} label label for the progress bar
13872  * @cfg {String} panel (success | info | warning | danger )
13873  * @cfg {String} role role of the progress bar
13874  * @cfg {String} sr_only text
13875  * 
13876  * 
13877  * @constructor
13878  * Create a new ProgressBar
13879  * @param {Object} config The config object
13880  */
13881
13882 Roo.bootstrap.ProgressBar = function(config){
13883     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13884 };
13885
13886 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13887     
13888     aria_valuenow : 0,
13889     aria_valuemin : 0,
13890     aria_valuemax : 100,
13891     label : false,
13892     panel : false,
13893     role : false,
13894     sr_only: false,
13895     
13896     getAutoCreate : function()
13897     {
13898         
13899         var cfg = {
13900             tag: 'div',
13901             cls: 'progress-bar',
13902             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13903         };
13904         
13905         if(this.sr_only){
13906             cfg.cn = {
13907                 tag: 'span',
13908                 cls: 'sr-only',
13909                 html: this.sr_only
13910             }
13911         }
13912         
13913         if(this.role){
13914             cfg.role = this.role;
13915         }
13916         
13917         if(this.aria_valuenow){
13918             cfg['aria-valuenow'] = this.aria_valuenow;
13919         }
13920         
13921         if(this.aria_valuemin){
13922             cfg['aria-valuemin'] = this.aria_valuemin;
13923         }
13924         
13925         if(this.aria_valuemax){
13926             cfg['aria-valuemax'] = this.aria_valuemax;
13927         }
13928         
13929         if(this.label && !this.sr_only){
13930             cfg.html = this.label;
13931         }
13932         
13933         if(this.panel){
13934             cfg.cls += ' progress-bar-' + this.panel;
13935         }
13936         
13937         return cfg;
13938     },
13939     
13940     update : function(aria_valuenow)
13941     {
13942         this.aria_valuenow = aria_valuenow;
13943         
13944         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13945     }
13946    
13947 });
13948
13949  
13950
13951  /*
13952  * - LGPL
13953  *
13954  * column
13955  * 
13956  */
13957
13958 /**
13959  * @class Roo.bootstrap.TabGroup
13960  * @extends Roo.bootstrap.Column
13961  * Bootstrap Column class
13962  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13963  * @cfg {Boolean} carousel true to make the group behave like a carousel
13964  * 
13965  * @constructor
13966  * Create a new TabGroup
13967  * @param {Object} config The config object
13968  */
13969
13970 Roo.bootstrap.TabGroup = function(config){
13971     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13972     if (!this.navId) {
13973         this.navId = Roo.id();
13974     }
13975     this.tabs = [];
13976     Roo.bootstrap.TabGroup.register(this);
13977     
13978 };
13979
13980 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13981     
13982     carousel : false,
13983     transition : false,
13984      
13985     getAutoCreate : function()
13986     {
13987         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13988         
13989         cfg.cls += ' tab-content';
13990         
13991         if (this.carousel) {
13992             cfg.cls += ' carousel slide';
13993             cfg.cn = [{
13994                cls : 'carousel-inner'
13995             }]
13996         }
13997         
13998         
13999         return cfg;
14000     },
14001     getChildContainer : function()
14002     {
14003         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14004     },
14005     
14006     /**
14007     * register a Navigation item
14008     * @param {Roo.bootstrap.NavItem} the navitem to add
14009     */
14010     register : function(item)
14011     {
14012         this.tabs.push( item);
14013         item.navId = this.navId; // not really needed..
14014     
14015     },
14016     
14017     getActivePanel : function()
14018     {
14019         var r = false;
14020         Roo.each(this.tabs, function(t) {
14021             if (t.active) {
14022                 r = t;
14023                 return false;
14024             }
14025             return null;
14026         });
14027         return r;
14028         
14029     },
14030     getPanelByName : function(n)
14031     {
14032         var r = false;
14033         Roo.each(this.tabs, function(t) {
14034             if (t.tabId == n) {
14035                 r = t;
14036                 return false;
14037             }
14038             return null;
14039         });
14040         return r;
14041     },
14042     indexOfPanel : function(p)
14043     {
14044         var r = false;
14045         Roo.each(this.tabs, function(t,i) {
14046             if (t.tabId == p.tabId) {
14047                 r = i;
14048                 return false;
14049             }
14050             return null;
14051         });
14052         return r;
14053     },
14054     /**
14055      * show a specific panel
14056      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14057      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14058      */
14059     showPanel : function (pan)
14060     {
14061         
14062         if (typeof(pan) == 'number') {
14063             pan = this.tabs[pan];
14064         }
14065         if (typeof(pan) == 'string') {
14066             pan = this.getPanelByName(pan);
14067         }
14068         if (pan.tabId == this.getActivePanel().tabId) {
14069             return true;
14070         }
14071         var cur = this.getActivePanel();
14072         
14073         if (false === cur.fireEvent('beforedeactivate')) {
14074             return false;
14075         }
14076         
14077         if (this.carousel) {
14078             this.transition = true;
14079             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14080             var lr = dir == 'next' ? 'left' : 'right';
14081             pan.el.addClass(dir); // or prev
14082             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14083             cur.el.addClass(lr); // or right
14084             pan.el.addClass(lr);
14085             
14086             var _this = this;
14087             cur.el.on('transitionend', function() {
14088                 Roo.log("trans end?");
14089                 
14090                 pan.el.removeClass([lr,dir]);
14091                 pan.setActive(true);
14092                 
14093                 cur.el.removeClass([lr]);
14094                 cur.setActive(false);
14095                 
14096                 _this.transition = false;
14097                 
14098             }, this, { single:  true } );
14099             return true;
14100         }
14101         
14102         cur.setActive(false);
14103         pan.setActive(true);
14104         return true;
14105         
14106     },
14107     showPanelNext : function()
14108     {
14109         var i = this.indexOfPanel(this.getActivePanel());
14110         if (i > this.tabs.length) {
14111             return;
14112         }
14113         this.showPanel(this.tabs[i+1]);
14114     },
14115     showPanelPrev : function()
14116     {
14117         var i = this.indexOfPanel(this.getActivePanel());
14118         if (i  < 1) {
14119             return;
14120         }
14121         this.showPanel(this.tabs[i-1]);
14122     }
14123     
14124     
14125   
14126 });
14127
14128  
14129
14130  
14131  
14132 Roo.apply(Roo.bootstrap.TabGroup, {
14133     
14134     groups: {},
14135      /**
14136     * register a Navigation Group
14137     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14138     */
14139     register : function(navgrp)
14140     {
14141         this.groups[navgrp.navId] = navgrp;
14142         
14143     },
14144     /**
14145     * fetch a Navigation Group based on the navigation ID
14146     * if one does not exist , it will get created.
14147     * @param {string} the navgroup to add
14148     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14149     */
14150     get: function(navId) {
14151         if (typeof(this.groups[navId]) == 'undefined') {
14152             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14153         }
14154         return this.groups[navId] ;
14155     }
14156     
14157     
14158     
14159 });
14160
14161  /*
14162  * - LGPL
14163  *
14164  * TabPanel
14165  * 
14166  */
14167
14168 /**
14169  * @class Roo.bootstrap.TabPanel
14170  * @extends Roo.bootstrap.Component
14171  * Bootstrap TabPanel class
14172  * @cfg {Boolean} active panel active
14173  * @cfg {String} html panel content
14174  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14175  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14176  * 
14177  * 
14178  * @constructor
14179  * Create a new TabPanel
14180  * @param {Object} config The config object
14181  */
14182
14183 Roo.bootstrap.TabPanel = function(config){
14184     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14185     this.addEvents({
14186         /**
14187              * @event changed
14188              * Fires when the active status changes
14189              * @param {Roo.bootstrap.TabPanel} this
14190              * @param {Boolean} state the new state
14191             
14192          */
14193         'changed': true,
14194         /**
14195              * @event beforedeactivate
14196              * Fires before a tab is de-activated - can be used to do validation on a form.
14197              * @param {Roo.bootstrap.TabPanel} this
14198              * @return {Boolean} false if there is an error
14199             
14200          */
14201         'beforedeactivate': true
14202      });
14203     
14204     this.tabId = this.tabId || Roo.id();
14205   
14206 };
14207
14208 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14209     
14210     active: false,
14211     html: false,
14212     tabId: false,
14213     navId : false,
14214     
14215     getAutoCreate : function(){
14216         var cfg = {
14217             tag: 'div',
14218             // item is needed for carousel - not sure if it has any effect otherwise
14219             cls: 'tab-pane item',
14220             html: this.html || ''
14221         };
14222         
14223         if(this.active){
14224             cfg.cls += ' active';
14225         }
14226         
14227         if(this.tabId){
14228             cfg.tabId = this.tabId;
14229         }
14230         
14231         
14232         return cfg;
14233     },
14234     
14235     initEvents:  function()
14236     {
14237         Roo.log('-------- init events on tab panel ---------');
14238         
14239         var p = this.parent();
14240         this.navId = this.navId || p.navId;
14241         
14242         if (typeof(this.navId) != 'undefined') {
14243             // not really needed.. but just in case.. parent should be a NavGroup.
14244             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14245             Roo.log(['register', tg, this]);
14246             tg.register(this);
14247         }
14248     },
14249     
14250     
14251     onRender : function(ct, position)
14252     {
14253        // Roo.log("Call onRender: " + this.xtype);
14254         
14255         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14256         
14257         
14258         
14259         
14260         
14261     },
14262     
14263     setActive: function(state)
14264     {
14265         Roo.log("panel - set active " + this.tabId + "=" + state);
14266         
14267         this.active = state;
14268         if (!state) {
14269             this.el.removeClass('active');
14270             
14271         } else  if (!this.el.hasClass('active')) {
14272             this.el.addClass('active');
14273         }
14274         this.fireEvent('changed', this, state);
14275     }
14276     
14277     
14278 });
14279  
14280
14281  
14282
14283  /*
14284  * - LGPL
14285  *
14286  * DateField
14287  * 
14288  */
14289
14290 /**
14291  * @class Roo.bootstrap.DateField
14292  * @extends Roo.bootstrap.Input
14293  * Bootstrap DateField class
14294  * @cfg {Number} weekStart default 0
14295  * @cfg {Number} weekStart default 0
14296  * @cfg {Number} viewMode default empty, (months|years)
14297  * @cfg {Number} minViewMode default empty, (months|years)
14298  * @cfg {Number} startDate default -Infinity
14299  * @cfg {Number} endDate default Infinity
14300  * @cfg {Boolean} todayHighlight default false
14301  * @cfg {Boolean} todayBtn default false
14302  * @cfg {Boolean} calendarWeeks default false
14303  * @cfg {Object} daysOfWeekDisabled default empty
14304  * 
14305  * @cfg {Boolean} keyboardNavigation default true
14306  * @cfg {String} language default en
14307  * 
14308  * @constructor
14309  * Create a new DateField
14310  * @param {Object} config The config object
14311  */
14312
14313 Roo.bootstrap.DateField = function(config){
14314     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14315      this.addEvents({
14316             /**
14317              * @event show
14318              * Fires when this field show.
14319              * @param {Roo.bootstrap.DateField} this
14320              * @param {Mixed} date The date value
14321              */
14322             show : true,
14323             /**
14324              * @event show
14325              * Fires when this field hide.
14326              * @param {Roo.bootstrap.DateField} this
14327              * @param {Mixed} date The date value
14328              */
14329             hide : true,
14330             /**
14331              * @event select
14332              * Fires when select a date.
14333              * @param {Roo.bootstrap.DateField} this
14334              * @param {Mixed} date The date value
14335              */
14336             select : true
14337         });
14338 };
14339
14340 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14341     
14342     /**
14343      * @cfg {String} format
14344      * The default date format string which can be overriden for localization support.  The format must be
14345      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14346      */
14347     format : "m/d/y",
14348     /**
14349      * @cfg {String} altFormats
14350      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14351      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14352      */
14353     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14354     
14355     weekStart : 0,
14356     
14357     viewMode : '',
14358     
14359     minViewMode : '',
14360     
14361     todayHighlight : false,
14362     
14363     todayBtn: false,
14364     
14365     language: 'en',
14366     
14367     keyboardNavigation: true,
14368     
14369     calendarWeeks: false,
14370     
14371     startDate: -Infinity,
14372     
14373     endDate: Infinity,
14374     
14375     daysOfWeekDisabled: [],
14376     
14377     _events: [],
14378     
14379     UTCDate: function()
14380     {
14381         return new Date(Date.UTC.apply(Date, arguments));
14382     },
14383     
14384     UTCToday: function()
14385     {
14386         var today = new Date();
14387         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14388     },
14389     
14390     getDate: function() {
14391             var d = this.getUTCDate();
14392             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14393     },
14394     
14395     getUTCDate: function() {
14396             return this.date;
14397     },
14398     
14399     setDate: function(d) {
14400             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14401     },
14402     
14403     setUTCDate: function(d) {
14404             this.date = d;
14405             this.setValue(this.formatDate(this.date));
14406     },
14407         
14408     onRender: function(ct, position)
14409     {
14410         
14411         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14412         
14413         this.language = this.language || 'en';
14414         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14415         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14416         
14417         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14418         this.format = this.format || 'm/d/y';
14419         this.isInline = false;
14420         this.isInput = true;
14421         this.component = this.el.select('.add-on', true).first() || false;
14422         this.component = (this.component && this.component.length === 0) ? false : this.component;
14423         this.hasInput = this.component && this.inputEL().length;
14424         
14425         if (typeof(this.minViewMode === 'string')) {
14426             switch (this.minViewMode) {
14427                 case 'months':
14428                     this.minViewMode = 1;
14429                     break;
14430                 case 'years':
14431                     this.minViewMode = 2;
14432                     break;
14433                 default:
14434                     this.minViewMode = 0;
14435                     break;
14436             }
14437         }
14438         
14439         if (typeof(this.viewMode === 'string')) {
14440             switch (this.viewMode) {
14441                 case 'months':
14442                     this.viewMode = 1;
14443                     break;
14444                 case 'years':
14445                     this.viewMode = 2;
14446                     break;
14447                 default:
14448                     this.viewMode = 0;
14449                     break;
14450             }
14451         }
14452                 
14453         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14454         
14455 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14456         
14457         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14458         
14459         this.picker().on('mousedown', this.onMousedown, this);
14460         this.picker().on('click', this.onClick, this);
14461         
14462         this.picker().addClass('datepicker-dropdown');
14463         
14464         this.startViewMode = this.viewMode;
14465         
14466         
14467         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14468             if(!this.calendarWeeks){
14469                 v.remove();
14470                 return;
14471             };
14472             
14473             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14474             v.attr('colspan', function(i, val){
14475                 return parseInt(val) + 1;
14476             });
14477         })
14478                         
14479         
14480         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14481         
14482         this.setStartDate(this.startDate);
14483         this.setEndDate(this.endDate);
14484         
14485         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14486         
14487         this.fillDow();
14488         this.fillMonths();
14489         this.update();
14490         this.showMode();
14491         
14492         if(this.isInline) {
14493             this.show();
14494         }
14495     },
14496     
14497     picker : function()
14498     {
14499         return this.pickerEl;
14500 //        return this.el.select('.datepicker', true).first();
14501     },
14502     
14503     fillDow: function()
14504     {
14505         var dowCnt = this.weekStart;
14506         
14507         var dow = {
14508             tag: 'tr',
14509             cn: [
14510                 
14511             ]
14512         };
14513         
14514         if(this.calendarWeeks){
14515             dow.cn.push({
14516                 tag: 'th',
14517                 cls: 'cw',
14518                 html: '&nbsp;'
14519             })
14520         }
14521         
14522         while (dowCnt < this.weekStart + 7) {
14523             dow.cn.push({
14524                 tag: 'th',
14525                 cls: 'dow',
14526                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14527             });
14528         }
14529         
14530         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14531     },
14532     
14533     fillMonths: function()
14534     {    
14535         var i = 0
14536         var months = this.picker().select('>.datepicker-months td', true).first();
14537         
14538         months.dom.innerHTML = '';
14539         
14540         while (i < 12) {
14541             var month = {
14542                 tag: 'span',
14543                 cls: 'month',
14544                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14545             }
14546             
14547             months.createChild(month);
14548         }
14549         
14550     },
14551     
14552     update: function()
14553     {
14554         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;
14555         
14556         if (this.date < this.startDate) {
14557             this.viewDate = new Date(this.startDate);
14558         } else if (this.date > this.endDate) {
14559             this.viewDate = new Date(this.endDate);
14560         } else {
14561             this.viewDate = new Date(this.date);
14562         }
14563         
14564         this.fill();
14565     },
14566     
14567     fill: function() 
14568     {
14569         var d = new Date(this.viewDate),
14570                 year = d.getUTCFullYear(),
14571                 month = d.getUTCMonth(),
14572                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14573                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14574                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14575                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14576                 currentDate = this.date && this.date.valueOf(),
14577                 today = this.UTCToday();
14578         
14579         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14580         
14581 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14582         
14583 //        this.picker.select('>tfoot th.today').
14584 //                                              .text(dates[this.language].today)
14585 //                                              .toggle(this.todayBtn !== false);
14586     
14587         this.updateNavArrows();
14588         this.fillMonths();
14589                                                 
14590         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14591         
14592         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14593          
14594         prevMonth.setUTCDate(day);
14595         
14596         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14597         
14598         var nextMonth = new Date(prevMonth);
14599         
14600         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14601         
14602         nextMonth = nextMonth.valueOf();
14603         
14604         var fillMonths = false;
14605         
14606         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14607         
14608         while(prevMonth.valueOf() < nextMonth) {
14609             var clsName = '';
14610             
14611             if (prevMonth.getUTCDay() === this.weekStart) {
14612                 if(fillMonths){
14613                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14614                 }
14615                     
14616                 fillMonths = {
14617                     tag: 'tr',
14618                     cn: []
14619                 };
14620                 
14621                 if(this.calendarWeeks){
14622                     // ISO 8601: First week contains first thursday.
14623                     // ISO also states week starts on Monday, but we can be more abstract here.
14624                     var
14625                     // Start of current week: based on weekstart/current date
14626                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14627                     // Thursday of this week
14628                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14629                     // First Thursday of year, year from thursday
14630                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14631                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14632                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14633                     
14634                     fillMonths.cn.push({
14635                         tag: 'td',
14636                         cls: 'cw',
14637                         html: calWeek
14638                     });
14639                 }
14640             }
14641             
14642             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14643                 clsName += ' old';
14644             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14645                 clsName += ' new';
14646             }
14647             if (this.todayHighlight &&
14648                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14649                 prevMonth.getUTCMonth() == today.getMonth() &&
14650                 prevMonth.getUTCDate() == today.getDate()) {
14651                 clsName += ' today';
14652             }
14653             
14654             if (currentDate && prevMonth.valueOf() === currentDate) {
14655                 clsName += ' active';
14656             }
14657             
14658             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14659                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14660                     clsName += ' disabled';
14661             }
14662             
14663             fillMonths.cn.push({
14664                 tag: 'td',
14665                 cls: 'day ' + clsName,
14666                 html: prevMonth.getDate()
14667             })
14668             
14669             prevMonth.setDate(prevMonth.getDate()+1);
14670         }
14671           
14672         var currentYear = this.date && this.date.getUTCFullYear();
14673         var currentMonth = this.date && this.date.getUTCMonth();
14674         
14675         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14676         
14677         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14678             v.removeClass('active');
14679             
14680             if(currentYear === year && k === currentMonth){
14681                 v.addClass('active');
14682             }
14683             
14684             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14685                 v.addClass('disabled');
14686             }
14687             
14688         });
14689         
14690         
14691         year = parseInt(year/10, 10) * 10;
14692         
14693         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14694         
14695         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14696         
14697         year -= 1;
14698         for (var i = -1; i < 11; i++) {
14699             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14700                 tag: 'span',
14701                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14702                 html: year
14703             })
14704             
14705             year += 1;
14706         }
14707     },
14708     
14709     showMode: function(dir) 
14710     {
14711         if (dir) {
14712             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14713         }
14714         Roo.each(this.picker().select('>div',true).elements, function(v){
14715             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14716             v.hide();
14717         });
14718         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14719     },
14720     
14721     place: function()
14722     {
14723         if(this.isInline) return;
14724         
14725         this.picker().removeClass(['bottom', 'top']);
14726         
14727         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14728             /*
14729              * place to the top of element!
14730              *
14731              */
14732             
14733             this.picker().addClass('top');
14734             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14735             
14736             return;
14737         }
14738         
14739         this.picker().addClass('bottom');
14740         
14741         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14742     },
14743     
14744     parseDate : function(value)
14745     {
14746         if(!value || value instanceof Date){
14747             return value;
14748         }
14749         var v = Date.parseDate(value, this.format);
14750         if (!v && this.useIso) {
14751             v = Date.parseDate(value, 'Y-m-d');
14752         }
14753         if(!v && this.altFormats){
14754             if(!this.altFormatsArray){
14755                 this.altFormatsArray = this.altFormats.split("|");
14756             }
14757             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14758                 v = Date.parseDate(value, this.altFormatsArray[i]);
14759             }
14760         }
14761         return v;
14762     },
14763     
14764     formatDate : function(date, fmt)
14765     {
14766         return (!date || !(date instanceof Date)) ?
14767         date : date.dateFormat(fmt || this.format);
14768     },
14769     
14770     onFocus : function()
14771     {
14772         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14773         this.show();
14774     },
14775     
14776     onBlur : function()
14777     {
14778         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14779         
14780         var d = this.inputEl().getValue();
14781         
14782         this.setValue(d);
14783                 
14784         this.hide();
14785     },
14786     
14787     show : function()
14788     {
14789         this.picker().show();
14790         this.update();
14791         this.place();
14792         
14793         this.fireEvent('show', this, this.date);
14794     },
14795     
14796     hide : function()
14797     {
14798         if(this.isInline) return;
14799         this.picker().hide();
14800         this.viewMode = this.startViewMode;
14801         this.showMode();
14802         
14803         this.fireEvent('hide', this, this.date);
14804         
14805     },
14806     
14807     onMousedown: function(e)
14808     {
14809         e.stopPropagation();
14810         e.preventDefault();
14811     },
14812     
14813     keyup: function(e)
14814     {
14815         Roo.bootstrap.DateField.superclass.keyup.call(this);
14816         this.update();
14817     },
14818
14819     setValue: function(v)
14820     {
14821         var d = new Date(v).clearTime();
14822         
14823         if(isNaN(d.getTime())){
14824             this.date = this.viewDate = '';
14825             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14826             return;
14827         }
14828         
14829         v = this.formatDate(d);
14830         
14831         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14832         
14833         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14834      
14835         this.update();
14836
14837         this.fireEvent('select', this, this.date);
14838         
14839     },
14840     
14841     getValue: function()
14842     {
14843         return this.formatDate(this.date);
14844     },
14845     
14846     fireKey: function(e)
14847     {
14848         if (!this.picker().isVisible()){
14849             if (e.keyCode == 27) // allow escape to hide and re-show picker
14850                 this.show();
14851             return;
14852         }
14853         
14854         var dateChanged = false,
14855         dir, day, month,
14856         newDate, newViewDate;
14857         
14858         switch(e.keyCode){
14859             case 27: // escape
14860                 this.hide();
14861                 e.preventDefault();
14862                 break;
14863             case 37: // left
14864             case 39: // right
14865                 if (!this.keyboardNavigation) break;
14866                 dir = e.keyCode == 37 ? -1 : 1;
14867                 
14868                 if (e.ctrlKey){
14869                     newDate = this.moveYear(this.date, dir);
14870                     newViewDate = this.moveYear(this.viewDate, dir);
14871                 } else if (e.shiftKey){
14872                     newDate = this.moveMonth(this.date, dir);
14873                     newViewDate = this.moveMonth(this.viewDate, dir);
14874                 } else {
14875                     newDate = new Date(this.date);
14876                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14877                     newViewDate = new Date(this.viewDate);
14878                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14879                 }
14880                 if (this.dateWithinRange(newDate)){
14881                     this.date = newDate;
14882                     this.viewDate = newViewDate;
14883                     this.setValue(this.formatDate(this.date));
14884 //                    this.update();
14885                     e.preventDefault();
14886                     dateChanged = true;
14887                 }
14888                 break;
14889             case 38: // up
14890             case 40: // down
14891                 if (!this.keyboardNavigation) break;
14892                 dir = e.keyCode == 38 ? -1 : 1;
14893                 if (e.ctrlKey){
14894                     newDate = this.moveYear(this.date, dir);
14895                     newViewDate = this.moveYear(this.viewDate, dir);
14896                 } else if (e.shiftKey){
14897                     newDate = this.moveMonth(this.date, dir);
14898                     newViewDate = this.moveMonth(this.viewDate, dir);
14899                 } else {
14900                     newDate = new Date(this.date);
14901                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14902                     newViewDate = new Date(this.viewDate);
14903                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14904                 }
14905                 if (this.dateWithinRange(newDate)){
14906                     this.date = newDate;
14907                     this.viewDate = newViewDate;
14908                     this.setValue(this.formatDate(this.date));
14909 //                    this.update();
14910                     e.preventDefault();
14911                     dateChanged = true;
14912                 }
14913                 break;
14914             case 13: // enter
14915                 this.setValue(this.formatDate(this.date));
14916                 this.hide();
14917                 e.preventDefault();
14918                 break;
14919             case 9: // tab
14920                 this.setValue(this.formatDate(this.date));
14921                 this.hide();
14922                 break;
14923             case 16: // shift
14924             case 17: // ctrl
14925             case 18: // alt
14926                 break;
14927             default :
14928                 this.hide();
14929                 
14930         }
14931     },
14932     
14933     
14934     onClick: function(e) 
14935     {
14936         e.stopPropagation();
14937         e.preventDefault();
14938         
14939         var target = e.getTarget();
14940         
14941         if(target.nodeName.toLowerCase() === 'i'){
14942             target = Roo.get(target).dom.parentNode;
14943         }
14944         
14945         var nodeName = target.nodeName;
14946         var className = target.className;
14947         var html = target.innerHTML;
14948         
14949         switch(nodeName.toLowerCase()) {
14950             case 'th':
14951                 switch(className) {
14952                     case 'switch':
14953                         this.showMode(1);
14954                         break;
14955                     case 'prev':
14956                     case 'next':
14957                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14958                         switch(this.viewMode){
14959                                 case 0:
14960                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14961                                         break;
14962                                 case 1:
14963                                 case 2:
14964                                         this.viewDate = this.moveYear(this.viewDate, dir);
14965                                         break;
14966                         }
14967                         this.fill();
14968                         break;
14969                     case 'today':
14970                         var date = new Date();
14971                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14972 //                        this.fill()
14973                         this.setValue(this.formatDate(this.date));
14974                         
14975                         this.hide();
14976                         break;
14977                 }
14978                 break;
14979             case 'span':
14980                 if (className.indexOf('disabled') === -1) {
14981                     this.viewDate.setUTCDate(1);
14982                     if (className.indexOf('month') !== -1) {
14983                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14984                     } else {
14985                         var year = parseInt(html, 10) || 0;
14986                         this.viewDate.setUTCFullYear(year);
14987                         
14988                     }
14989                     this.showMode(-1);
14990                     this.fill();
14991                 }
14992                 break;
14993                 
14994             case 'td':
14995                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14996                     var day = parseInt(html, 10) || 1;
14997                     var year = this.viewDate.getUTCFullYear(),
14998                         month = this.viewDate.getUTCMonth();
14999
15000                     if (className.indexOf('old') !== -1) {
15001                         if(month === 0 ){
15002                             month = 11;
15003                             year -= 1;
15004                         }else{
15005                             month -= 1;
15006                         }
15007                     } else if (className.indexOf('new') !== -1) {
15008                         if (month == 11) {
15009                             month = 0;
15010                             year += 1;
15011                         } else {
15012                             month += 1;
15013                         }
15014                     }
15015                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15016                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15017 //                    this.fill();
15018                     this.setValue(this.formatDate(this.date));
15019                     this.hide();
15020                 }
15021                 break;
15022         }
15023     },
15024     
15025     setStartDate: function(startDate)
15026     {
15027         this.startDate = startDate || -Infinity;
15028         if (this.startDate !== -Infinity) {
15029             this.startDate = this.parseDate(this.startDate);
15030         }
15031         this.update();
15032         this.updateNavArrows();
15033     },
15034
15035     setEndDate: function(endDate)
15036     {
15037         this.endDate = endDate || Infinity;
15038         if (this.endDate !== Infinity) {
15039             this.endDate = this.parseDate(this.endDate);
15040         }
15041         this.update();
15042         this.updateNavArrows();
15043     },
15044     
15045     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15046     {
15047         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15048         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15049             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15050         }
15051         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15052             return parseInt(d, 10);
15053         });
15054         this.update();
15055         this.updateNavArrows();
15056     },
15057     
15058     updateNavArrows: function() 
15059     {
15060         var d = new Date(this.viewDate),
15061         year = d.getUTCFullYear(),
15062         month = d.getUTCMonth();
15063         
15064         Roo.each(this.picker().select('.prev', true).elements, function(v){
15065             v.show();
15066             switch (this.viewMode) {
15067                 case 0:
15068
15069                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15070                         v.hide();
15071                     }
15072                     break;
15073                 case 1:
15074                 case 2:
15075                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15076                         v.hide();
15077                     }
15078                     break;
15079             }
15080         });
15081         
15082         Roo.each(this.picker().select('.next', true).elements, function(v){
15083             v.show();
15084             switch (this.viewMode) {
15085                 case 0:
15086
15087                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15088                         v.hide();
15089                     }
15090                     break;
15091                 case 1:
15092                 case 2:
15093                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15094                         v.hide();
15095                     }
15096                     break;
15097             }
15098         })
15099     },
15100     
15101     moveMonth: function(date, dir)
15102     {
15103         if (!dir) return date;
15104         var new_date = new Date(date.valueOf()),
15105         day = new_date.getUTCDate(),
15106         month = new_date.getUTCMonth(),
15107         mag = Math.abs(dir),
15108         new_month, test;
15109         dir = dir > 0 ? 1 : -1;
15110         if (mag == 1){
15111             test = dir == -1
15112             // If going back one month, make sure month is not current month
15113             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15114             ? function(){
15115                 return new_date.getUTCMonth() == month;
15116             }
15117             // If going forward one month, make sure month is as expected
15118             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15119             : function(){
15120                 return new_date.getUTCMonth() != new_month;
15121             };
15122             new_month = month + dir;
15123             new_date.setUTCMonth(new_month);
15124             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15125             if (new_month < 0 || new_month > 11)
15126                 new_month = (new_month + 12) % 12;
15127         } else {
15128             // For magnitudes >1, move one month at a time...
15129             for (var i=0; i<mag; i++)
15130                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15131                 new_date = this.moveMonth(new_date, dir);
15132             // ...then reset the day, keeping it in the new month
15133             new_month = new_date.getUTCMonth();
15134             new_date.setUTCDate(day);
15135             test = function(){
15136                 return new_month != new_date.getUTCMonth();
15137             };
15138         }
15139         // Common date-resetting loop -- if date is beyond end of month, make it
15140         // end of month
15141         while (test()){
15142             new_date.setUTCDate(--day);
15143             new_date.setUTCMonth(new_month);
15144         }
15145         return new_date;
15146     },
15147
15148     moveYear: function(date, dir)
15149     {
15150         return this.moveMonth(date, dir*12);
15151     },
15152
15153     dateWithinRange: function(date)
15154     {
15155         return date >= this.startDate && date <= this.endDate;
15156     },
15157
15158     
15159     remove: function() 
15160     {
15161         this.picker().remove();
15162     }
15163    
15164 });
15165
15166 Roo.apply(Roo.bootstrap.DateField,  {
15167     
15168     head : {
15169         tag: 'thead',
15170         cn: [
15171         {
15172             tag: 'tr',
15173             cn: [
15174             {
15175                 tag: 'th',
15176                 cls: 'prev',
15177                 html: '<i class="fa fa-arrow-left"/>'
15178             },
15179             {
15180                 tag: 'th',
15181                 cls: 'switch',
15182                 colspan: '5'
15183             },
15184             {
15185                 tag: 'th',
15186                 cls: 'next',
15187                 html: '<i class="fa fa-arrow-right"/>'
15188             }
15189
15190             ]
15191         }
15192         ]
15193     },
15194     
15195     content : {
15196         tag: 'tbody',
15197         cn: [
15198         {
15199             tag: 'tr',
15200             cn: [
15201             {
15202                 tag: 'td',
15203                 colspan: '7'
15204             }
15205             ]
15206         }
15207         ]
15208     },
15209     
15210     footer : {
15211         tag: 'tfoot',
15212         cn: [
15213         {
15214             tag: 'tr',
15215             cn: [
15216             {
15217                 tag: 'th',
15218                 colspan: '7',
15219                 cls: 'today'
15220             }
15221                     
15222             ]
15223         }
15224         ]
15225     },
15226     
15227     dates:{
15228         en: {
15229             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15230             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15231             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15232             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15233             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15234             today: "Today"
15235         }
15236     },
15237     
15238     modes: [
15239     {
15240         clsName: 'days',
15241         navFnc: 'Month',
15242         navStep: 1
15243     },
15244     {
15245         clsName: 'months',
15246         navFnc: 'FullYear',
15247         navStep: 1
15248     },
15249     {
15250         clsName: 'years',
15251         navFnc: 'FullYear',
15252         navStep: 10
15253     }]
15254 });
15255
15256 Roo.apply(Roo.bootstrap.DateField,  {
15257   
15258     template : {
15259         tag: 'div',
15260         cls: 'datepicker dropdown-menu',
15261         cn: [
15262         {
15263             tag: 'div',
15264             cls: 'datepicker-days',
15265             cn: [
15266             {
15267                 tag: 'table',
15268                 cls: 'table-condensed',
15269                 cn:[
15270                 Roo.bootstrap.DateField.head,
15271                 {
15272                     tag: 'tbody'
15273                 },
15274                 Roo.bootstrap.DateField.footer
15275                 ]
15276             }
15277             ]
15278         },
15279         {
15280             tag: 'div',
15281             cls: 'datepicker-months',
15282             cn: [
15283             {
15284                 tag: 'table',
15285                 cls: 'table-condensed',
15286                 cn:[
15287                 Roo.bootstrap.DateField.head,
15288                 Roo.bootstrap.DateField.content,
15289                 Roo.bootstrap.DateField.footer
15290                 ]
15291             }
15292             ]
15293         },
15294         {
15295             tag: 'div',
15296             cls: 'datepicker-years',
15297             cn: [
15298             {
15299                 tag: 'table',
15300                 cls: 'table-condensed',
15301                 cn:[
15302                 Roo.bootstrap.DateField.head,
15303                 Roo.bootstrap.DateField.content,
15304                 Roo.bootstrap.DateField.footer
15305                 ]
15306             }
15307             ]
15308         }
15309         ]
15310     }
15311 });
15312
15313  
15314
15315  /*
15316  * - LGPL
15317  *
15318  * TimeField
15319  * 
15320  */
15321
15322 /**
15323  * @class Roo.bootstrap.TimeField
15324  * @extends Roo.bootstrap.Input
15325  * Bootstrap DateField class
15326  * 
15327  * 
15328  * @constructor
15329  * Create a new TimeField
15330  * @param {Object} config The config object
15331  */
15332
15333 Roo.bootstrap.TimeField = function(config){
15334     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15335     this.addEvents({
15336             /**
15337              * @event show
15338              * Fires when this field show.
15339              * @param {Roo.bootstrap.DateField} this
15340              * @param {Mixed} date The date value
15341              */
15342             show : true,
15343             /**
15344              * @event show
15345              * Fires when this field hide.
15346              * @param {Roo.bootstrap.DateField} this
15347              * @param {Mixed} date The date value
15348              */
15349             hide : true,
15350             /**
15351              * @event select
15352              * Fires when select a date.
15353              * @param {Roo.bootstrap.DateField} this
15354              * @param {Mixed} date The date value
15355              */
15356             select : true
15357         });
15358 };
15359
15360 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15361     
15362     /**
15363      * @cfg {String} format
15364      * The default time format string which can be overriden for localization support.  The format must be
15365      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15366      */
15367     format : "H:i",
15368        
15369     onRender: function(ct, position)
15370     {
15371         
15372         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15373                 
15374         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15375         
15376         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15377         
15378         this.pop = this.picker().select('>.datepicker-time',true).first();
15379         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15380         
15381         this.picker().on('mousedown', this.onMousedown, this);
15382         this.picker().on('click', this.onClick, this);
15383         
15384         this.picker().addClass('datepicker-dropdown');
15385     
15386         this.fillTime();
15387         this.update();
15388             
15389         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15390         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15391         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15392         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15393         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15394         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15395
15396     },
15397     
15398     fireKey: function(e){
15399         if (!this.picker().isVisible()){
15400             if (e.keyCode == 27) // allow escape to hide and re-show picker
15401                 this.show();
15402             return;
15403         }
15404
15405         e.preventDefault();
15406         
15407         switch(e.keyCode){
15408             case 27: // escape
15409                 this.hide();
15410                 break;
15411             case 37: // left
15412             case 39: // right
15413                 this.onTogglePeriod();
15414                 break;
15415             case 38: // up
15416                 this.onIncrementMinutes();
15417                 break;
15418             case 40: // down
15419                 this.onDecrementMinutes();
15420                 break;
15421             case 13: // enter
15422             case 9: // tab
15423                 this.setTime();
15424                 break;
15425         }
15426     },
15427     
15428     onClick: function(e) {
15429         e.stopPropagation();
15430         e.preventDefault();
15431     },
15432     
15433     picker : function()
15434     {
15435         return this.el.select('.datepicker', true).first();
15436     },
15437     
15438     fillTime: function()
15439     {    
15440         var time = this.pop.select('tbody', true).first();
15441         
15442         time.dom.innerHTML = '';
15443         
15444         time.createChild({
15445             tag: 'tr',
15446             cn: [
15447                 {
15448                     tag: 'td',
15449                     cn: [
15450                         {
15451                             tag: 'a',
15452                             href: '#',
15453                             cls: 'btn',
15454                             cn: [
15455                                 {
15456                                     tag: 'span',
15457                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15458                                 }
15459                             ]
15460                         } 
15461                     ]
15462                 },
15463                 {
15464                     tag: 'td',
15465                     cls: 'separator'
15466                 },
15467                 {
15468                     tag: 'td',
15469                     cn: [
15470                         {
15471                             tag: 'a',
15472                             href: '#',
15473                             cls: 'btn',
15474                             cn: [
15475                                 {
15476                                     tag: 'span',
15477                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15478                                 }
15479                             ]
15480                         }
15481                     ]
15482                 },
15483                 {
15484                     tag: 'td',
15485                     cls: 'separator'
15486                 }
15487             ]
15488         });
15489         
15490         time.createChild({
15491             tag: 'tr',
15492             cn: [
15493                 {
15494                     tag: 'td',
15495                     cn: [
15496                         {
15497                             tag: 'span',
15498                             cls: 'timepicker-hour',
15499                             html: '00'
15500                         }  
15501                     ]
15502                 },
15503                 {
15504                     tag: 'td',
15505                     cls: 'separator',
15506                     html: ':'
15507                 },
15508                 {
15509                     tag: 'td',
15510                     cn: [
15511                         {
15512                             tag: 'span',
15513                             cls: 'timepicker-minute',
15514                             html: '00'
15515                         }  
15516                     ]
15517                 },
15518                 {
15519                     tag: 'td',
15520                     cls: 'separator'
15521                 },
15522                 {
15523                     tag: 'td',
15524                     cn: [
15525                         {
15526                             tag: 'button',
15527                             type: 'button',
15528                             cls: 'btn btn-primary period',
15529                             html: 'AM'
15530                             
15531                         }
15532                     ]
15533                 }
15534             ]
15535         });
15536         
15537         time.createChild({
15538             tag: 'tr',
15539             cn: [
15540                 {
15541                     tag: 'td',
15542                     cn: [
15543                         {
15544                             tag: 'a',
15545                             href: '#',
15546                             cls: 'btn',
15547                             cn: [
15548                                 {
15549                                     tag: 'span',
15550                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15551                                 }
15552                             ]
15553                         }
15554                     ]
15555                 },
15556                 {
15557                     tag: 'td',
15558                     cls: 'separator'
15559                 },
15560                 {
15561                     tag: 'td',
15562                     cn: [
15563                         {
15564                             tag: 'a',
15565                             href: '#',
15566                             cls: 'btn',
15567                             cn: [
15568                                 {
15569                                     tag: 'span',
15570                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15571                                 }
15572                             ]
15573                         }
15574                     ]
15575                 },
15576                 {
15577                     tag: 'td',
15578                     cls: 'separator'
15579                 }
15580             ]
15581         });
15582         
15583     },
15584     
15585     update: function()
15586     {
15587         
15588         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15589         
15590         this.fill();
15591     },
15592     
15593     fill: function() 
15594     {
15595         var hours = this.time.getHours();
15596         var minutes = this.time.getMinutes();
15597         var period = 'AM';
15598         
15599         if(hours > 11){
15600             period = 'PM';
15601         }
15602         
15603         if(hours == 0){
15604             hours = 12;
15605         }
15606         
15607         
15608         if(hours > 12){
15609             hours = hours - 12;
15610         }
15611         
15612         if(hours < 10){
15613             hours = '0' + hours;
15614         }
15615         
15616         if(minutes < 10){
15617             minutes = '0' + minutes;
15618         }
15619         
15620         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15621         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15622         this.pop.select('button', true).first().dom.innerHTML = period;
15623         
15624     },
15625     
15626     place: function()
15627     {   
15628         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15629         
15630         var cls = ['bottom'];
15631         
15632         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15633             cls.pop();
15634             cls.push('top');
15635         }
15636         
15637         cls.push('right');
15638         
15639         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15640             cls.pop();
15641             cls.push('left');
15642         }
15643         
15644         this.picker().addClass(cls.join('-'));
15645         
15646         var _this = this;
15647         
15648         Roo.each(cls, function(c){
15649             if(c == 'bottom'){
15650                 _this.picker().setTop(_this.inputEl().getHeight());
15651                 return;
15652             }
15653             if(c == 'top'){
15654                 _this.picker().setTop(0 - _this.picker().getHeight());
15655                 return;
15656             }
15657             
15658             if(c == 'left'){
15659                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15660                 return;
15661             }
15662             if(c == 'right'){
15663                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15664                 return;
15665             }
15666         });
15667         
15668     },
15669   
15670     onFocus : function()
15671     {
15672         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15673         this.show();
15674     },
15675     
15676     onBlur : function()
15677     {
15678         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15679         this.hide();
15680     },
15681     
15682     show : function()
15683     {
15684         this.picker().show();
15685         this.pop.show();
15686         this.update();
15687         this.place();
15688         
15689         this.fireEvent('show', this, this.date);
15690     },
15691     
15692     hide : function()
15693     {
15694         this.picker().hide();
15695         this.pop.hide();
15696         
15697         this.fireEvent('hide', this, this.date);
15698     },
15699     
15700     setTime : function()
15701     {
15702         this.hide();
15703         this.setValue(this.time.format(this.format));
15704         
15705         this.fireEvent('select', this, this.date);
15706         
15707         
15708     },
15709     
15710     onMousedown: function(e){
15711         e.stopPropagation();
15712         e.preventDefault();
15713     },
15714     
15715     onIncrementHours: function()
15716     {
15717         Roo.log('onIncrementHours');
15718         this.time = this.time.add(Date.HOUR, 1);
15719         this.update();
15720         
15721     },
15722     
15723     onDecrementHours: function()
15724     {
15725         Roo.log('onDecrementHours');
15726         this.time = this.time.add(Date.HOUR, -1);
15727         this.update();
15728     },
15729     
15730     onIncrementMinutes: function()
15731     {
15732         Roo.log('onIncrementMinutes');
15733         this.time = this.time.add(Date.MINUTE, 1);
15734         this.update();
15735     },
15736     
15737     onDecrementMinutes: function()
15738     {
15739         Roo.log('onDecrementMinutes');
15740         this.time = this.time.add(Date.MINUTE, -1);
15741         this.update();
15742     },
15743     
15744     onTogglePeriod: function()
15745     {
15746         Roo.log('onTogglePeriod');
15747         this.time = this.time.add(Date.HOUR, 12);
15748         this.update();
15749     }
15750     
15751    
15752 });
15753
15754 Roo.apply(Roo.bootstrap.TimeField,  {
15755     
15756     content : {
15757         tag: 'tbody',
15758         cn: [
15759             {
15760                 tag: 'tr',
15761                 cn: [
15762                 {
15763                     tag: 'td',
15764                     colspan: '7'
15765                 }
15766                 ]
15767             }
15768         ]
15769     },
15770     
15771     footer : {
15772         tag: 'tfoot',
15773         cn: [
15774             {
15775                 tag: 'tr',
15776                 cn: [
15777                 {
15778                     tag: 'th',
15779                     colspan: '7',
15780                     cls: '',
15781                     cn: [
15782                         {
15783                             tag: 'button',
15784                             cls: 'btn btn-info ok',
15785                             html: 'OK'
15786                         }
15787                     ]
15788                 }
15789
15790                 ]
15791             }
15792         ]
15793     }
15794 });
15795
15796 Roo.apply(Roo.bootstrap.TimeField,  {
15797   
15798     template : {
15799         tag: 'div',
15800         cls: 'datepicker dropdown-menu',
15801         cn: [
15802             {
15803                 tag: 'div',
15804                 cls: 'datepicker-time',
15805                 cn: [
15806                 {
15807                     tag: 'table',
15808                     cls: 'table-condensed',
15809                     cn:[
15810                     Roo.bootstrap.TimeField.content,
15811                     Roo.bootstrap.TimeField.footer
15812                     ]
15813                 }
15814                 ]
15815             }
15816         ]
15817     }
15818 });
15819
15820  
15821
15822  /*
15823  * - LGPL
15824  *
15825  * CheckBox
15826  * 
15827  */
15828
15829 /**
15830  * @class Roo.bootstrap.CheckBox
15831  * @extends Roo.bootstrap.Input
15832  * Bootstrap CheckBox class
15833  * 
15834  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15835  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15836  * @cfg {String} boxLabel The text that appears beside the checkbox
15837  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15838  * @cfg {Boolean} checked initnal the element
15839  * 
15840  * 
15841  * @constructor
15842  * Create a new CheckBox
15843  * @param {Object} config The config object
15844  */
15845
15846 Roo.bootstrap.CheckBox = function(config){
15847     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15848    
15849         this.addEvents({
15850             /**
15851             * @event check
15852             * Fires when the element is checked or unchecked.
15853             * @param {Roo.bootstrap.CheckBox} this This input
15854             * @param {Boolean} checked The new checked value
15855             */
15856            check : true
15857         });
15858 };
15859
15860 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15861     
15862     inputType: 'checkbox',
15863     inputValue: 1,
15864     valueOff: 0,
15865     boxLabel: false,
15866     checked: false,
15867     weight : false,
15868     
15869     getAutoCreate : function()
15870     {
15871         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15872         
15873         var id = Roo.id();
15874         
15875         var cfg = {};
15876         
15877         cfg.cls = 'form-group checkbox' //input-group
15878         
15879         
15880         
15881         
15882         var input =  {
15883             tag: 'input',
15884             id : id,
15885             type : this.inputType,
15886             value : (!this.checked) ? this.valueOff : this.inputValue,
15887             cls : 'roo-checkbox', //'form-box',
15888             placeholder : this.placeholder || ''
15889             
15890         };
15891         
15892         if (this.weight) { // Validity check?
15893             cfg.cls += " checkbox-" + this.weight;
15894         }
15895         
15896         if (this.disabled) {
15897             input.disabled=true;
15898         }
15899         
15900         if(this.checked){
15901             input.checked = this.checked;
15902         }
15903         
15904         if (this.name) {
15905             input.name = this.name;
15906         }
15907         
15908         if (this.size) {
15909             input.cls += ' input-' + this.size;
15910         }
15911         
15912         var settings=this;
15913         ['xs','sm','md','lg'].map(function(size){
15914             if (settings[size]) {
15915                 cfg.cls += ' col-' + size + '-' + settings[size];
15916             }
15917         });
15918         
15919        
15920         
15921         var inputblock = input;
15922         
15923         
15924         
15925         
15926         if (this.before || this.after) {
15927             
15928             inputblock = {
15929                 cls : 'input-group',
15930                 cn :  [] 
15931             };
15932             if (this.before) {
15933                 inputblock.cn.push({
15934                     tag :'span',
15935                     cls : 'input-group-addon',
15936                     html : this.before
15937                 });
15938             }
15939             inputblock.cn.push(input);
15940             if (this.after) {
15941                 inputblock.cn.push({
15942                     tag :'span',
15943                     cls : 'input-group-addon',
15944                     html : this.after
15945                 });
15946             }
15947             
15948         };
15949         
15950         if (align ==='left' && this.fieldLabel.length) {
15951                 Roo.log("left and has label");
15952                 cfg.cn = [
15953                     
15954                     {
15955                         tag: 'label',
15956                         'for' :  id,
15957                         cls : 'control-label col-md-' + this.labelWidth,
15958                         html : this.fieldLabel
15959                         
15960                     },
15961                     {
15962                         cls : "col-md-" + (12 - this.labelWidth), 
15963                         cn: [
15964                             inputblock
15965                         ]
15966                     }
15967                     
15968                 ];
15969         } else if ( this.fieldLabel.length) {
15970                 Roo.log(" label");
15971                 cfg.cn = [
15972                    
15973                     {
15974                         tag: this.boxLabel ? 'span' : 'label',
15975                         'for': id,
15976                         cls: 'control-label box-input-label',
15977                         //cls : 'input-group-addon',
15978                         html : this.fieldLabel
15979                         
15980                     },
15981                     
15982                     inputblock
15983                     
15984                 ];
15985
15986         } else {
15987             
15988                 Roo.log(" no label && no align");
15989                 cfg.cn = [  inputblock ] ;
15990                 
15991                 
15992         };
15993          if(this.boxLabel){
15994             cfg.cn.push( {
15995                 tag: 'label',
15996                 'for': id,
15997                 cls: 'box-label',
15998                 html: this.boxLabel
15999                 
16000             });
16001         }
16002         
16003         
16004        
16005         return cfg;
16006         
16007     },
16008     
16009     /**
16010      * return the real input element.
16011      */
16012     inputEl: function ()
16013     {
16014         return this.el.select('input.roo-checkbox',true).first();
16015     },
16016     
16017     label: function()
16018     {
16019         return this.el.select('label.control-label',true).first();
16020     },
16021     
16022     initEvents : function()
16023     {
16024 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16025         
16026         this.inputEl().on('click', this.onClick,  this);
16027         
16028     },
16029     
16030     onClick : function()
16031     {   
16032         this.setChecked(!this.checked);
16033     },
16034     
16035     setChecked : function(state,suppressEvent)
16036     {
16037         this.checked = state;
16038         
16039         this.inputEl().dom.checked = state;
16040         
16041         if(suppressEvent !== true){
16042             this.fireEvent('check', this, state);
16043         }
16044         
16045         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16046         
16047     },
16048     
16049     setValue : function(v,suppressEvent)
16050     {
16051         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16052     }
16053     
16054 });
16055
16056  
16057 /*
16058  * - LGPL
16059  *
16060  * Radio
16061  * 
16062  */
16063
16064 /**
16065  * @class Roo.bootstrap.Radio
16066  * @extends Roo.bootstrap.CheckBox
16067  * Bootstrap Radio class
16068
16069  * @constructor
16070  * Create a new Radio
16071  * @param {Object} config The config object
16072  */
16073
16074 Roo.bootstrap.Radio = function(config){
16075     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16076    
16077 };
16078
16079 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16080     
16081     inputType: 'radio',
16082     inputValue: '',
16083     valueOff: '',
16084     
16085     getAutoCreate : function()
16086     {
16087         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16088         
16089         var id = Roo.id();
16090         
16091         var cfg = {};
16092         
16093         cfg.cls = 'form-group radio' //input-group
16094         
16095         var input =  {
16096             tag: 'input',
16097             id : id,
16098             type : this.inputType,
16099             value : (!this.checked) ? this.valueOff : this.inputValue,
16100             cls : 'roo-radio',
16101             placeholder : this.placeholder || ''
16102             
16103         };
16104           if (this.weight) { // Validity check?
16105             cfg.cls += " radio-" + this.weight;
16106         }
16107         if (this.disabled) {
16108             input.disabled=true;
16109         }
16110         
16111         if(this.checked){
16112             input.checked = this.checked;
16113         }
16114         
16115         if (this.name) {
16116             input.name = this.name;
16117         }
16118         
16119         if (this.size) {
16120             input.cls += ' input-' + this.size;
16121         }
16122         
16123         var settings=this;
16124         ['xs','sm','md','lg'].map(function(size){
16125             if (settings[size]) {
16126                 cfg.cls += ' col-' + size + '-' + settings[size];
16127             }
16128         });
16129         
16130         var inputblock = input;
16131         
16132         if (this.before || this.after) {
16133             
16134             inputblock = {
16135                 cls : 'input-group',
16136                 cn :  [] 
16137             };
16138             if (this.before) {
16139                 inputblock.cn.push({
16140                     tag :'span',
16141                     cls : 'input-group-addon',
16142                     html : this.before
16143                 });
16144             }
16145             inputblock.cn.push(input);
16146             if (this.after) {
16147                 inputblock.cn.push({
16148                     tag :'span',
16149                     cls : 'input-group-addon',
16150                     html : this.after
16151                 });
16152             }
16153             
16154         };
16155         
16156         if (align ==='left' && this.fieldLabel.length) {
16157                 Roo.log("left and has label");
16158                 cfg.cn = [
16159                     
16160                     {
16161                         tag: 'label',
16162                         'for' :  id,
16163                         cls : 'control-label col-md-' + this.labelWidth,
16164                         html : this.fieldLabel
16165                         
16166                     },
16167                     {
16168                         cls : "col-md-" + (12 - this.labelWidth), 
16169                         cn: [
16170                             inputblock
16171                         ]
16172                     }
16173                     
16174                 ];
16175         } else if ( this.fieldLabel.length) {
16176                 Roo.log(" label");
16177                  cfg.cn = [
16178                    
16179                     {
16180                         tag: 'label',
16181                         'for': id,
16182                         cls: 'control-label box-input-label',
16183                         //cls : 'input-group-addon',
16184                         html : this.fieldLabel
16185                         
16186                     },
16187                     
16188                     inputblock
16189                     
16190                 ];
16191
16192         } else {
16193             
16194                    Roo.log(" no label && no align");
16195                 cfg.cn = [
16196                     
16197                         inputblock
16198                     
16199                 ];
16200                 
16201                 
16202         };
16203         
16204         if(this.boxLabel){
16205             cfg.cn.push({
16206                 tag: 'label',
16207                 'for': id,
16208                 cls: 'box-label',
16209                 html: this.boxLabel
16210             })
16211         }
16212         
16213         return cfg;
16214         
16215     },
16216     inputEl: function ()
16217     {
16218         return this.el.select('input.roo-radio',true).first();
16219     },
16220     onClick : function()
16221     {   
16222         this.setChecked(true);
16223     },
16224     
16225     setChecked : function(state,suppressEvent)
16226     {
16227         if(state){
16228             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16229                 v.dom.checked = false;
16230             });
16231         }
16232         
16233         this.checked = state;
16234         this.inputEl().dom.checked = state;
16235         
16236         if(suppressEvent !== true){
16237             this.fireEvent('check', this, state);
16238         }
16239         
16240         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16241         
16242     },
16243     
16244     getGroupValue : function()
16245     {
16246         var value = ''
16247         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16248             if(v.dom.checked == true){
16249                 value = v.dom.value;
16250             }
16251         });
16252         
16253         return value;
16254     },
16255     
16256     /**
16257      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16258      * @return {Mixed} value The field value
16259      */
16260     getValue : function(){
16261         return this.getGroupValue();
16262     }
16263     
16264 });
16265
16266  
16267 //<script type="text/javascript">
16268
16269 /*
16270  * Based  Ext JS Library 1.1.1
16271  * Copyright(c) 2006-2007, Ext JS, LLC.
16272  * LGPL
16273  *
16274  */
16275  
16276 /**
16277  * @class Roo.HtmlEditorCore
16278  * @extends Roo.Component
16279  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16280  *
16281  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16282  */
16283
16284 Roo.HtmlEditorCore = function(config){
16285     
16286     
16287     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16288     this.addEvents({
16289         /**
16290          * @event initialize
16291          * Fires when the editor is fully initialized (including the iframe)
16292          * @param {Roo.HtmlEditorCore} this
16293          */
16294         initialize: true,
16295         /**
16296          * @event activate
16297          * Fires when the editor is first receives the focus. Any insertion must wait
16298          * until after this event.
16299          * @param {Roo.HtmlEditorCore} this
16300          */
16301         activate: true,
16302          /**
16303          * @event beforesync
16304          * Fires before the textarea is updated with content from the editor iframe. Return false
16305          * to cancel the sync.
16306          * @param {Roo.HtmlEditorCore} this
16307          * @param {String} html
16308          */
16309         beforesync: true,
16310          /**
16311          * @event beforepush
16312          * Fires before the iframe editor is updated with content from the textarea. Return false
16313          * to cancel the push.
16314          * @param {Roo.HtmlEditorCore} this
16315          * @param {String} html
16316          */
16317         beforepush: true,
16318          /**
16319          * @event sync
16320          * Fires when the textarea is updated with content from the editor iframe.
16321          * @param {Roo.HtmlEditorCore} this
16322          * @param {String} html
16323          */
16324         sync: true,
16325          /**
16326          * @event push
16327          * Fires when the iframe editor is updated with content from the textarea.
16328          * @param {Roo.HtmlEditorCore} this
16329          * @param {String} html
16330          */
16331         push: true,
16332         
16333         /**
16334          * @event editorevent
16335          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16336          * @param {Roo.HtmlEditorCore} this
16337          */
16338         editorevent: true
16339     });
16340      
16341 };
16342
16343
16344 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16345
16346
16347      /**
16348      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16349      */
16350     
16351     owner : false,
16352     
16353      /**
16354      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16355      *                        Roo.resizable.
16356      */
16357     resizable : false,
16358      /**
16359      * @cfg {Number} height (in pixels)
16360      */   
16361     height: 300,
16362    /**
16363      * @cfg {Number} width (in pixels)
16364      */   
16365     width: 500,
16366     
16367     /**
16368      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16369      * 
16370      */
16371     stylesheets: false,
16372     
16373     // id of frame..
16374     frameId: false,
16375     
16376     // private properties
16377     validationEvent : false,
16378     deferHeight: true,
16379     initialized : false,
16380     activated : false,
16381     sourceEditMode : false,
16382     onFocus : Roo.emptyFn,
16383     iframePad:3,
16384     hideMode:'offsets',
16385     
16386     clearUp: true,
16387     
16388      
16389     
16390
16391     /**
16392      * Protected method that will not generally be called directly. It
16393      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16394      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16395      */
16396     getDocMarkup : function(){
16397         // body styles..
16398         var st = '';
16399         Roo.log(this.stylesheets);
16400         
16401         // inherit styels from page...?? 
16402         if (this.stylesheets === false) {
16403             
16404             Roo.get(document.head).select('style').each(function(node) {
16405                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16406             });
16407             
16408             Roo.get(document.head).select('link').each(function(node) { 
16409                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16410             });
16411             
16412         } else if (!this.stylesheets.length) {
16413                 // simple..
16414                 st = '<style type="text/css">' +
16415                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16416                    '</style>';
16417         } else {
16418             Roo.each(this.stylesheets, function(s) {
16419                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16420             });
16421             
16422         }
16423         
16424         st +=  '<style type="text/css">' +
16425             'IMG { cursor: pointer } ' +
16426         '</style>';
16427
16428         
16429         return '<html><head>' + st  +
16430             //<style type="text/css">' +
16431             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16432             //'</style>' +
16433             ' </head><body class="roo-htmleditor-body"></body></html>';
16434     },
16435
16436     // private
16437     onRender : function(ct, position)
16438     {
16439         var _t = this;
16440         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16441         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16442         
16443         
16444         this.el.dom.style.border = '0 none';
16445         this.el.dom.setAttribute('tabIndex', -1);
16446         this.el.addClass('x-hidden hide');
16447         
16448         
16449         
16450         if(Roo.isIE){ // fix IE 1px bogus margin
16451             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16452         }
16453        
16454         
16455         this.frameId = Roo.id();
16456         
16457          
16458         
16459         var iframe = this.owner.wrap.createChild({
16460             tag: 'iframe',
16461             cls: 'form-control', // bootstrap..
16462             id: this.frameId,
16463             name: this.frameId,
16464             frameBorder : 'no',
16465             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16466         }, this.el
16467         );
16468         
16469         
16470         this.iframe = iframe.dom;
16471
16472          this.assignDocWin();
16473         
16474         this.doc.designMode = 'on';
16475        
16476         this.doc.open();
16477         this.doc.write(this.getDocMarkup());
16478         this.doc.close();
16479
16480         
16481         var task = { // must defer to wait for browser to be ready
16482             run : function(){
16483                 //console.log("run task?" + this.doc.readyState);
16484                 this.assignDocWin();
16485                 if(this.doc.body || this.doc.readyState == 'complete'){
16486                     try {
16487                         this.doc.designMode="on";
16488                     } catch (e) {
16489                         return;
16490                     }
16491                     Roo.TaskMgr.stop(task);
16492                     this.initEditor.defer(10, this);
16493                 }
16494             },
16495             interval : 10,
16496             duration: 10000,
16497             scope: this
16498         };
16499         Roo.TaskMgr.start(task);
16500
16501         
16502          
16503     },
16504
16505     // private
16506     onResize : function(w, h)
16507     {
16508          Roo.log('resize: ' +w + ',' + h );
16509         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16510         if(!this.iframe){
16511             return;
16512         }
16513         if(typeof w == 'number'){
16514             
16515             this.iframe.style.width = w + 'px';
16516         }
16517         if(typeof h == 'number'){
16518             
16519             this.iframe.style.height = h + 'px';
16520             if(this.doc){
16521                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16522             }
16523         }
16524         
16525     },
16526
16527     /**
16528      * Toggles the editor between standard and source edit mode.
16529      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16530      */
16531     toggleSourceEdit : function(sourceEditMode){
16532         
16533         this.sourceEditMode = sourceEditMode === true;
16534         
16535         if(this.sourceEditMode){
16536  
16537             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16538             
16539         }else{
16540             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16541             //this.iframe.className = '';
16542             this.deferFocus();
16543         }
16544         //this.setSize(this.owner.wrap.getSize());
16545         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16546     },
16547
16548     
16549   
16550
16551     /**
16552      * Protected method that will not generally be called directly. If you need/want
16553      * custom HTML cleanup, this is the method you should override.
16554      * @param {String} html The HTML to be cleaned
16555      * return {String} The cleaned HTML
16556      */
16557     cleanHtml : function(html){
16558         html = String(html);
16559         if(html.length > 5){
16560             if(Roo.isSafari){ // strip safari nonsense
16561                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16562             }
16563         }
16564         if(html == '&nbsp;'){
16565             html = '';
16566         }
16567         return html;
16568     },
16569
16570     /**
16571      * HTML Editor -> Textarea
16572      * Protected method that will not generally be called directly. Syncs the contents
16573      * of the editor iframe with the textarea.
16574      */
16575     syncValue : function(){
16576         if(this.initialized){
16577             var bd = (this.doc.body || this.doc.documentElement);
16578             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16579             var html = bd.innerHTML;
16580             if(Roo.isSafari){
16581                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16582                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16583                 if(m && m[1]){
16584                     html = '<div style="'+m[0]+'">' + html + '</div>';
16585                 }
16586             }
16587             html = this.cleanHtml(html);
16588             // fix up the special chars.. normaly like back quotes in word...
16589             // however we do not want to do this with chinese..
16590             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16591                 var cc = b.charCodeAt();
16592                 if (
16593                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16594                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16595                     (cc >= 0xf900 && cc < 0xfb00 )
16596                 ) {
16597                         return b;
16598                 }
16599                 return "&#"+cc+";" 
16600             });
16601             if(this.owner.fireEvent('beforesync', this, html) !== false){
16602                 this.el.dom.value = html;
16603                 this.owner.fireEvent('sync', this, html);
16604             }
16605         }
16606     },
16607
16608     /**
16609      * Protected method that will not generally be called directly. Pushes the value of the textarea
16610      * into the iframe editor.
16611      */
16612     pushValue : function(){
16613         if(this.initialized){
16614             var v = this.el.dom.value.trim();
16615             
16616 //            if(v.length < 1){
16617 //                v = '&#160;';
16618 //            }
16619             
16620             if(this.owner.fireEvent('beforepush', this, v) !== false){
16621                 var d = (this.doc.body || this.doc.documentElement);
16622                 d.innerHTML = v;
16623                 this.cleanUpPaste();
16624                 this.el.dom.value = d.innerHTML;
16625                 this.owner.fireEvent('push', this, v);
16626             }
16627         }
16628     },
16629
16630     // private
16631     deferFocus : function(){
16632         this.focus.defer(10, this);
16633     },
16634
16635     // doc'ed in Field
16636     focus : function(){
16637         if(this.win && !this.sourceEditMode){
16638             this.win.focus();
16639         }else{
16640             this.el.focus();
16641         }
16642     },
16643     
16644     assignDocWin: function()
16645     {
16646         var iframe = this.iframe;
16647         
16648          if(Roo.isIE){
16649             this.doc = iframe.contentWindow.document;
16650             this.win = iframe.contentWindow;
16651         } else {
16652 //            if (!Roo.get(this.frameId)) {
16653 //                return;
16654 //            }
16655 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16656 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16657             
16658             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16659                 return;
16660             }
16661             
16662             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16663             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16664         }
16665     },
16666     
16667     // private
16668     initEditor : function(){
16669         //console.log("INIT EDITOR");
16670         this.assignDocWin();
16671         
16672         
16673         
16674         this.doc.designMode="on";
16675         this.doc.open();
16676         this.doc.write(this.getDocMarkup());
16677         this.doc.close();
16678         
16679         var dbody = (this.doc.body || this.doc.documentElement);
16680         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16681         // this copies styles from the containing element into thsi one..
16682         // not sure why we need all of this..
16683         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16684         
16685         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16686         //ss['background-attachment'] = 'fixed'; // w3c
16687         dbody.bgProperties = 'fixed'; // ie
16688         //Roo.DomHelper.applyStyles(dbody, ss);
16689         Roo.EventManager.on(this.doc, {
16690             //'mousedown': this.onEditorEvent,
16691             'mouseup': this.onEditorEvent,
16692             'dblclick': this.onEditorEvent,
16693             'click': this.onEditorEvent,
16694             'keyup': this.onEditorEvent,
16695             buffer:100,
16696             scope: this
16697         });
16698         if(Roo.isGecko){
16699             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16700         }
16701         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16702             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16703         }
16704         this.initialized = true;
16705
16706         this.owner.fireEvent('initialize', this);
16707         this.pushValue();
16708     },
16709
16710     // private
16711     onDestroy : function(){
16712         
16713         
16714         
16715         if(this.rendered){
16716             
16717             //for (var i =0; i < this.toolbars.length;i++) {
16718             //    // fixme - ask toolbars for heights?
16719             //    this.toolbars[i].onDestroy();
16720            // }
16721             
16722             //this.wrap.dom.innerHTML = '';
16723             //this.wrap.remove();
16724         }
16725     },
16726
16727     // private
16728     onFirstFocus : function(){
16729         
16730         this.assignDocWin();
16731         
16732         
16733         this.activated = true;
16734          
16735     
16736         if(Roo.isGecko){ // prevent silly gecko errors
16737             this.win.focus();
16738             var s = this.win.getSelection();
16739             if(!s.focusNode || s.focusNode.nodeType != 3){
16740                 var r = s.getRangeAt(0);
16741                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16742                 r.collapse(true);
16743                 this.deferFocus();
16744             }
16745             try{
16746                 this.execCmd('useCSS', true);
16747                 this.execCmd('styleWithCSS', false);
16748             }catch(e){}
16749         }
16750         this.owner.fireEvent('activate', this);
16751     },
16752
16753     // private
16754     adjustFont: function(btn){
16755         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16756         //if(Roo.isSafari){ // safari
16757         //    adjust *= 2;
16758        // }
16759         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16760         if(Roo.isSafari){ // safari
16761             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16762             v =  (v < 10) ? 10 : v;
16763             v =  (v > 48) ? 48 : v;
16764             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16765             
16766         }
16767         
16768         
16769         v = Math.max(1, v+adjust);
16770         
16771         this.execCmd('FontSize', v  );
16772     },
16773
16774     onEditorEvent : function(e){
16775         this.owner.fireEvent('editorevent', this, e);
16776       //  this.updateToolbar();
16777         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16778     },
16779
16780     insertTag : function(tg)
16781     {
16782         // could be a bit smarter... -> wrap the current selected tRoo..
16783         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16784             
16785             range = this.createRange(this.getSelection());
16786             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16787             wrappingNode.appendChild(range.extractContents());
16788             range.insertNode(wrappingNode);
16789
16790             return;
16791             
16792             
16793             
16794         }
16795         this.execCmd("formatblock",   tg);
16796         
16797     },
16798     
16799     insertText : function(txt)
16800     {
16801         
16802         
16803         var range = this.createRange();
16804         range.deleteContents();
16805                //alert(Sender.getAttribute('label'));
16806                
16807         range.insertNode(this.doc.createTextNode(txt));
16808     } ,
16809     
16810      
16811
16812     /**
16813      * Executes a Midas editor command on the editor document and performs necessary focus and
16814      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16815      * @param {String} cmd The Midas command
16816      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16817      */
16818     relayCmd : function(cmd, value){
16819         this.win.focus();
16820         this.execCmd(cmd, value);
16821         this.owner.fireEvent('editorevent', this);
16822         //this.updateToolbar();
16823         this.owner.deferFocus();
16824     },
16825
16826     /**
16827      * Executes a Midas editor command directly on the editor document.
16828      * For visual commands, you should use {@link #relayCmd} instead.
16829      * <b>This should only be called after the editor is initialized.</b>
16830      * @param {String} cmd The Midas command
16831      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16832      */
16833     execCmd : function(cmd, value){
16834         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16835         this.syncValue();
16836     },
16837  
16838  
16839    
16840     /**
16841      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16842      * to insert tRoo.
16843      * @param {String} text | dom node.. 
16844      */
16845     insertAtCursor : function(text)
16846     {
16847         
16848         
16849         
16850         if(!this.activated){
16851             return;
16852         }
16853         /*
16854         if(Roo.isIE){
16855             this.win.focus();
16856             var r = this.doc.selection.createRange();
16857             if(r){
16858                 r.collapse(true);
16859                 r.pasteHTML(text);
16860                 this.syncValue();
16861                 this.deferFocus();
16862             
16863             }
16864             return;
16865         }
16866         */
16867         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16868             this.win.focus();
16869             
16870             
16871             // from jquery ui (MIT licenced)
16872             var range, node;
16873             var win = this.win;
16874             
16875             if (win.getSelection && win.getSelection().getRangeAt) {
16876                 range = win.getSelection().getRangeAt(0);
16877                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16878                 range.insertNode(node);
16879             } else if (win.document.selection && win.document.selection.createRange) {
16880                 // no firefox support
16881                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16882                 win.document.selection.createRange().pasteHTML(txt);
16883             } else {
16884                 // no firefox support
16885                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16886                 this.execCmd('InsertHTML', txt);
16887             } 
16888             
16889             this.syncValue();
16890             
16891             this.deferFocus();
16892         }
16893     },
16894  // private
16895     mozKeyPress : function(e){
16896         if(e.ctrlKey){
16897             var c = e.getCharCode(), cmd;
16898           
16899             if(c > 0){
16900                 c = String.fromCharCode(c).toLowerCase();
16901                 switch(c){
16902                     case 'b':
16903                         cmd = 'bold';
16904                         break;
16905                     case 'i':
16906                         cmd = 'italic';
16907                         break;
16908                     
16909                     case 'u':
16910                         cmd = 'underline';
16911                         break;
16912                     
16913                     case 'v':
16914                         this.cleanUpPaste.defer(100, this);
16915                         return;
16916                         
16917                 }
16918                 if(cmd){
16919                     this.win.focus();
16920                     this.execCmd(cmd);
16921                     this.deferFocus();
16922                     e.preventDefault();
16923                 }
16924                 
16925             }
16926         }
16927     },
16928
16929     // private
16930     fixKeys : function(){ // load time branching for fastest keydown performance
16931         if(Roo.isIE){
16932             return function(e){
16933                 var k = e.getKey(), r;
16934                 if(k == e.TAB){
16935                     e.stopEvent();
16936                     r = this.doc.selection.createRange();
16937                     if(r){
16938                         r.collapse(true);
16939                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16940                         this.deferFocus();
16941                     }
16942                     return;
16943                 }
16944                 
16945                 if(k == e.ENTER){
16946                     r = this.doc.selection.createRange();
16947                     if(r){
16948                         var target = r.parentElement();
16949                         if(!target || target.tagName.toLowerCase() != 'li'){
16950                             e.stopEvent();
16951                             r.pasteHTML('<br />');
16952                             r.collapse(false);
16953                             r.select();
16954                         }
16955                     }
16956                 }
16957                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16958                     this.cleanUpPaste.defer(100, this);
16959                     return;
16960                 }
16961                 
16962                 
16963             };
16964         }else if(Roo.isOpera){
16965             return function(e){
16966                 var k = e.getKey();
16967                 if(k == e.TAB){
16968                     e.stopEvent();
16969                     this.win.focus();
16970                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16971                     this.deferFocus();
16972                 }
16973                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16974                     this.cleanUpPaste.defer(100, this);
16975                     return;
16976                 }
16977                 
16978             };
16979         }else if(Roo.isSafari){
16980             return function(e){
16981                 var k = e.getKey();
16982                 
16983                 if(k == e.TAB){
16984                     e.stopEvent();
16985                     this.execCmd('InsertText','\t');
16986                     this.deferFocus();
16987                     return;
16988                 }
16989                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16990                     this.cleanUpPaste.defer(100, this);
16991                     return;
16992                 }
16993                 
16994              };
16995         }
16996     }(),
16997     
16998     getAllAncestors: function()
16999     {
17000         var p = this.getSelectedNode();
17001         var a = [];
17002         if (!p) {
17003             a.push(p); // push blank onto stack..
17004             p = this.getParentElement();
17005         }
17006         
17007         
17008         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17009             a.push(p);
17010             p = p.parentNode;
17011         }
17012         a.push(this.doc.body);
17013         return a;
17014     },
17015     lastSel : false,
17016     lastSelNode : false,
17017     
17018     
17019     getSelection : function() 
17020     {
17021         this.assignDocWin();
17022         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17023     },
17024     
17025     getSelectedNode: function() 
17026     {
17027         // this may only work on Gecko!!!
17028         
17029         // should we cache this!!!!
17030         
17031         
17032         
17033          
17034         var range = this.createRange(this.getSelection()).cloneRange();
17035         
17036         if (Roo.isIE) {
17037             var parent = range.parentElement();
17038             while (true) {
17039                 var testRange = range.duplicate();
17040                 testRange.moveToElementText(parent);
17041                 if (testRange.inRange(range)) {
17042                     break;
17043                 }
17044                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17045                     break;
17046                 }
17047                 parent = parent.parentElement;
17048             }
17049             return parent;
17050         }
17051         
17052         // is ancestor a text element.
17053         var ac =  range.commonAncestorContainer;
17054         if (ac.nodeType == 3) {
17055             ac = ac.parentNode;
17056         }
17057         
17058         var ar = ac.childNodes;
17059          
17060         var nodes = [];
17061         var other_nodes = [];
17062         var has_other_nodes = false;
17063         for (var i=0;i<ar.length;i++) {
17064             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17065                 continue;
17066             }
17067             // fullly contained node.
17068             
17069             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17070                 nodes.push(ar[i]);
17071                 continue;
17072             }
17073             
17074             // probably selected..
17075             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17076                 other_nodes.push(ar[i]);
17077                 continue;
17078             }
17079             // outer..
17080             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17081                 continue;
17082             }
17083             
17084             
17085             has_other_nodes = true;
17086         }
17087         if (!nodes.length && other_nodes.length) {
17088             nodes= other_nodes;
17089         }
17090         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17091             return false;
17092         }
17093         
17094         return nodes[0];
17095     },
17096     createRange: function(sel)
17097     {
17098         // this has strange effects when using with 
17099         // top toolbar - not sure if it's a great idea.
17100         //this.editor.contentWindow.focus();
17101         if (typeof sel != "undefined") {
17102             try {
17103                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17104             } catch(e) {
17105                 return this.doc.createRange();
17106             }
17107         } else {
17108             return this.doc.createRange();
17109         }
17110     },
17111     getParentElement: function()
17112     {
17113         
17114         this.assignDocWin();
17115         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17116         
17117         var range = this.createRange(sel);
17118          
17119         try {
17120             var p = range.commonAncestorContainer;
17121             while (p.nodeType == 3) { // text node
17122                 p = p.parentNode;
17123             }
17124             return p;
17125         } catch (e) {
17126             return null;
17127         }
17128     
17129     },
17130     /***
17131      *
17132      * Range intersection.. the hard stuff...
17133      *  '-1' = before
17134      *  '0' = hits..
17135      *  '1' = after.
17136      *         [ -- selected range --- ]
17137      *   [fail]                        [fail]
17138      *
17139      *    basically..
17140      *      if end is before start or  hits it. fail.
17141      *      if start is after end or hits it fail.
17142      *
17143      *   if either hits (but other is outside. - then it's not 
17144      *   
17145      *    
17146      **/
17147     
17148     
17149     // @see http://www.thismuchiknow.co.uk/?p=64.
17150     rangeIntersectsNode : function(range, node)
17151     {
17152         var nodeRange = node.ownerDocument.createRange();
17153         try {
17154             nodeRange.selectNode(node);
17155         } catch (e) {
17156             nodeRange.selectNodeContents(node);
17157         }
17158     
17159         var rangeStartRange = range.cloneRange();
17160         rangeStartRange.collapse(true);
17161     
17162         var rangeEndRange = range.cloneRange();
17163         rangeEndRange.collapse(false);
17164     
17165         var nodeStartRange = nodeRange.cloneRange();
17166         nodeStartRange.collapse(true);
17167     
17168         var nodeEndRange = nodeRange.cloneRange();
17169         nodeEndRange.collapse(false);
17170     
17171         return rangeStartRange.compareBoundaryPoints(
17172                  Range.START_TO_START, nodeEndRange) == -1 &&
17173                rangeEndRange.compareBoundaryPoints(
17174                  Range.START_TO_START, nodeStartRange) == 1;
17175         
17176          
17177     },
17178     rangeCompareNode : function(range, node)
17179     {
17180         var nodeRange = node.ownerDocument.createRange();
17181         try {
17182             nodeRange.selectNode(node);
17183         } catch (e) {
17184             nodeRange.selectNodeContents(node);
17185         }
17186         
17187         
17188         range.collapse(true);
17189     
17190         nodeRange.collapse(true);
17191      
17192         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17193         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17194          
17195         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17196         
17197         var nodeIsBefore   =  ss == 1;
17198         var nodeIsAfter    = ee == -1;
17199         
17200         if (nodeIsBefore && nodeIsAfter)
17201             return 0; // outer
17202         if (!nodeIsBefore && nodeIsAfter)
17203             return 1; //right trailed.
17204         
17205         if (nodeIsBefore && !nodeIsAfter)
17206             return 2;  // left trailed.
17207         // fully contined.
17208         return 3;
17209     },
17210
17211     // private? - in a new class?
17212     cleanUpPaste :  function()
17213     {
17214         // cleans up the whole document..
17215         Roo.log('cleanuppaste');
17216         
17217         this.cleanUpChildren(this.doc.body);
17218         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17219         if (clean != this.doc.body.innerHTML) {
17220             this.doc.body.innerHTML = clean;
17221         }
17222         
17223     },
17224     
17225     cleanWordChars : function(input) {// change the chars to hex code
17226         var he = Roo.HtmlEditorCore;
17227         
17228         var output = input;
17229         Roo.each(he.swapCodes, function(sw) { 
17230             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17231             
17232             output = output.replace(swapper, sw[1]);
17233         });
17234         
17235         return output;
17236     },
17237     
17238     
17239     cleanUpChildren : function (n)
17240     {
17241         if (!n.childNodes.length) {
17242             return;
17243         }
17244         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17245            this.cleanUpChild(n.childNodes[i]);
17246         }
17247     },
17248     
17249     
17250         
17251     
17252     cleanUpChild : function (node)
17253     {
17254         var ed = this;
17255         //console.log(node);
17256         if (node.nodeName == "#text") {
17257             // clean up silly Windows -- stuff?
17258             return; 
17259         }
17260         if (node.nodeName == "#comment") {
17261             node.parentNode.removeChild(node);
17262             // clean up silly Windows -- stuff?
17263             return; 
17264         }
17265         
17266         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17267             // remove node.
17268             node.parentNode.removeChild(node);
17269             return;
17270             
17271         }
17272         
17273         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17274         
17275         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17276         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17277         
17278         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17279         //    remove_keep_children = true;
17280         //}
17281         
17282         if (remove_keep_children) {
17283             this.cleanUpChildren(node);
17284             // inserts everything just before this node...
17285             while (node.childNodes.length) {
17286                 var cn = node.childNodes[0];
17287                 node.removeChild(cn);
17288                 node.parentNode.insertBefore(cn, node);
17289             }
17290             node.parentNode.removeChild(node);
17291             return;
17292         }
17293         
17294         if (!node.attributes || !node.attributes.length) {
17295             this.cleanUpChildren(node);
17296             return;
17297         }
17298         
17299         function cleanAttr(n,v)
17300         {
17301             
17302             if (v.match(/^\./) || v.match(/^\//)) {
17303                 return;
17304             }
17305             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17306                 return;
17307             }
17308             if (v.match(/^#/)) {
17309                 return;
17310             }
17311 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17312             node.removeAttribute(n);
17313             
17314         }
17315         
17316         function cleanStyle(n,v)
17317         {
17318             if (v.match(/expression/)) { //XSS?? should we even bother..
17319                 node.removeAttribute(n);
17320                 return;
17321             }
17322             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17323             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17324             
17325             
17326             var parts = v.split(/;/);
17327             var clean = [];
17328             
17329             Roo.each(parts, function(p) {
17330                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17331                 if (!p.length) {
17332                     return true;
17333                 }
17334                 var l = p.split(':').shift().replace(/\s+/g,'');
17335                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17336                 
17337                 if ( cblack.indexOf(l) > -1) {
17338 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17339                     //node.removeAttribute(n);
17340                     return true;
17341                 }
17342                 //Roo.log()
17343                 // only allow 'c whitelisted system attributes'
17344                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17345 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17346                     //node.removeAttribute(n);
17347                     return true;
17348                 }
17349                 
17350                 
17351                  
17352                 
17353                 clean.push(p);
17354                 return true;
17355             });
17356             if (clean.length) { 
17357                 node.setAttribute(n, clean.join(';'));
17358             } else {
17359                 node.removeAttribute(n);
17360             }
17361             
17362         }
17363         
17364         
17365         for (var i = node.attributes.length-1; i > -1 ; i--) {
17366             var a = node.attributes[i];
17367             //console.log(a);
17368             
17369             if (a.name.toLowerCase().substr(0,2)=='on')  {
17370                 node.removeAttribute(a.name);
17371                 continue;
17372             }
17373             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17374                 node.removeAttribute(a.name);
17375                 continue;
17376             }
17377             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17378                 cleanAttr(a.name,a.value); // fixme..
17379                 continue;
17380             }
17381             if (a.name == 'style') {
17382                 cleanStyle(a.name,a.value);
17383                 continue;
17384             }
17385             /// clean up MS crap..
17386             // tecnically this should be a list of valid class'es..
17387             
17388             
17389             if (a.name == 'class') {
17390                 if (a.value.match(/^Mso/)) {
17391                     node.className = '';
17392                 }
17393                 
17394                 if (a.value.match(/body/)) {
17395                     node.className = '';
17396                 }
17397                 continue;
17398             }
17399             
17400             // style cleanup!?
17401             // class cleanup?
17402             
17403         }
17404         
17405         
17406         this.cleanUpChildren(node);
17407         
17408         
17409     },
17410     /**
17411      * Clean up MS wordisms...
17412      */
17413     cleanWord : function(node)
17414     {
17415         var _t = this;
17416         var cleanWordChildren = function()
17417         {
17418             if (!node.childNodes.length) {
17419                 return;
17420             }
17421             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17422                _t.cleanWord(node.childNodes[i]);
17423             }
17424         }
17425         
17426         
17427         if (!node) {
17428             this.cleanWord(this.doc.body);
17429             return;
17430         }
17431         if (node.nodeName == "#text") {
17432             // clean up silly Windows -- stuff?
17433             return; 
17434         }
17435         if (node.nodeName == "#comment") {
17436             node.parentNode.removeChild(node);
17437             // clean up silly Windows -- stuff?
17438             return; 
17439         }
17440         
17441         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17442             node.parentNode.removeChild(node);
17443             return;
17444         }
17445         
17446         // remove - but keep children..
17447         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17448             while (node.childNodes.length) {
17449                 var cn = node.childNodes[0];
17450                 node.removeChild(cn);
17451                 node.parentNode.insertBefore(cn, node);
17452             }
17453             node.parentNode.removeChild(node);
17454             cleanWordChildren();
17455             return;
17456         }
17457         // clean styles
17458         if (node.className.length) {
17459             
17460             var cn = node.className.split(/\W+/);
17461             var cna = [];
17462             Roo.each(cn, function(cls) {
17463                 if (cls.match(/Mso[a-zA-Z]+/)) {
17464                     return;
17465                 }
17466                 cna.push(cls);
17467             });
17468             node.className = cna.length ? cna.join(' ') : '';
17469             if (!cna.length) {
17470                 node.removeAttribute("class");
17471             }
17472         }
17473         
17474         if (node.hasAttribute("lang")) {
17475             node.removeAttribute("lang");
17476         }
17477         
17478         if (node.hasAttribute("style")) {
17479             
17480             var styles = node.getAttribute("style").split(";");
17481             var nstyle = [];
17482             Roo.each(styles, function(s) {
17483                 if (!s.match(/:/)) {
17484                     return;
17485                 }
17486                 var kv = s.split(":");
17487                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17488                     return;
17489                 }
17490                 // what ever is left... we allow.
17491                 nstyle.push(s);
17492             });
17493             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17494             if (!nstyle.length) {
17495                 node.removeAttribute('style');
17496             }
17497         }
17498         
17499         cleanWordChildren();
17500         
17501         
17502     },
17503     domToHTML : function(currentElement, depth, nopadtext) {
17504         
17505             depth = depth || 0;
17506             nopadtext = nopadtext || false;
17507         
17508             if (!currentElement) {
17509                 return this.domToHTML(this.doc.body);
17510             }
17511             
17512             //Roo.log(currentElement);
17513             var j;
17514             var allText = false;
17515             var nodeName = currentElement.nodeName;
17516             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17517             
17518             if  (nodeName == '#text') {
17519                 return currentElement.nodeValue;
17520             }
17521             
17522             
17523             var ret = '';
17524             if (nodeName != 'BODY') {
17525                  
17526                 var i = 0;
17527                 // Prints the node tagName, such as <A>, <IMG>, etc
17528                 if (tagName) {
17529                     var attr = [];
17530                     for(i = 0; i < currentElement.attributes.length;i++) {
17531                         // quoting?
17532                         var aname = currentElement.attributes.item(i).name;
17533                         if (!currentElement.attributes.item(i).value.length) {
17534                             continue;
17535                         }
17536                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17537                     }
17538                     
17539                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17540                 } 
17541                 else {
17542                     
17543                     // eack
17544                 }
17545             } else {
17546                 tagName = false;
17547             }
17548             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17549                 return ret;
17550             }
17551             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17552                 nopadtext = true;
17553             }
17554             
17555             
17556             // Traverse the tree
17557             i = 0;
17558             var currentElementChild = currentElement.childNodes.item(i);
17559             var allText = true;
17560             var innerHTML  = '';
17561             lastnode = '';
17562             while (currentElementChild) {
17563                 // Formatting code (indent the tree so it looks nice on the screen)
17564                 var nopad = nopadtext;
17565                 if (lastnode == 'SPAN') {
17566                     nopad  = true;
17567                 }
17568                 // text
17569                 if  (currentElementChild.nodeName == '#text') {
17570                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17571                     if (!nopad && toadd.length > 80) {
17572                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17573                     }
17574                     innerHTML  += toadd;
17575                     
17576                     i++;
17577                     currentElementChild = currentElement.childNodes.item(i);
17578                     lastNode = '';
17579                     continue;
17580                 }
17581                 allText = false;
17582                 
17583                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17584                     
17585                 // Recursively traverse the tree structure of the child node
17586                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17587                 lastnode = currentElementChild.nodeName;
17588                 i++;
17589                 currentElementChild=currentElement.childNodes.item(i);
17590             }
17591             
17592             ret += innerHTML;
17593             
17594             if (!allText) {
17595                     // The remaining code is mostly for formatting the tree
17596                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17597             }
17598             
17599             
17600             if (tagName) {
17601                 ret+= "</"+tagName+">";
17602             }
17603             return ret;
17604             
17605         }
17606     
17607     // hide stuff that is not compatible
17608     /**
17609      * @event blur
17610      * @hide
17611      */
17612     /**
17613      * @event change
17614      * @hide
17615      */
17616     /**
17617      * @event focus
17618      * @hide
17619      */
17620     /**
17621      * @event specialkey
17622      * @hide
17623      */
17624     /**
17625      * @cfg {String} fieldClass @hide
17626      */
17627     /**
17628      * @cfg {String} focusClass @hide
17629      */
17630     /**
17631      * @cfg {String} autoCreate @hide
17632      */
17633     /**
17634      * @cfg {String} inputType @hide
17635      */
17636     /**
17637      * @cfg {String} invalidClass @hide
17638      */
17639     /**
17640      * @cfg {String} invalidText @hide
17641      */
17642     /**
17643      * @cfg {String} msgFx @hide
17644      */
17645     /**
17646      * @cfg {String} validateOnBlur @hide
17647      */
17648 });
17649
17650 Roo.HtmlEditorCore.white = [
17651         'area', 'br', 'img', 'input', 'hr', 'wbr',
17652         
17653        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17654        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17655        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17656        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17657        'table',   'ul',         'xmp', 
17658        
17659        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17660       'thead',   'tr', 
17661      
17662       'dir', 'menu', 'ol', 'ul', 'dl',
17663        
17664       'embed',  'object'
17665 ];
17666
17667
17668 Roo.HtmlEditorCore.black = [
17669     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17670         'applet', // 
17671         'base',   'basefont', 'bgsound', 'blink',  'body', 
17672         'frame',  'frameset', 'head',    'html',   'ilayer', 
17673         'iframe', 'layer',  'link',     'meta',    'object',   
17674         'script', 'style' ,'title',  'xml' // clean later..
17675 ];
17676 Roo.HtmlEditorCore.clean = [
17677     'script', 'style', 'title', 'xml'
17678 ];
17679 Roo.HtmlEditorCore.remove = [
17680     'font'
17681 ];
17682 // attributes..
17683
17684 Roo.HtmlEditorCore.ablack = [
17685     'on'
17686 ];
17687     
17688 Roo.HtmlEditorCore.aclean = [ 
17689     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17690 ];
17691
17692 // protocols..
17693 Roo.HtmlEditorCore.pwhite= [
17694         'http',  'https',  'mailto'
17695 ];
17696
17697 // white listed style attributes.
17698 Roo.HtmlEditorCore.cwhite= [
17699       //  'text-align', /// default is to allow most things..
17700       
17701          
17702 //        'font-size'//??
17703 ];
17704
17705 // black listed style attributes.
17706 Roo.HtmlEditorCore.cblack= [
17707       //  'font-size' -- this can be set by the project 
17708 ];
17709
17710
17711 Roo.HtmlEditorCore.swapCodes   =[ 
17712     [    8211, "--" ], 
17713     [    8212, "--" ], 
17714     [    8216,  "'" ],  
17715     [    8217, "'" ],  
17716     [    8220, '"' ],  
17717     [    8221, '"' ],  
17718     [    8226, "*" ],  
17719     [    8230, "..." ]
17720 ]; 
17721
17722     /*
17723  * - LGPL
17724  *
17725  * HtmlEditor
17726  * 
17727  */
17728
17729 /**
17730  * @class Roo.bootstrap.HtmlEditor
17731  * @extends Roo.bootstrap.TextArea
17732  * Bootstrap HtmlEditor class
17733
17734  * @constructor
17735  * Create a new HtmlEditor
17736  * @param {Object} config The config object
17737  */
17738
17739 Roo.bootstrap.HtmlEditor = function(config){
17740     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17741     if (!this.toolbars) {
17742         this.toolbars = [];
17743     }
17744     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17745     this.addEvents({
17746             /**
17747              * @event initialize
17748              * Fires when the editor is fully initialized (including the iframe)
17749              * @param {HtmlEditor} this
17750              */
17751             initialize: true,
17752             /**
17753              * @event activate
17754              * Fires when the editor is first receives the focus. Any insertion must wait
17755              * until after this event.
17756              * @param {HtmlEditor} this
17757              */
17758             activate: true,
17759              /**
17760              * @event beforesync
17761              * Fires before the textarea is updated with content from the editor iframe. Return false
17762              * to cancel the sync.
17763              * @param {HtmlEditor} this
17764              * @param {String} html
17765              */
17766             beforesync: true,
17767              /**
17768              * @event beforepush
17769              * Fires before the iframe editor is updated with content from the textarea. Return false
17770              * to cancel the push.
17771              * @param {HtmlEditor} this
17772              * @param {String} html
17773              */
17774             beforepush: true,
17775              /**
17776              * @event sync
17777              * Fires when the textarea is updated with content from the editor iframe.
17778              * @param {HtmlEditor} this
17779              * @param {String} html
17780              */
17781             sync: true,
17782              /**
17783              * @event push
17784              * Fires when the iframe editor is updated with content from the textarea.
17785              * @param {HtmlEditor} this
17786              * @param {String} html
17787              */
17788             push: true,
17789              /**
17790              * @event editmodechange
17791              * Fires when the editor switches edit modes
17792              * @param {HtmlEditor} this
17793              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17794              */
17795             editmodechange: true,
17796             /**
17797              * @event editorevent
17798              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17799              * @param {HtmlEditor} this
17800              */
17801             editorevent: true,
17802             /**
17803              * @event firstfocus
17804              * Fires when on first focus - needed by toolbars..
17805              * @param {HtmlEditor} this
17806              */
17807             firstfocus: true,
17808             /**
17809              * @event autosave
17810              * Auto save the htmlEditor value as a file into Events
17811              * @param {HtmlEditor} this
17812              */
17813             autosave: true,
17814             /**
17815              * @event savedpreview
17816              * preview the saved version of htmlEditor
17817              * @param {HtmlEditor} this
17818              */
17819             savedpreview: true
17820         });
17821 };
17822
17823
17824 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17825     
17826     
17827       /**
17828      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17829      */
17830     toolbars : false,
17831    
17832      /**
17833      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17834      *                        Roo.resizable.
17835      */
17836     resizable : false,
17837      /**
17838      * @cfg {Number} height (in pixels)
17839      */   
17840     height: 300,
17841    /**
17842      * @cfg {Number} width (in pixels)
17843      */   
17844     width: false,
17845     
17846     /**
17847      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17848      * 
17849      */
17850     stylesheets: false,
17851     
17852     // id of frame..
17853     frameId: false,
17854     
17855     // private properties
17856     validationEvent : false,
17857     deferHeight: true,
17858     initialized : false,
17859     activated : false,
17860     
17861     onFocus : Roo.emptyFn,
17862     iframePad:3,
17863     hideMode:'offsets',
17864     
17865     
17866     tbContainer : false,
17867     
17868     toolbarContainer :function() {
17869         return this.wrap.select('.x-html-editor-tb',true).first();
17870     },
17871
17872     /**
17873      * Protected method that will not generally be called directly. It
17874      * is called when the editor creates its toolbar. Override this method if you need to
17875      * add custom toolbar buttons.
17876      * @param {HtmlEditor} editor
17877      */
17878     createToolbar : function(){
17879         
17880         Roo.log("create toolbars");
17881         
17882         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17883         this.toolbars[0].render(this.toolbarContainer());
17884         
17885         return;
17886         
17887 //        if (!editor.toolbars || !editor.toolbars.length) {
17888 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17889 //        }
17890 //        
17891 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17892 //            editor.toolbars[i] = Roo.factory(
17893 //                    typeof(editor.toolbars[i]) == 'string' ?
17894 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17895 //                Roo.bootstrap.HtmlEditor);
17896 //            editor.toolbars[i].init(editor);
17897 //        }
17898     },
17899
17900      
17901     // private
17902     onRender : function(ct, position)
17903     {
17904        // Roo.log("Call onRender: " + this.xtype);
17905         var _t = this;
17906         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17907       
17908         this.wrap = this.inputEl().wrap({
17909             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17910         });
17911         
17912         this.editorcore.onRender(ct, position);
17913          
17914         if (this.resizable) {
17915             this.resizeEl = new Roo.Resizable(this.wrap, {
17916                 pinned : true,
17917                 wrap: true,
17918                 dynamic : true,
17919                 minHeight : this.height,
17920                 height: this.height,
17921                 handles : this.resizable,
17922                 width: this.width,
17923                 listeners : {
17924                     resize : function(r, w, h) {
17925                         _t.onResize(w,h); // -something
17926                     }
17927                 }
17928             });
17929             
17930         }
17931         this.createToolbar(this);
17932        
17933         
17934         if(!this.width && this.resizable){
17935             this.setSize(this.wrap.getSize());
17936         }
17937         if (this.resizeEl) {
17938             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17939             // should trigger onReize..
17940         }
17941         
17942     },
17943
17944     // private
17945     onResize : function(w, h)
17946     {
17947         Roo.log('resize: ' +w + ',' + h );
17948         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17949         var ew = false;
17950         var eh = false;
17951         
17952         if(this.inputEl() ){
17953             if(typeof w == 'number'){
17954                 var aw = w - this.wrap.getFrameWidth('lr');
17955                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17956                 ew = aw;
17957             }
17958             if(typeof h == 'number'){
17959                  var tbh = -11;  // fixme it needs to tool bar size!
17960                 for (var i =0; i < this.toolbars.length;i++) {
17961                     // fixme - ask toolbars for heights?
17962                     tbh += this.toolbars[i].el.getHeight();
17963                     //if (this.toolbars[i].footer) {
17964                     //    tbh += this.toolbars[i].footer.el.getHeight();
17965                     //}
17966                 }
17967               
17968                 
17969                 
17970                 
17971                 
17972                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17973                 ah -= 5; // knock a few pixes off for look..
17974                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17975                 var eh = ah;
17976             }
17977         }
17978         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17979         this.editorcore.onResize(ew,eh);
17980         
17981     },
17982
17983     /**
17984      * Toggles the editor between standard and source edit mode.
17985      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17986      */
17987     toggleSourceEdit : function(sourceEditMode)
17988     {
17989         this.editorcore.toggleSourceEdit(sourceEditMode);
17990         
17991         if(this.editorcore.sourceEditMode){
17992             Roo.log('editor - showing textarea');
17993             
17994 //            Roo.log('in');
17995 //            Roo.log(this.syncValue());
17996             this.syncValue();
17997             this.inputEl().removeClass(['hide', 'x-hidden']);
17998             this.inputEl().dom.removeAttribute('tabIndex');
17999             this.inputEl().focus();
18000         }else{
18001             Roo.log('editor - hiding textarea');
18002 //            Roo.log('out')
18003 //            Roo.log(this.pushValue()); 
18004             this.pushValue();
18005             
18006             this.inputEl().addClass(['hide', 'x-hidden']);
18007             this.inputEl().dom.setAttribute('tabIndex', -1);
18008             //this.deferFocus();
18009         }
18010          
18011         if(this.resizable){
18012             this.setSize(this.wrap.getSize());
18013         }
18014         
18015         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18016     },
18017  
18018     // private (for BoxComponent)
18019     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18020
18021     // private (for BoxComponent)
18022     getResizeEl : function(){
18023         return this.wrap;
18024     },
18025
18026     // private (for BoxComponent)
18027     getPositionEl : function(){
18028         return this.wrap;
18029     },
18030
18031     // private
18032     initEvents : function(){
18033         this.originalValue = this.getValue();
18034     },
18035
18036 //    /**
18037 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18038 //     * @method
18039 //     */
18040 //    markInvalid : Roo.emptyFn,
18041 //    /**
18042 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18043 //     * @method
18044 //     */
18045 //    clearInvalid : Roo.emptyFn,
18046
18047     setValue : function(v){
18048         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18049         this.editorcore.pushValue();
18050     },
18051
18052      
18053     // private
18054     deferFocus : function(){
18055         this.focus.defer(10, this);
18056     },
18057
18058     // doc'ed in Field
18059     focus : function(){
18060         this.editorcore.focus();
18061         
18062     },
18063       
18064
18065     // private
18066     onDestroy : function(){
18067         
18068         
18069         
18070         if(this.rendered){
18071             
18072             for (var i =0; i < this.toolbars.length;i++) {
18073                 // fixme - ask toolbars for heights?
18074                 this.toolbars[i].onDestroy();
18075             }
18076             
18077             this.wrap.dom.innerHTML = '';
18078             this.wrap.remove();
18079         }
18080     },
18081
18082     // private
18083     onFirstFocus : function(){
18084         //Roo.log("onFirstFocus");
18085         this.editorcore.onFirstFocus();
18086          for (var i =0; i < this.toolbars.length;i++) {
18087             this.toolbars[i].onFirstFocus();
18088         }
18089         
18090     },
18091     
18092     // private
18093     syncValue : function()
18094     {   
18095         this.editorcore.syncValue();
18096     },
18097     
18098     pushValue : function()
18099     {   
18100         this.editorcore.pushValue();
18101     }
18102      
18103     
18104     // hide stuff that is not compatible
18105     /**
18106      * @event blur
18107      * @hide
18108      */
18109     /**
18110      * @event change
18111      * @hide
18112      */
18113     /**
18114      * @event focus
18115      * @hide
18116      */
18117     /**
18118      * @event specialkey
18119      * @hide
18120      */
18121     /**
18122      * @cfg {String} fieldClass @hide
18123      */
18124     /**
18125      * @cfg {String} focusClass @hide
18126      */
18127     /**
18128      * @cfg {String} autoCreate @hide
18129      */
18130     /**
18131      * @cfg {String} inputType @hide
18132      */
18133     /**
18134      * @cfg {String} invalidClass @hide
18135      */
18136     /**
18137      * @cfg {String} invalidText @hide
18138      */
18139     /**
18140      * @cfg {String} msgFx @hide
18141      */
18142     /**
18143      * @cfg {String} validateOnBlur @hide
18144      */
18145 });
18146  
18147     
18148    
18149    
18150    
18151       
18152 Roo.namespace('Roo.bootstrap.htmleditor');
18153 /**
18154  * @class Roo.bootstrap.HtmlEditorToolbar1
18155  * Basic Toolbar
18156  * 
18157  * Usage:
18158  *
18159  new Roo.bootstrap.HtmlEditor({
18160     ....
18161     toolbars : [
18162         new Roo.bootstrap.HtmlEditorToolbar1({
18163             disable : { fonts: 1 , format: 1, ..., ... , ...],
18164             btns : [ .... ]
18165         })
18166     }
18167      
18168  * 
18169  * @cfg {Object} disable List of elements to disable..
18170  * @cfg {Array} btns List of additional buttons.
18171  * 
18172  * 
18173  * NEEDS Extra CSS? 
18174  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18175  */
18176  
18177 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18178 {
18179     
18180     Roo.apply(this, config);
18181     
18182     // default disabled, based on 'good practice'..
18183     this.disable = this.disable || {};
18184     Roo.applyIf(this.disable, {
18185         fontSize : true,
18186         colors : true,
18187         specialElements : true
18188     });
18189     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18190     
18191     this.editor = config.editor;
18192     this.editorcore = config.editor.editorcore;
18193     
18194     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18195     
18196     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18197     // dont call parent... till later.
18198 }
18199 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18200      
18201     bar : true,
18202     
18203     editor : false,
18204     editorcore : false,
18205     
18206     
18207     formats : [
18208         "p" ,  
18209         "h1","h2","h3","h4","h5","h6", 
18210         "pre", "code", 
18211         "abbr", "acronym", "address", "cite", "samp", "var",
18212         'div','span'
18213     ],
18214     
18215     onRender : function(ct, position)
18216     {
18217        // Roo.log("Call onRender: " + this.xtype);
18218         
18219        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18220        Roo.log(this.el);
18221        this.el.dom.style.marginBottom = '0';
18222        var _this = this;
18223        var editorcore = this.editorcore;
18224        var editor= this.editor;
18225        
18226        var children = [];
18227        var btn = function(id,cmd , toggle, handler){
18228        
18229             var  event = toggle ? 'toggle' : 'click';
18230        
18231             var a = {
18232                 size : 'sm',
18233                 xtype: 'Button',
18234                 xns: Roo.bootstrap,
18235                 glyphicon : id,
18236                 cmd : id || cmd,
18237                 enableToggle:toggle !== false,
18238                 //html : 'submit'
18239                 pressed : toggle ? false : null,
18240                 listeners : {}
18241             }
18242             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18243                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18244             }
18245             children.push(a);
18246             return a;
18247        }
18248         
18249         var style = {
18250                 xtype: 'Button',
18251                 size : 'sm',
18252                 xns: Roo.bootstrap,
18253                 glyphicon : 'font',
18254                 //html : 'submit'
18255                 menu : {
18256                     xtype: 'Menu',
18257                     xns: Roo.bootstrap,
18258                     items:  []
18259                 }
18260         };
18261         Roo.each(this.formats, function(f) {
18262             style.menu.items.push({
18263                 xtype :'MenuItem',
18264                 xns: Roo.bootstrap,
18265                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18266                 tagname : f,
18267                 listeners : {
18268                     click : function()
18269                     {
18270                         editorcore.insertTag(this.tagname);
18271                         editor.focus();
18272                     }
18273                 }
18274                 
18275             });
18276         });
18277          children.push(style);   
18278             
18279             
18280         btn('bold',false,true);
18281         btn('italic',false,true);
18282         btn('align-left', 'justifyleft',true);
18283         btn('align-center', 'justifycenter',true);
18284         btn('align-right' , 'justifyright',true);
18285         btn('link', false, false, function(btn) {
18286             //Roo.log("create link?");
18287             var url = prompt(this.createLinkText, this.defaultLinkValue);
18288             if(url && url != 'http:/'+'/'){
18289                 this.editorcore.relayCmd('createlink', url);
18290             }
18291         }),
18292         btn('list','insertunorderedlist',true);
18293         btn('pencil', false,true, function(btn){
18294                 Roo.log(this);
18295                 
18296                 this.toggleSourceEdit(btn.pressed);
18297         });
18298         /*
18299         var cog = {
18300                 xtype: 'Button',
18301                 size : 'sm',
18302                 xns: Roo.bootstrap,
18303                 glyphicon : 'cog',
18304                 //html : 'submit'
18305                 menu : {
18306                     xtype: 'Menu',
18307                     xns: Roo.bootstrap,
18308                     items:  []
18309                 }
18310         };
18311         
18312         cog.menu.items.push({
18313             xtype :'MenuItem',
18314             xns: Roo.bootstrap,
18315             html : Clean styles,
18316             tagname : f,
18317             listeners : {
18318                 click : function()
18319                 {
18320                     editorcore.insertTag(this.tagname);
18321                     editor.focus();
18322                 }
18323             }
18324             
18325         });
18326        */
18327         
18328          
18329        this.xtype = 'NavSimplebar';
18330         
18331         for(var i=0;i< children.length;i++) {
18332             
18333             this.buttons.add(this.addxtypeChild(children[i]));
18334             
18335         }
18336         
18337         editor.on('editorevent', this.updateToolbar, this);
18338     },
18339     onBtnClick : function(id)
18340     {
18341        this.editorcore.relayCmd(id);
18342        this.editorcore.focus();
18343     },
18344     
18345     /**
18346      * Protected method that will not generally be called directly. It triggers
18347      * a toolbar update by reading the markup state of the current selection in the editor.
18348      */
18349     updateToolbar: function(){
18350
18351         if(!this.editorcore.activated){
18352             this.editor.onFirstFocus(); // is this neeed?
18353             return;
18354         }
18355
18356         var btns = this.buttons; 
18357         var doc = this.editorcore.doc;
18358         btns.get('bold').setActive(doc.queryCommandState('bold'));
18359         btns.get('italic').setActive(doc.queryCommandState('italic'));
18360         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18361         
18362         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18363         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18364         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18365         
18366         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18367         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18368          /*
18369         
18370         var ans = this.editorcore.getAllAncestors();
18371         if (this.formatCombo) {
18372             
18373             
18374             var store = this.formatCombo.store;
18375             this.formatCombo.setValue("");
18376             for (var i =0; i < ans.length;i++) {
18377                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18378                     // select it..
18379                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18380                     break;
18381                 }
18382             }
18383         }
18384         
18385         
18386         
18387         // hides menus... - so this cant be on a menu...
18388         Roo.bootstrap.MenuMgr.hideAll();
18389         */
18390         Roo.bootstrap.MenuMgr.hideAll();
18391         //this.editorsyncValue();
18392     },
18393     onFirstFocus: function() {
18394         this.buttons.each(function(item){
18395            item.enable();
18396         });
18397     },
18398     toggleSourceEdit : function(sourceEditMode){
18399         
18400           
18401         if(sourceEditMode){
18402             Roo.log("disabling buttons");
18403            this.buttons.each( function(item){
18404                 if(item.cmd != 'pencil'){
18405                     item.disable();
18406                 }
18407             });
18408           
18409         }else{
18410             Roo.log("enabling buttons");
18411             if(this.editorcore.initialized){
18412                 this.buttons.each( function(item){
18413                     item.enable();
18414                 });
18415             }
18416             
18417         }
18418         Roo.log("calling toggole on editor");
18419         // tell the editor that it's been pressed..
18420         this.editor.toggleSourceEdit(sourceEditMode);
18421        
18422     }
18423 });
18424
18425
18426
18427
18428
18429 /**
18430  * @class Roo.bootstrap.Table.AbstractSelectionModel
18431  * @extends Roo.util.Observable
18432  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18433  * implemented by descendant classes.  This class should not be directly instantiated.
18434  * @constructor
18435  */
18436 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18437     this.locked = false;
18438     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18439 };
18440
18441
18442 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18443     /** @ignore Called by the grid automatically. Do not call directly. */
18444     init : function(grid){
18445         this.grid = grid;
18446         this.initEvents();
18447     },
18448
18449     /**
18450      * Locks the selections.
18451      */
18452     lock : function(){
18453         this.locked = true;
18454     },
18455
18456     /**
18457      * Unlocks the selections.
18458      */
18459     unlock : function(){
18460         this.locked = false;
18461     },
18462
18463     /**
18464      * Returns true if the selections are locked.
18465      * @return {Boolean}
18466      */
18467     isLocked : function(){
18468         return this.locked;
18469     }
18470 });
18471 /**
18472  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18473  * @class Roo.bootstrap.Table.RowSelectionModel
18474  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18475  * It supports multiple selections and keyboard selection/navigation. 
18476  * @constructor
18477  * @param {Object} config
18478  */
18479
18480 Roo.bootstrap.Table.RowSelectionModel = function(config){
18481     Roo.apply(this, config);
18482     this.selections = new Roo.util.MixedCollection(false, function(o){
18483         return o.id;
18484     });
18485
18486     this.last = false;
18487     this.lastActive = false;
18488
18489     this.addEvents({
18490         /**
18491              * @event selectionchange
18492              * Fires when the selection changes
18493              * @param {SelectionModel} this
18494              */
18495             "selectionchange" : true,
18496         /**
18497              * @event afterselectionchange
18498              * Fires after the selection changes (eg. by key press or clicking)
18499              * @param {SelectionModel} this
18500              */
18501             "afterselectionchange" : true,
18502         /**
18503              * @event beforerowselect
18504              * Fires when a row is selected being selected, return false to cancel.
18505              * @param {SelectionModel} this
18506              * @param {Number} rowIndex The selected index
18507              * @param {Boolean} keepExisting False if other selections will be cleared
18508              */
18509             "beforerowselect" : true,
18510         /**
18511              * @event rowselect
18512              * Fires when a row is selected.
18513              * @param {SelectionModel} this
18514              * @param {Number} rowIndex The selected index
18515              * @param {Roo.data.Record} r The record
18516              */
18517             "rowselect" : true,
18518         /**
18519              * @event rowdeselect
18520              * Fires when a row is deselected.
18521              * @param {SelectionModel} this
18522              * @param {Number} rowIndex The selected index
18523              */
18524         "rowdeselect" : true
18525     });
18526     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18527     this.locked = false;
18528 };
18529
18530 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18531     /**
18532      * @cfg {Boolean} singleSelect
18533      * True to allow selection of only one row at a time (defaults to false)
18534      */
18535     singleSelect : false,
18536
18537     // private
18538     initEvents : function(){
18539
18540         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18541             this.grid.on("mousedown", this.handleMouseDown, this);
18542         }else{ // allow click to work like normal
18543             this.grid.on("rowclick", this.handleDragableRowClick, this);
18544         }
18545
18546         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18547             "up" : function(e){
18548                 if(!e.shiftKey){
18549                     this.selectPrevious(e.shiftKey);
18550                 }else if(this.last !== false && this.lastActive !== false){
18551                     var last = this.last;
18552                     this.selectRange(this.last,  this.lastActive-1);
18553                     this.grid.getView().focusRow(this.lastActive);
18554                     if(last !== false){
18555                         this.last = last;
18556                     }
18557                 }else{
18558                     this.selectFirstRow();
18559                 }
18560                 this.fireEvent("afterselectionchange", this);
18561             },
18562             "down" : function(e){
18563                 if(!e.shiftKey){
18564                     this.selectNext(e.shiftKey);
18565                 }else if(this.last !== false && this.lastActive !== false){
18566                     var last = this.last;
18567                     this.selectRange(this.last,  this.lastActive+1);
18568                     this.grid.getView().focusRow(this.lastActive);
18569                     if(last !== false){
18570                         this.last = last;
18571                     }
18572                 }else{
18573                     this.selectFirstRow();
18574                 }
18575                 this.fireEvent("afterselectionchange", this);
18576             },
18577             scope: this
18578         });
18579
18580         var view = this.grid.view;
18581         view.on("refresh", this.onRefresh, this);
18582         view.on("rowupdated", this.onRowUpdated, this);
18583         view.on("rowremoved", this.onRemove, this);
18584     },
18585
18586     // private
18587     onRefresh : function(){
18588         var ds = this.grid.dataSource, i, v = this.grid.view;
18589         var s = this.selections;
18590         s.each(function(r){
18591             if((i = ds.indexOfId(r.id)) != -1){
18592                 v.onRowSelect(i);
18593             }else{
18594                 s.remove(r);
18595             }
18596         });
18597     },
18598
18599     // private
18600     onRemove : function(v, index, r){
18601         this.selections.remove(r);
18602     },
18603
18604     // private
18605     onRowUpdated : function(v, index, r){
18606         if(this.isSelected(r)){
18607             v.onRowSelect(index);
18608         }
18609     },
18610
18611     /**
18612      * Select records.
18613      * @param {Array} records The records to select
18614      * @param {Boolean} keepExisting (optional) True to keep existing selections
18615      */
18616     selectRecords : function(records, keepExisting){
18617         if(!keepExisting){
18618             this.clearSelections();
18619         }
18620         var ds = this.grid.dataSource;
18621         for(var i = 0, len = records.length; i < len; i++){
18622             this.selectRow(ds.indexOf(records[i]), true);
18623         }
18624     },
18625
18626     /**
18627      * Gets the number of selected rows.
18628      * @return {Number}
18629      */
18630     getCount : function(){
18631         return this.selections.length;
18632     },
18633
18634     /**
18635      * Selects the first row in the grid.
18636      */
18637     selectFirstRow : function(){
18638         this.selectRow(0);
18639     },
18640
18641     /**
18642      * Select the last row.
18643      * @param {Boolean} keepExisting (optional) True to keep existing selections
18644      */
18645     selectLastRow : function(keepExisting){
18646         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18647     },
18648
18649     /**
18650      * Selects the row immediately following the last selected row.
18651      * @param {Boolean} keepExisting (optional) True to keep existing selections
18652      */
18653     selectNext : function(keepExisting){
18654         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18655             this.selectRow(this.last+1, keepExisting);
18656             this.grid.getView().focusRow(this.last);
18657         }
18658     },
18659
18660     /**
18661      * Selects the row that precedes the last selected row.
18662      * @param {Boolean} keepExisting (optional) True to keep existing selections
18663      */
18664     selectPrevious : function(keepExisting){
18665         if(this.last){
18666             this.selectRow(this.last-1, keepExisting);
18667             this.grid.getView().focusRow(this.last);
18668         }
18669     },
18670
18671     /**
18672      * Returns the selected records
18673      * @return {Array} Array of selected records
18674      */
18675     getSelections : function(){
18676         return [].concat(this.selections.items);
18677     },
18678
18679     /**
18680      * Returns the first selected record.
18681      * @return {Record}
18682      */
18683     getSelected : function(){
18684         return this.selections.itemAt(0);
18685     },
18686
18687
18688     /**
18689      * Clears all selections.
18690      */
18691     clearSelections : function(fast){
18692         if(this.locked) return;
18693         if(fast !== true){
18694             var ds = this.grid.dataSource;
18695             var s = this.selections;
18696             s.each(function(r){
18697                 this.deselectRow(ds.indexOfId(r.id));
18698             }, this);
18699             s.clear();
18700         }else{
18701             this.selections.clear();
18702         }
18703         this.last = false;
18704     },
18705
18706
18707     /**
18708      * Selects all rows.
18709      */
18710     selectAll : function(){
18711         if(this.locked) return;
18712         this.selections.clear();
18713         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18714             this.selectRow(i, true);
18715         }
18716     },
18717
18718     /**
18719      * Returns True if there is a selection.
18720      * @return {Boolean}
18721      */
18722     hasSelection : function(){
18723         return this.selections.length > 0;
18724     },
18725
18726     /**
18727      * Returns True if the specified row is selected.
18728      * @param {Number/Record} record The record or index of the record to check
18729      * @return {Boolean}
18730      */
18731     isSelected : function(index){
18732         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18733         return (r && this.selections.key(r.id) ? true : false);
18734     },
18735
18736     /**
18737      * Returns True if the specified record id is selected.
18738      * @param {String} id The id of record to check
18739      * @return {Boolean}
18740      */
18741     isIdSelected : function(id){
18742         return (this.selections.key(id) ? true : false);
18743     },
18744
18745     // private
18746     handleMouseDown : function(e, t){
18747         var view = this.grid.getView(), rowIndex;
18748         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18749             return;
18750         };
18751         if(e.shiftKey && this.last !== false){
18752             var last = this.last;
18753             this.selectRange(last, rowIndex, e.ctrlKey);
18754             this.last = last; // reset the last
18755             view.focusRow(rowIndex);
18756         }else{
18757             var isSelected = this.isSelected(rowIndex);
18758             if(e.button !== 0 && isSelected){
18759                 view.focusRow(rowIndex);
18760             }else if(e.ctrlKey && isSelected){
18761                 this.deselectRow(rowIndex);
18762             }else if(!isSelected){
18763                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18764                 view.focusRow(rowIndex);
18765             }
18766         }
18767         this.fireEvent("afterselectionchange", this);
18768     },
18769     // private
18770     handleDragableRowClick :  function(grid, rowIndex, e) 
18771     {
18772         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18773             this.selectRow(rowIndex, false);
18774             grid.view.focusRow(rowIndex);
18775              this.fireEvent("afterselectionchange", this);
18776         }
18777     },
18778     
18779     /**
18780      * Selects multiple rows.
18781      * @param {Array} rows Array of the indexes of the row to select
18782      * @param {Boolean} keepExisting (optional) True to keep existing selections
18783      */
18784     selectRows : function(rows, keepExisting){
18785         if(!keepExisting){
18786             this.clearSelections();
18787         }
18788         for(var i = 0, len = rows.length; i < len; i++){
18789             this.selectRow(rows[i], true);
18790         }
18791     },
18792
18793     /**
18794      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18795      * @param {Number} startRow The index of the first row in the range
18796      * @param {Number} endRow The index of the last row in the range
18797      * @param {Boolean} keepExisting (optional) True to retain existing selections
18798      */
18799     selectRange : function(startRow, endRow, keepExisting){
18800         if(this.locked) return;
18801         if(!keepExisting){
18802             this.clearSelections();
18803         }
18804         if(startRow <= endRow){
18805             for(var i = startRow; i <= endRow; i++){
18806                 this.selectRow(i, true);
18807             }
18808         }else{
18809             for(var i = startRow; i >= endRow; i--){
18810                 this.selectRow(i, true);
18811             }
18812         }
18813     },
18814
18815     /**
18816      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18817      * @param {Number} startRow The index of the first row in the range
18818      * @param {Number} endRow The index of the last row in the range
18819      */
18820     deselectRange : function(startRow, endRow, preventViewNotify){
18821         if(this.locked) return;
18822         for(var i = startRow; i <= endRow; i++){
18823             this.deselectRow(i, preventViewNotify);
18824         }
18825     },
18826
18827     /**
18828      * Selects a row.
18829      * @param {Number} row The index of the row to select
18830      * @param {Boolean} keepExisting (optional) True to keep existing selections
18831      */
18832     selectRow : function(index, keepExisting, preventViewNotify){
18833         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18834         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18835             if(!keepExisting || this.singleSelect){
18836                 this.clearSelections();
18837             }
18838             var r = this.grid.dataSource.getAt(index);
18839             this.selections.add(r);
18840             this.last = this.lastActive = index;
18841             if(!preventViewNotify){
18842                 this.grid.getView().onRowSelect(index);
18843             }
18844             this.fireEvent("rowselect", this, index, r);
18845             this.fireEvent("selectionchange", this);
18846         }
18847     },
18848
18849     /**
18850      * Deselects a row.
18851      * @param {Number} row The index of the row to deselect
18852      */
18853     deselectRow : function(index, preventViewNotify){
18854         if(this.locked) return;
18855         if(this.last == index){
18856             this.last = false;
18857         }
18858         if(this.lastActive == index){
18859             this.lastActive = false;
18860         }
18861         var r = this.grid.dataSource.getAt(index);
18862         this.selections.remove(r);
18863         if(!preventViewNotify){
18864             this.grid.getView().onRowDeselect(index);
18865         }
18866         this.fireEvent("rowdeselect", this, index);
18867         this.fireEvent("selectionchange", this);
18868     },
18869
18870     // private
18871     restoreLast : function(){
18872         if(this._last){
18873             this.last = this._last;
18874         }
18875     },
18876
18877     // private
18878     acceptsNav : function(row, col, cm){
18879         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18880     },
18881
18882     // private
18883     onEditorKey : function(field, e){
18884         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18885         if(k == e.TAB){
18886             e.stopEvent();
18887             ed.completeEdit();
18888             if(e.shiftKey){
18889                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18890             }else{
18891                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18892             }
18893         }else if(k == e.ENTER && !e.ctrlKey){
18894             e.stopEvent();
18895             ed.completeEdit();
18896             if(e.shiftKey){
18897                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18898             }else{
18899                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18900             }
18901         }else if(k == e.ESC){
18902             ed.cancelEdit();
18903         }
18904         if(newCell){
18905             g.startEditing(newCell[0], newCell[1]);
18906         }
18907     }
18908 });/*
18909  * Based on:
18910  * Ext JS Library 1.1.1
18911  * Copyright(c) 2006-2007, Ext JS, LLC.
18912  *
18913  * Originally Released Under LGPL - original licence link has changed is not relivant.
18914  *
18915  * Fork - LGPL
18916  * <script type="text/javascript">
18917  */
18918  
18919 /**
18920  * @class Roo.bootstrap.PagingToolbar
18921  * @extends Roo.Row
18922  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18923  * @constructor
18924  * Create a new PagingToolbar
18925  * @param {Object} config The config object
18926  */
18927 Roo.bootstrap.PagingToolbar = function(config)
18928 {
18929     // old args format still supported... - xtype is prefered..
18930         // created from xtype...
18931     var ds = config.dataSource;
18932     this.toolbarItems = [];
18933     if (config.items) {
18934         this.toolbarItems = config.items;
18935 //        config.items = [];
18936     }
18937     
18938     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18939     this.ds = ds;
18940     this.cursor = 0;
18941     if (ds) { 
18942         this.bind(ds);
18943     }
18944     
18945     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18946     
18947 };
18948
18949 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18950     /**
18951      * @cfg {Roo.data.Store} dataSource
18952      * The underlying data store providing the paged data
18953      */
18954     /**
18955      * @cfg {String/HTMLElement/Element} container
18956      * container The id or element that will contain the toolbar
18957      */
18958     /**
18959      * @cfg {Boolean} displayInfo
18960      * True to display the displayMsg (defaults to false)
18961      */
18962     /**
18963      * @cfg {Number} pageSize
18964      * The number of records to display per page (defaults to 20)
18965      */
18966     pageSize: 20,
18967     /**
18968      * @cfg {String} displayMsg
18969      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18970      */
18971     displayMsg : 'Displaying {0} - {1} of {2}',
18972     /**
18973      * @cfg {String} emptyMsg
18974      * The message to display when no records are found (defaults to "No data to display")
18975      */
18976     emptyMsg : 'No data to display',
18977     /**
18978      * Customizable piece of the default paging text (defaults to "Page")
18979      * @type String
18980      */
18981     beforePageText : "Page",
18982     /**
18983      * Customizable piece of the default paging text (defaults to "of %0")
18984      * @type String
18985      */
18986     afterPageText : "of {0}",
18987     /**
18988      * Customizable piece of the default paging text (defaults to "First Page")
18989      * @type String
18990      */
18991     firstText : "First Page",
18992     /**
18993      * Customizable piece of the default paging text (defaults to "Previous Page")
18994      * @type String
18995      */
18996     prevText : "Previous Page",
18997     /**
18998      * Customizable piece of the default paging text (defaults to "Next Page")
18999      * @type String
19000      */
19001     nextText : "Next Page",
19002     /**
19003      * Customizable piece of the default paging text (defaults to "Last Page")
19004      * @type String
19005      */
19006     lastText : "Last Page",
19007     /**
19008      * Customizable piece of the default paging text (defaults to "Refresh")
19009      * @type String
19010      */
19011     refreshText : "Refresh",
19012
19013     buttons : false,
19014     // private
19015     onRender : function(ct, position) 
19016     {
19017         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19018         this.navgroup.parentId = this.id;
19019         this.navgroup.onRender(this.el, null);
19020         // add the buttons to the navgroup
19021         
19022         if(this.displayInfo){
19023             Roo.log(this.el.select('ul.navbar-nav',true).first());
19024             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19025             this.displayEl = this.el.select('.x-paging-info', true).first();
19026 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19027 //            this.displayEl = navel.el.select('span',true).first();
19028         }
19029         
19030         var _this = this;
19031         
19032         if(this.buttons){
19033             Roo.each(_this.buttons, function(e){
19034                Roo.factory(e).onRender(_this.el, null);
19035             });
19036         }
19037             
19038         Roo.each(_this.toolbarItems, function(e) {
19039             _this.navgroup.addItem(e);
19040         });
19041         
19042         this.first = this.navgroup.addItem({
19043             tooltip: this.firstText,
19044             cls: "prev",
19045             icon : 'fa fa-backward',
19046             disabled: true,
19047             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19048         });
19049         
19050         this.prev =  this.navgroup.addItem({
19051             tooltip: this.prevText,
19052             cls: "prev",
19053             icon : 'fa fa-step-backward',
19054             disabled: true,
19055             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19056         });
19057     //this.addSeparator();
19058         
19059         
19060         var field = this.navgroup.addItem( {
19061             tagtype : 'span',
19062             cls : 'x-paging-position',
19063             
19064             html : this.beforePageText  +
19065                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19066                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19067          } ); //?? escaped?
19068         
19069         this.field = field.el.select('input', true).first();
19070         this.field.on("keydown", this.onPagingKeydown, this);
19071         this.field.on("focus", function(){this.dom.select();});
19072     
19073     
19074         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19075         //this.field.setHeight(18);
19076         //this.addSeparator();
19077         this.next = this.navgroup.addItem({
19078             tooltip: this.nextText,
19079             cls: "next",
19080             html : ' <i class="fa fa-step-forward">',
19081             disabled: true,
19082             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19083         });
19084         this.last = this.navgroup.addItem({
19085             tooltip: this.lastText,
19086             icon : 'fa fa-forward',
19087             cls: "next",
19088             disabled: true,
19089             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19090         });
19091     //this.addSeparator();
19092         this.loading = this.navgroup.addItem({
19093             tooltip: this.refreshText,
19094             icon: 'fa fa-refresh',
19095             
19096             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19097         });
19098
19099     },
19100
19101     // private
19102     updateInfo : function(){
19103         if(this.displayEl){
19104             var count = this.ds.getCount();
19105             var msg = count == 0 ?
19106                 this.emptyMsg :
19107                 String.format(
19108                     this.displayMsg,
19109                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19110                 );
19111             this.displayEl.update(msg);
19112         }
19113     },
19114
19115     // private
19116     onLoad : function(ds, r, o){
19117        this.cursor = o.params ? o.params.start : 0;
19118        var d = this.getPageData(),
19119             ap = d.activePage,
19120             ps = d.pages;
19121         
19122        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19123        this.field.dom.value = ap;
19124        this.first.setDisabled(ap == 1);
19125        this.prev.setDisabled(ap == 1);
19126        this.next.setDisabled(ap == ps);
19127        this.last.setDisabled(ap == ps);
19128        this.loading.enable();
19129        this.updateInfo();
19130     },
19131
19132     // private
19133     getPageData : function(){
19134         var total = this.ds.getTotalCount();
19135         return {
19136             total : total,
19137             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19138             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19139         };
19140     },
19141
19142     // private
19143     onLoadError : function(){
19144         this.loading.enable();
19145     },
19146
19147     // private
19148     onPagingKeydown : function(e){
19149         var k = e.getKey();
19150         var d = this.getPageData();
19151         if(k == e.RETURN){
19152             var v = this.field.dom.value, pageNum;
19153             if(!v || isNaN(pageNum = parseInt(v, 10))){
19154                 this.field.dom.value = d.activePage;
19155                 return;
19156             }
19157             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19158             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19159             e.stopEvent();
19160         }
19161         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))
19162         {
19163           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19164           this.field.dom.value = pageNum;
19165           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19166           e.stopEvent();
19167         }
19168         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19169         {
19170           var v = this.field.dom.value, pageNum; 
19171           var increment = (e.shiftKey) ? 10 : 1;
19172           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19173             increment *= -1;
19174           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19175             this.field.dom.value = d.activePage;
19176             return;
19177           }
19178           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19179           {
19180             this.field.dom.value = parseInt(v, 10) + increment;
19181             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19182             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19183           }
19184           e.stopEvent();
19185         }
19186     },
19187
19188     // private
19189     beforeLoad : function(){
19190         if(this.loading){
19191             this.loading.disable();
19192         }
19193     },
19194
19195     // private
19196     onClick : function(which){
19197         var ds = this.ds;
19198         if (!ds) {
19199             return;
19200         }
19201         switch(which){
19202             case "first":
19203                 ds.load({params:{start: 0, limit: this.pageSize}});
19204             break;
19205             case "prev":
19206                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19207             break;
19208             case "next":
19209                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19210             break;
19211             case "last":
19212                 var total = ds.getTotalCount();
19213                 var extra = total % this.pageSize;
19214                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19215                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19216             break;
19217             case "refresh":
19218                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19219             break;
19220         }
19221     },
19222
19223     /**
19224      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19225      * @param {Roo.data.Store} store The data store to unbind
19226      */
19227     unbind : function(ds){
19228         ds.un("beforeload", this.beforeLoad, this);
19229         ds.un("load", this.onLoad, this);
19230         ds.un("loadexception", this.onLoadError, this);
19231         ds.un("remove", this.updateInfo, this);
19232         ds.un("add", this.updateInfo, this);
19233         this.ds = undefined;
19234     },
19235
19236     /**
19237      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19238      * @param {Roo.data.Store} store The data store to bind
19239      */
19240     bind : function(ds){
19241         ds.on("beforeload", this.beforeLoad, this);
19242         ds.on("load", this.onLoad, this);
19243         ds.on("loadexception", this.onLoadError, this);
19244         ds.on("remove", this.updateInfo, this);
19245         ds.on("add", this.updateInfo, this);
19246         this.ds = ds;
19247     }
19248 });/*
19249  * - LGPL
19250  *
19251  * element
19252  * 
19253  */
19254
19255 /**
19256  * @class Roo.bootstrap.MessageBar
19257  * @extends Roo.bootstrap.Component
19258  * Bootstrap MessageBar class
19259  * @cfg {String} html contents of the MessageBar
19260  * @cfg {String} weight (info | success | warning | danger) default info
19261  * @cfg {String} beforeClass insert the bar before the given class
19262  * @cfg {Boolean} closable (true | false) default false
19263  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19264  * 
19265  * @constructor
19266  * Create a new Element
19267  * @param {Object} config The config object
19268  */
19269
19270 Roo.bootstrap.MessageBar = function(config){
19271     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19272 };
19273
19274 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19275     
19276     html: '',
19277     weight: 'info',
19278     closable: false,
19279     fixed: false,
19280     beforeClass: 'bootstrap-sticky-wrap',
19281     
19282     getAutoCreate : function(){
19283         
19284         var cfg = {
19285             tag: 'div',
19286             cls: 'alert alert-dismissable alert-' + this.weight,
19287             cn: [
19288                 {
19289                     tag: 'span',
19290                     cls: 'message',
19291                     html: this.html || ''
19292                 }
19293             ]
19294         }
19295         
19296         if(this.fixed){
19297             cfg.cls += ' alert-messages-fixed';
19298         }
19299         
19300         if(this.closable){
19301             cfg.cn.push({
19302                 tag: 'button',
19303                 cls: 'close',
19304                 html: 'x'
19305             });
19306         }
19307         
19308         return cfg;
19309     },
19310     
19311     onRender : function(ct, position)
19312     {
19313         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19314         
19315         if(!this.el){
19316             var cfg = Roo.apply({},  this.getAutoCreate());
19317             cfg.id = Roo.id();
19318             
19319             if (this.cls) {
19320                 cfg.cls += ' ' + this.cls;
19321             }
19322             if (this.style) {
19323                 cfg.style = this.style;
19324             }
19325             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19326             
19327             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19328         }
19329         
19330         this.el.select('>button.close').on('click', this.hide, this);
19331         
19332     },
19333     
19334     show : function()
19335     {
19336         if (!this.rendered) {
19337             this.render();
19338         }
19339         
19340         this.el.show();
19341         
19342         this.fireEvent('show', this);
19343         
19344     },
19345     
19346     hide : function()
19347     {
19348         if (!this.rendered) {
19349             this.render();
19350         }
19351         
19352         this.el.hide();
19353         
19354         this.fireEvent('hide', this);
19355     },
19356     
19357     update : function()
19358     {
19359 //        var e = this.el.dom.firstChild;
19360 //        
19361 //        if(this.closable){
19362 //            e = e.nextSibling;
19363 //        }
19364 //        
19365 //        e.data = this.html || '';
19366
19367         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19368     }
19369    
19370 });
19371
19372  
19373
19374      /*
19375  * - LGPL
19376  *
19377  * Graph
19378  * 
19379  */
19380
19381
19382 /**
19383  * @class Roo.bootstrap.Graph
19384  * @extends Roo.bootstrap.Component
19385  * Bootstrap Graph class
19386 > Prameters
19387  -sm {number} sm 4
19388  -md {number} md 5
19389  @cfg {String} graphtype  bar | vbar | pie
19390  @cfg {number} g_x coodinator | centre x (pie)
19391  @cfg {number} g_y coodinator | centre y (pie)
19392  @cfg {number} g_r radius (pie)
19393  @cfg {number} g_height height of the chart (respected by all elements in the set)
19394  @cfg {number} g_width width of the chart (respected by all elements in the set)
19395  @cfg {Object} title The title of the chart
19396     
19397  -{Array}  values
19398  -opts (object) options for the chart 
19399      o {
19400      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19401      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19402      o vgutter (number)
19403      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.
19404      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19405      o to
19406      o stretch (boolean)
19407      o }
19408  -opts (object) options for the pie
19409      o{
19410      o cut
19411      o startAngle (number)
19412      o endAngle (number)
19413      } 
19414  *
19415  * @constructor
19416  * Create a new Input
19417  * @param {Object} config The config object
19418  */
19419
19420 Roo.bootstrap.Graph = function(config){
19421     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19422     
19423     this.addEvents({
19424         // img events
19425         /**
19426          * @event click
19427          * The img click event for the img.
19428          * @param {Roo.EventObject} e
19429          */
19430         "click" : true
19431     });
19432 };
19433
19434 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19435     
19436     sm: 4,
19437     md: 5,
19438     graphtype: 'bar',
19439     g_height: 250,
19440     g_width: 400,
19441     g_x: 50,
19442     g_y: 50,
19443     g_r: 30,
19444     opts:{
19445         //g_colors: this.colors,
19446         g_type: 'soft',
19447         g_gutter: '20%'
19448
19449     },
19450     title : false,
19451
19452     getAutoCreate : function(){
19453         
19454         var cfg = {
19455             tag: 'div',
19456             html : null
19457         }
19458         
19459         
19460         return  cfg;
19461     },
19462
19463     onRender : function(ct,position){
19464         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19465         this.raphael = Raphael(this.el.dom);
19466         
19467                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19468                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19469                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19470                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19471                 /*
19472                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19473                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19474                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19475                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19476                 
19477                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19478                 r.barchart(330, 10, 300, 220, data1);
19479                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19480                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19481                 */
19482                 
19483                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19484                 // r.barchart(30, 30, 560, 250,  xdata, {
19485                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19486                 //     axis : "0 0 1 1",
19487                 //     axisxlabels :  xdata
19488                 //     //yvalues : cols,
19489                    
19490                 // });
19491 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19492 //        
19493 //        this.load(null,xdata,{
19494 //                axis : "0 0 1 1",
19495 //                axisxlabels :  xdata
19496 //                });
19497
19498     },
19499
19500     load : function(graphtype,xdata,opts){
19501         this.raphael.clear();
19502         if(!graphtype) {
19503             graphtype = this.graphtype;
19504         }
19505         if(!opts){
19506             opts = this.opts;
19507         }
19508         var r = this.raphael,
19509             fin = function () {
19510                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19511             },
19512             fout = function () {
19513                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19514             },
19515             pfin = function() {
19516                 this.sector.stop();
19517                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19518
19519                 if (this.label) {
19520                     this.label[0].stop();
19521                     this.label[0].attr({ r: 7.5 });
19522                     this.label[1].attr({ "font-weight": 800 });
19523                 }
19524             },
19525             pfout = function() {
19526                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19527
19528                 if (this.label) {
19529                     this.label[0].animate({ r: 5 }, 500, "bounce");
19530                     this.label[1].attr({ "font-weight": 400 });
19531                 }
19532             };
19533
19534         switch(graphtype){
19535             case 'bar':
19536                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19537                 break;
19538             case 'hbar':
19539                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19540                 break;
19541             case 'pie':
19542 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19543 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19544 //            
19545                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19546                 
19547                 break;
19548
19549         }
19550         
19551         if(this.title){
19552             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19553         }
19554         
19555     },
19556     
19557     setTitle: function(o)
19558     {
19559         this.title = o;
19560     },
19561     
19562     initEvents: function() {
19563         
19564         if(!this.href){
19565             this.el.on('click', this.onClick, this);
19566         }
19567     },
19568     
19569     onClick : function(e)
19570     {
19571         Roo.log('img onclick');
19572         this.fireEvent('click', this, e);
19573     }
19574    
19575 });
19576
19577  
19578 /*
19579  * - LGPL
19580  *
19581  * numberBox
19582  * 
19583  */
19584 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19585
19586 /**
19587  * @class Roo.bootstrap.dash.NumberBox
19588  * @extends Roo.bootstrap.Component
19589  * Bootstrap NumberBox class
19590  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19591  * @cfg {String} headline Box headline
19592  * @cfg {String} content Box content
19593  * @cfg {String} icon Box icon
19594  * @cfg {String} footer Footer text
19595  * @cfg {String} fhref Footer href
19596  * 
19597  * @constructor
19598  * Create a new NumberBox
19599  * @param {Object} config The config object
19600  */
19601
19602
19603 Roo.bootstrap.dash.NumberBox = function(config){
19604     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19605     
19606 };
19607
19608 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19609     
19610     bgcolor : 'aqua',
19611     headline : '',
19612     content : '',
19613     icon : '',
19614     footer : '',
19615     fhref : '',
19616     ficon : '',
19617     
19618     getAutoCreate : function(){
19619         
19620         var cfg = {
19621             tag : 'div',
19622             cls : 'small-box bg-' + this.bgcolor,
19623             cn : [
19624                 {
19625                     tag : 'div',
19626                     cls : 'inner',
19627                     cn :[
19628                         {
19629                             tag : 'h3',
19630                             cls : 'roo-headline',
19631                             html : this.headline
19632                         },
19633                         {
19634                             tag : 'p',
19635                             cls : 'roo-content',
19636                             html : this.content
19637                         }
19638                     ]
19639                 }
19640             ]
19641         }
19642         
19643         if(this.icon){
19644             cfg.cn.push({
19645                 tag : 'div',
19646                 cls : 'icon',
19647                 cn :[
19648                     {
19649                         tag : 'i',
19650                         cls : 'ion ' + this.icon
19651                     }
19652                 ]
19653             });
19654         }
19655         
19656         if(this.footer){
19657             var footer = {
19658                 tag : 'a',
19659                 cls : 'small-box-footer',
19660                 href : this.fhref || '#',
19661                 html : this.footer
19662             };
19663             
19664             cfg.cn.push(footer);
19665             
19666         }
19667         
19668         return  cfg;
19669     },
19670
19671     onRender : function(ct,position){
19672         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19673
19674
19675        
19676                 
19677     },
19678
19679     setHeadline: function (value)
19680     {
19681         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19682     },
19683     
19684     setFooter: function (value, href)
19685     {
19686         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19687         
19688         if(href){
19689             this.el.select('a.small-box-footer',true).first().attr('href', href);
19690         }
19691         
19692     },
19693
19694     setContent: function (value)
19695     {
19696         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19697     },
19698
19699     initEvents: function() 
19700     {   
19701         
19702     }
19703     
19704 });
19705
19706  
19707 /*
19708  * - LGPL
19709  *
19710  * TabBox
19711  * 
19712  */
19713 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19714
19715 /**
19716  * @class Roo.bootstrap.dash.TabBox
19717  * @extends Roo.bootstrap.Component
19718  * Bootstrap TabBox class
19719  * @cfg {String} title Title of the TabBox
19720  * @cfg {String} icon Icon of the TabBox
19721  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19722  * 
19723  * @constructor
19724  * Create a new TabBox
19725  * @param {Object} config The config object
19726  */
19727
19728
19729 Roo.bootstrap.dash.TabBox = function(config){
19730     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19731     this.addEvents({
19732         // raw events
19733         /**
19734          * @event addpane
19735          * When a pane is added
19736          * @param {Roo.bootstrap.dash.TabPane} pane
19737          */
19738         "addpane" : true
19739          
19740     });
19741 };
19742
19743 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19744
19745     title : '',
19746     icon : false,
19747     showtabs : true,
19748     
19749     getChildContainer : function()
19750     {
19751         return this.el.select('.tab-content', true).first();
19752     },
19753     
19754     getAutoCreate : function(){
19755         
19756         var header = {
19757             tag: 'li',
19758             cls: 'pull-left header',
19759             html: this.title,
19760             cn : []
19761         };
19762         
19763         if(this.icon){
19764             header.cn.push({
19765                 tag: 'i',
19766                 cls: 'fa ' + this.icon
19767             });
19768         }
19769         
19770         
19771         var cfg = {
19772             tag: 'div',
19773             cls: 'nav-tabs-custom',
19774             cn: [
19775                 {
19776                     tag: 'ul',
19777                     cls: 'nav nav-tabs pull-right',
19778                     cn: [
19779                         header
19780                     ]
19781                 },
19782                 {
19783                     tag: 'div',
19784                     cls: 'tab-content no-padding',
19785                     cn: []
19786                 }
19787             ]
19788         }
19789
19790         return  cfg;
19791     },
19792     initEvents : function()
19793     {
19794         //Roo.log('add add pane handler');
19795         this.on('addpane', this.onAddPane, this);
19796     },
19797      /**
19798      * Updates the box title
19799      * @param {String} html to set the title to.
19800      */
19801     setTitle : function(value)
19802     {
19803         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19804     },
19805     onAddPane : function(pane)
19806     {
19807         //Roo.log('addpane');
19808         //Roo.log(pane);
19809         // tabs are rendere left to right..
19810         if(!this.showtabs){
19811             return;
19812         }
19813         
19814         var ctr = this.el.select('.nav-tabs', true).first();
19815          
19816          
19817         var existing = ctr.select('.nav-tab',true);
19818         var qty = existing.getCount();;
19819         
19820         
19821         var tab = ctr.createChild({
19822             tag : 'li',
19823             cls : 'nav-tab' + (qty ? '' : ' active'),
19824             cn : [
19825                 {
19826                     tag : 'a',
19827                     href:'#',
19828                     html : pane.title
19829                 }
19830             ]
19831         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19832         pane.tab = tab;
19833         
19834         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19835         if (!qty) {
19836             pane.el.addClass('active');
19837         }
19838         
19839                 
19840     },
19841     onTabClick : function(ev,un,ob,pane)
19842     {
19843         //Roo.log('tab - prev default');
19844         ev.preventDefault();
19845         
19846         
19847         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19848         pane.tab.addClass('active');
19849         //Roo.log(pane.title);
19850         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19851         // technically we should have a deactivate event.. but maybe add later.
19852         // and it should not de-activate the selected tab...
19853         
19854         pane.el.addClass('active');
19855         pane.fireEvent('activate');
19856         
19857         
19858     }
19859     
19860     
19861 });
19862
19863  
19864 /*
19865  * - LGPL
19866  *
19867  * Tab pane
19868  * 
19869  */
19870 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19871 /**
19872  * @class Roo.bootstrap.TabPane
19873  * @extends Roo.bootstrap.Component
19874  * Bootstrap TabPane class
19875  * @cfg {Boolean} active (false | true) Default false
19876  * @cfg {String} title title of panel
19877
19878  * 
19879  * @constructor
19880  * Create a new TabPane
19881  * @param {Object} config The config object
19882  */
19883
19884 Roo.bootstrap.dash.TabPane = function(config){
19885     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19886     
19887     this.addEvents({
19888         // raw events
19889         /**
19890          * @event activate
19891          * When a pane is activated
19892          * @param {Roo.bootstrap.dash.TabPane} pane
19893          */
19894         "activate" : true
19895          
19896     });
19897 };
19898
19899 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19900     
19901     active : false,
19902     title : '',
19903     
19904     // the tabBox that this is attached to.
19905     tab : false,
19906      
19907     getAutoCreate : function() 
19908     {
19909         var cfg = {
19910             tag: 'div',
19911             cls: 'tab-pane'
19912         }
19913         
19914         if(this.active){
19915             cfg.cls += ' active';
19916         }
19917         
19918         return cfg;
19919     },
19920     initEvents  : function()
19921     {
19922         //Roo.log('trigger add pane handler');
19923         this.parent().fireEvent('addpane', this)
19924     },
19925     
19926      /**
19927      * Updates the tab title 
19928      * @param {String} html to set the title to.
19929      */
19930     setTitle: function(str)
19931     {
19932         if (!this.tab) {
19933             return;
19934         }
19935         this.title = str;
19936         this.tab.select('a', true).first().dom.innerHTML = str;
19937         
19938     }
19939     
19940     
19941     
19942 });
19943
19944  
19945
19946
19947  /*
19948  * - LGPL
19949  *
19950  * menu
19951  * 
19952  */
19953 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19954
19955 /**
19956  * @class Roo.bootstrap.menu.Menu
19957  * @extends Roo.bootstrap.Component
19958  * Bootstrap Menu class - container for Menu
19959  * @cfg {String} html Text of the menu
19960  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19961  * @cfg {String} icon Font awesome icon
19962  * @cfg {String} pos Menu align to (top | bottom) default bottom
19963  * 
19964  * 
19965  * @constructor
19966  * Create a new Menu
19967  * @param {Object} config The config object
19968  */
19969
19970
19971 Roo.bootstrap.menu.Menu = function(config){
19972     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19973     
19974     this.addEvents({
19975         /**
19976          * @event beforeshow
19977          * Fires before this menu is displayed
19978          * @param {Roo.bootstrap.menu.Menu} this
19979          */
19980         beforeshow : true,
19981         /**
19982          * @event beforehide
19983          * Fires before this menu is hidden
19984          * @param {Roo.bootstrap.menu.Menu} this
19985          */
19986         beforehide : true,
19987         /**
19988          * @event show
19989          * Fires after this menu is displayed
19990          * @param {Roo.bootstrap.menu.Menu} this
19991          */
19992         show : true,
19993         /**
19994          * @event hide
19995          * Fires after this menu is hidden
19996          * @param {Roo.bootstrap.menu.Menu} this
19997          */
19998         hide : true,
19999         /**
20000          * @event click
20001          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20002          * @param {Roo.bootstrap.menu.Menu} this
20003          * @param {Roo.EventObject} e
20004          */
20005         click : true
20006     });
20007     
20008 };
20009
20010 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20011     
20012     submenu : false,
20013     html : '',
20014     weight : 'default',
20015     icon : false,
20016     pos : 'bottom',
20017     
20018     
20019     getChildContainer : function() {
20020         if(this.isSubMenu){
20021             return this.el;
20022         }
20023         
20024         return this.el.select('ul.dropdown-menu', true).first();  
20025     },
20026     
20027     getAutoCreate : function()
20028     {
20029         var text = [
20030             {
20031                 tag : 'span',
20032                 cls : 'roo-menu-text',
20033                 html : this.html
20034             }
20035         ];
20036         
20037         if(this.icon){
20038             text.unshift({
20039                 tag : 'i',
20040                 cls : 'fa ' + this.icon
20041             })
20042         }
20043         
20044         
20045         var cfg = {
20046             tag : 'div',
20047             cls : 'btn-group',
20048             cn : [
20049                 {
20050                     tag : 'button',
20051                     cls : 'dropdown-button btn btn-' + this.weight,
20052                     cn : text
20053                 },
20054                 {
20055                     tag : 'button',
20056                     cls : 'dropdown-toggle btn btn-' + this.weight,
20057                     cn : [
20058                         {
20059                             tag : 'span',
20060                             cls : 'caret'
20061                         }
20062                     ]
20063                 },
20064                 {
20065                     tag : 'ul',
20066                     cls : 'dropdown-menu'
20067                 }
20068             ]
20069             
20070         };
20071         
20072         if(this.pos == 'top'){
20073             cfg.cls += ' dropup';
20074         }
20075         
20076         if(this.isSubMenu){
20077             cfg = {
20078                 tag : 'ul',
20079                 cls : 'dropdown-menu'
20080             }
20081         }
20082         
20083         return cfg;
20084     },
20085     
20086     onRender : function(ct, position)
20087     {
20088         this.isSubMenu = ct.hasClass('dropdown-submenu');
20089         
20090         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20091     },
20092     
20093     initEvents : function() 
20094     {
20095         if(this.isSubMenu){
20096             return;
20097         }
20098         
20099         this.hidden = true;
20100         
20101         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20102         this.triggerEl.on('click', this.onTriggerPress, this);
20103         
20104         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20105         this.buttonEl.on('click', this.onClick, this);
20106         
20107     },
20108     
20109     list : function()
20110     {
20111         if(this.isSubMenu){
20112             return this.el;
20113         }
20114         
20115         return this.el.select('ul.dropdown-menu', true).first();
20116     },
20117     
20118     onClick : function(e)
20119     {
20120         this.fireEvent("click", this, e);
20121     },
20122     
20123     onTriggerPress  : function(e)
20124     {   
20125         if (this.isVisible()) {
20126             this.hide();
20127         } else {
20128             this.show();
20129         }
20130     },
20131     
20132     isVisible : function(){
20133         return !this.hidden;
20134     },
20135     
20136     show : function()
20137     {
20138         this.fireEvent("beforeshow", this);
20139         
20140         this.hidden = false;
20141         this.el.addClass('open');
20142         
20143         Roo.get(document).on("mouseup", this.onMouseUp, this);
20144         
20145         this.fireEvent("show", this);
20146         
20147         
20148     },
20149     
20150     hide : function()
20151     {
20152         this.fireEvent("beforehide", this);
20153         
20154         this.hidden = true;
20155         this.el.removeClass('open');
20156         
20157         Roo.get(document).un("mouseup", this.onMouseUp);
20158         
20159         this.fireEvent("hide", this);
20160     },
20161     
20162     onMouseUp : function()
20163     {
20164         this.hide();
20165     }
20166     
20167 });
20168
20169  
20170  /*
20171  * - LGPL
20172  *
20173  * menu item
20174  * 
20175  */
20176 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20177
20178 /**
20179  * @class Roo.bootstrap.menu.Item
20180  * @extends Roo.bootstrap.Component
20181  * Bootstrap MenuItem class
20182  * @cfg {Boolean} submenu (true | false) default false
20183  * @cfg {String} html text of the item
20184  * @cfg {String} href the link
20185  * @cfg {Boolean} disable (true | false) default false
20186  * @cfg {Boolean} preventDefault (true | false) default true
20187  * @cfg {String} icon Font awesome icon
20188  * @cfg {String} pos Submenu align to (left | right) default right 
20189  * 
20190  * 
20191  * @constructor
20192  * Create a new Item
20193  * @param {Object} config The config object
20194  */
20195
20196
20197 Roo.bootstrap.menu.Item = function(config){
20198     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20199     this.addEvents({
20200         /**
20201          * @event mouseover
20202          * Fires when the mouse is hovering over this menu
20203          * @param {Roo.bootstrap.menu.Item} this
20204          * @param {Roo.EventObject} e
20205          */
20206         mouseover : true,
20207         /**
20208          * @event mouseout
20209          * Fires when the mouse exits this menu
20210          * @param {Roo.bootstrap.menu.Item} this
20211          * @param {Roo.EventObject} e
20212          */
20213         mouseout : true,
20214         // raw events
20215         /**
20216          * @event click
20217          * The raw click event for the entire grid.
20218          * @param {Roo.EventObject} e
20219          */
20220         click : true
20221     });
20222 };
20223
20224 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20225     
20226     submenu : false,
20227     href : '',
20228     html : '',
20229     preventDefault: true,
20230     disable : false,
20231     icon : false,
20232     pos : 'right',
20233     
20234     getAutoCreate : function()
20235     {
20236         var text = [
20237             {
20238                 tag : 'span',
20239                 cls : 'roo-menu-item-text',
20240                 html : this.html
20241             }
20242         ];
20243         
20244         if(this.icon){
20245             text.unshift({
20246                 tag : 'i',
20247                 cls : 'fa ' + this.icon
20248             })
20249         }
20250         
20251         var cfg = {
20252             tag : 'li',
20253             cn : [
20254                 {
20255                     tag : 'a',
20256                     href : this.href || '#',
20257                     cn : text
20258                 }
20259             ]
20260         };
20261         
20262         if(this.disable){
20263             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20264         }
20265         
20266         if(this.submenu){
20267             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20268             
20269             if(this.pos == 'left'){
20270                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20271             }
20272         }
20273         
20274         return cfg;
20275     },
20276     
20277     initEvents : function() 
20278     {
20279         this.el.on('mouseover', this.onMouseOver, this);
20280         this.el.on('mouseout', this.onMouseOut, this);
20281         
20282         this.el.select('a', true).first().on('click', this.onClick, this);
20283         
20284     },
20285     
20286     onClick : function(e)
20287     {
20288         if(this.preventDefault){
20289             e.preventDefault();
20290         }
20291         
20292         this.fireEvent("click", this, e);
20293     },
20294     
20295     onMouseOver : function(e)
20296     {
20297         if(this.submenu && this.pos == 'left'){
20298             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20299         }
20300         
20301         this.fireEvent("mouseover", this, e);
20302     },
20303     
20304     onMouseOut : function(e)
20305     {
20306         this.fireEvent("mouseout", this, e);
20307     }
20308 });
20309
20310  
20311
20312  /*
20313  * - LGPL
20314  *
20315  * menu separator
20316  * 
20317  */
20318 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20319
20320 /**
20321  * @class Roo.bootstrap.menu.Separator
20322  * @extends Roo.bootstrap.Component
20323  * Bootstrap Separator class
20324  * 
20325  * @constructor
20326  * Create a new Separator
20327  * @param {Object} config The config object
20328  */
20329
20330
20331 Roo.bootstrap.menu.Separator = function(config){
20332     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20333 };
20334
20335 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20336     
20337     getAutoCreate : function(){
20338         var cfg = {
20339             tag : 'li',
20340             cls: 'divider'
20341         };
20342         
20343         return cfg;
20344     }
20345    
20346 });
20347
20348  
20349
20350