Roo/bootstrap/Link.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         
3704         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3705             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3706                 this.parent().setActiveItem(this);
3707             }
3708         } 
3709     },
3710     
3711     isActive: function () {
3712         return this.active
3713     },
3714     setActive : function(state, fire, is_was_active)
3715     {
3716         if (this.active && !state & this.navId) {
3717             this.was_active = true;
3718             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3719             if (nv) {
3720                 nv.clearWasActive(this);
3721             }
3722             
3723         }
3724         this.active = state;
3725         
3726         if (!state ) {
3727             this.el.removeClass('active');
3728         } else if (!this.el.hasClass('active')) {
3729             this.el.addClass('active');
3730         }
3731         if (fire) {
3732             this.fireEvent('changed', this, state);
3733         }
3734         
3735         // show a panel if it's registered and related..
3736         
3737         if (!this.navId || !this.tabId || !state || is_was_active) {
3738             return;
3739         }
3740         
3741         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3742         if (!tg) {
3743             return;
3744         }
3745         var pan = tg.getPanelByName(this.tabId);
3746         if (!pan) {
3747             return;
3748         }
3749         // if we can not flip to new panel - go back to old nav highlight..
3750         if (false == tg.showPanel(pan)) {
3751             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3752             if (nv) {
3753                 var onav = nv.getWasActive();
3754                 if (onav) {
3755                     onav.setActive(true, false, true);
3756                 }
3757             }
3758             
3759         }
3760         
3761         
3762         
3763     },
3764      // this should not be here...
3765     setDisabled : function(state)
3766     {
3767         this.disabled = state;
3768         if (!state ) {
3769             this.el.removeClass('disabled');
3770         } else if (!this.el.hasClass('disabled')) {
3771             this.el.addClass('disabled');
3772         }
3773         
3774     }
3775 });
3776  
3777
3778  /*
3779  * - LGPL
3780  *
3781  * sidebar item
3782  *
3783  *  li
3784  *    <span> icon </span>
3785  *    <span> text </span>
3786  *    <span>badge </span>
3787  */
3788
3789 /**
3790  * @class Roo.bootstrap.NavSidebarItem
3791  * @extends Roo.bootstrap.NavItem
3792  * Bootstrap Navbar.NavSidebarItem class
3793  * @constructor
3794  * Create a new Navbar Button
3795  * @param {Object} config The config object
3796  */
3797 Roo.bootstrap.NavSidebarItem = function(config){
3798     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3799     this.addEvents({
3800         // raw events
3801         /**
3802          * @event click
3803          * The raw click event for the entire grid.
3804          * @param {Roo.EventObject} e
3805          */
3806         "click" : true,
3807          /**
3808             * @event changed
3809             * Fires when the active item active state changes
3810             * @param {Roo.bootstrap.NavSidebarItem} this
3811             * @param {boolean} state the new state
3812              
3813          */
3814         'changed': true
3815     });
3816    
3817 };
3818
3819 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3820     
3821     
3822     getAutoCreate : function(){
3823         
3824         
3825         var a = {
3826                 tag: 'a',
3827                 href : this.href || '#',
3828                 cls: '',
3829                 html : '',
3830                 cn : []
3831         };
3832         var cfg = {
3833             tag: 'li',
3834             cls: '',
3835             cn: [ a ]
3836         }
3837         var span = {
3838             tag: 'span',
3839             html : this.html || ''
3840         }
3841         
3842         
3843         if (this.active) {
3844             cfg.cls += ' active';
3845         }
3846         
3847         // left icon..
3848         if (this.glyphicon || this.icon) {
3849             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3850             a.cn.push({ tag : 'i', cls : c }) ;
3851         }
3852         // html..
3853         a.cn.push(span);
3854         // then badge..
3855         if (this.badge !== '') {
3856             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3857         }
3858         // fi
3859         if (this.menu) {
3860             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3861             a.cls += 'dropdown-toggle treeview' ;
3862             
3863         }
3864         
3865         
3866         
3867         return cfg;
3868          
3869            
3870     }
3871    
3872      
3873  
3874 });
3875  
3876
3877  /*
3878  * - LGPL
3879  *
3880  * row
3881  * 
3882  */
3883
3884 /**
3885  * @class Roo.bootstrap.Row
3886  * @extends Roo.bootstrap.Component
3887  * Bootstrap Row class (contains columns...)
3888  * 
3889  * @constructor
3890  * Create a new Row
3891  * @param {Object} config The config object
3892  */
3893
3894 Roo.bootstrap.Row = function(config){
3895     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3896 };
3897
3898 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3899     
3900     getAutoCreate : function(){
3901        return {
3902             cls: 'row clearfix'
3903        };
3904     }
3905     
3906     
3907 });
3908
3909  
3910
3911  /*
3912  * - LGPL
3913  *
3914  * element
3915  * 
3916  */
3917
3918 /**
3919  * @class Roo.bootstrap.Element
3920  * @extends Roo.bootstrap.Component
3921  * Bootstrap Element class
3922  * @cfg {String} html contents of the element
3923  * @cfg {String} tag tag of the element
3924  * @cfg {String} cls class of the element
3925  * 
3926  * @constructor
3927  * Create a new Element
3928  * @param {Object} config The config object
3929  */
3930
3931 Roo.bootstrap.Element = function(config){
3932     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3933 };
3934
3935 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3936     
3937     tag: 'div',
3938     cls: '',
3939     html: '',
3940      
3941     
3942     getAutoCreate : function(){
3943         
3944         var cfg = {
3945             tag: this.tag,
3946             cls: this.cls,
3947             html: this.html
3948         }
3949         
3950         
3951         
3952         return cfg;
3953     }
3954    
3955 });
3956
3957  
3958
3959  /*
3960  * - LGPL
3961  *
3962  * pagination
3963  * 
3964  */
3965
3966 /**
3967  * @class Roo.bootstrap.Pagination
3968  * @extends Roo.bootstrap.Component
3969  * Bootstrap Pagination class
3970  * @cfg {String} size xs | sm | md | lg
3971  * @cfg {Boolean} inverse false | true
3972  * 
3973  * @constructor
3974  * Create a new Pagination
3975  * @param {Object} config The config object
3976  */
3977
3978 Roo.bootstrap.Pagination = function(config){
3979     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3980 };
3981
3982 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3983     
3984     cls: false,
3985     size: false,
3986     inverse: false,
3987     
3988     getAutoCreate : function(){
3989         var cfg = {
3990             tag: 'ul',
3991                 cls: 'pagination'
3992         };
3993         if (this.inverse) {
3994             cfg.cls += ' inverse';
3995         }
3996         if (this.html) {
3997             cfg.html=this.html;
3998         }
3999         if (this.cls) {
4000             cfg.cls += " " + this.cls;
4001         }
4002         return cfg;
4003     }
4004    
4005 });
4006
4007  
4008
4009  /*
4010  * - LGPL
4011  *
4012  * Pagination item
4013  * 
4014  */
4015
4016
4017 /**
4018  * @class Roo.bootstrap.PaginationItem
4019  * @extends Roo.bootstrap.Component
4020  * Bootstrap PaginationItem class
4021  * @cfg {String} html text
4022  * @cfg {String} href the link
4023  * @cfg {Boolean} preventDefault (true | false) default true
4024  * @cfg {Boolean} active (true | false) default false
4025  * 
4026  * 
4027  * @constructor
4028  * Create a new PaginationItem
4029  * @param {Object} config The config object
4030  */
4031
4032
4033 Roo.bootstrap.PaginationItem = function(config){
4034     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4035     this.addEvents({
4036         // raw events
4037         /**
4038          * @event click
4039          * The raw click event for the entire grid.
4040          * @param {Roo.EventObject} e
4041          */
4042         "click" : true
4043     });
4044 };
4045
4046 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4047     
4048     href : false,
4049     html : false,
4050     preventDefault: true,
4051     active : false,
4052     cls : false,
4053     
4054     getAutoCreate : function(){
4055         var cfg= {
4056             tag: 'li',
4057             cn: [
4058                 {
4059                     tag : 'a',
4060                     href : this.href ? this.href : '#',
4061                     html : this.html ? this.html : ''
4062                 }
4063             ]
4064         };
4065         
4066         if(this.cls){
4067             cfg.cls = this.cls;
4068         }
4069         
4070         if(this.active){
4071             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4072         }
4073         
4074         return cfg;
4075     },
4076     
4077     initEvents: function() {
4078         
4079         this.el.on('click', this.onClick, this);
4080         
4081     },
4082     onClick : function(e)
4083     {
4084         Roo.log('PaginationItem on click ');
4085         if(this.preventDefault){
4086             e.preventDefault();
4087         }
4088         
4089         this.fireEvent('click', this, e);
4090     }
4091    
4092 });
4093
4094  
4095
4096  /*
4097  * - LGPL
4098  *
4099  * slider
4100  * 
4101  */
4102
4103
4104 /**
4105  * @class Roo.bootstrap.Slider
4106  * @extends Roo.bootstrap.Component
4107  * Bootstrap Slider class
4108  *    
4109  * @constructor
4110  * Create a new Slider
4111  * @param {Object} config The config object
4112  */
4113
4114 Roo.bootstrap.Slider = function(config){
4115     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4116 };
4117
4118 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4119     
4120     getAutoCreate : function(){
4121         
4122         var cfg = {
4123             tag: 'div',
4124             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4125             cn: [
4126                 {
4127                     tag: 'a',
4128                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4129                 }
4130             ]
4131         }
4132         
4133         return cfg;
4134     }
4135    
4136 });
4137
4138  /*
4139  * Based on:
4140  * Ext JS Library 1.1.1
4141  * Copyright(c) 2006-2007, Ext JS, LLC.
4142  *
4143  * Originally Released Under LGPL - original licence link has changed is not relivant.
4144  *
4145  * Fork - LGPL
4146  * <script type="text/javascript">
4147  */
4148  
4149
4150 /**
4151  * @class Roo.grid.ColumnModel
4152  * @extends Roo.util.Observable
4153  * This is the default implementation of a ColumnModel used by the Grid. It defines
4154  * the columns in the grid.
4155  * <br>Usage:<br>
4156  <pre><code>
4157  var colModel = new Roo.grid.ColumnModel([
4158         {header: "Ticker", width: 60, sortable: true, locked: true},
4159         {header: "Company Name", width: 150, sortable: true},
4160         {header: "Market Cap.", width: 100, sortable: true},
4161         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4162         {header: "Employees", width: 100, sortable: true, resizable: false}
4163  ]);
4164  </code></pre>
4165  * <p>
4166  
4167  * The config options listed for this class are options which may appear in each
4168  * individual column definition.
4169  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4170  * @constructor
4171  * @param {Object} config An Array of column config objects. See this class's
4172  * config objects for details.
4173 */
4174 Roo.grid.ColumnModel = function(config){
4175         /**
4176      * The config passed into the constructor
4177      */
4178     this.config = config;
4179     this.lookup = {};
4180
4181     // if no id, create one
4182     // if the column does not have a dataIndex mapping,
4183     // map it to the order it is in the config
4184     for(var i = 0, len = config.length; i < len; i++){
4185         var c = config[i];
4186         if(typeof c.dataIndex == "undefined"){
4187             c.dataIndex = i;
4188         }
4189         if(typeof c.renderer == "string"){
4190             c.renderer = Roo.util.Format[c.renderer];
4191         }
4192         if(typeof c.id == "undefined"){
4193             c.id = Roo.id();
4194         }
4195         if(c.editor && c.editor.xtype){
4196             c.editor  = Roo.factory(c.editor, Roo.grid);
4197         }
4198         if(c.editor && c.editor.isFormField){
4199             c.editor = new Roo.grid.GridEditor(c.editor);
4200         }
4201         this.lookup[c.id] = c;
4202     }
4203
4204     /**
4205      * The width of columns which have no width specified (defaults to 100)
4206      * @type Number
4207      */
4208     this.defaultWidth = 100;
4209
4210     /**
4211      * Default sortable of columns which have no sortable specified (defaults to false)
4212      * @type Boolean
4213      */
4214     this.defaultSortable = false;
4215
4216     this.addEvents({
4217         /**
4218              * @event widthchange
4219              * Fires when the width of a column changes.
4220              * @param {ColumnModel} this
4221              * @param {Number} columnIndex The column index
4222              * @param {Number} newWidth The new width
4223              */
4224             "widthchange": true,
4225         /**
4226              * @event headerchange
4227              * Fires when the text of a header changes.
4228              * @param {ColumnModel} this
4229              * @param {Number} columnIndex The column index
4230              * @param {Number} newText The new header text
4231              */
4232             "headerchange": true,
4233         /**
4234              * @event hiddenchange
4235              * Fires when a column is hidden or "unhidden".
4236              * @param {ColumnModel} this
4237              * @param {Number} columnIndex The column index
4238              * @param {Boolean} hidden true if hidden, false otherwise
4239              */
4240             "hiddenchange": true,
4241             /**
4242          * @event columnmoved
4243          * Fires when a column is moved.
4244          * @param {ColumnModel} this
4245          * @param {Number} oldIndex
4246          * @param {Number} newIndex
4247          */
4248         "columnmoved" : true,
4249         /**
4250          * @event columlockchange
4251          * Fires when a column's locked state is changed
4252          * @param {ColumnModel} this
4253          * @param {Number} colIndex
4254          * @param {Boolean} locked true if locked
4255          */
4256         "columnlockchange" : true
4257     });
4258     Roo.grid.ColumnModel.superclass.constructor.call(this);
4259 };
4260 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4261     /**
4262      * @cfg {String} header The header text to display in the Grid view.
4263      */
4264     /**
4265      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4266      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4267      * specified, the column's index is used as an index into the Record's data Array.
4268      */
4269     /**
4270      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4271      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4272      */
4273     /**
4274      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4275      * Defaults to the value of the {@link #defaultSortable} property.
4276      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4277      */
4278     /**
4279      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4280      */
4281     /**
4282      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4283      */
4284     /**
4285      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4286      */
4287     /**
4288      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4289      */
4290     /**
4291      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4292      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4293      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4294      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4295      */
4296        /**
4297      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4298      */
4299     /**
4300      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4301      */
4302
4303     /**
4304      * Returns the id of the column at the specified index.
4305      * @param {Number} index The column index
4306      * @return {String} the id
4307      */
4308     getColumnId : function(index){
4309         return this.config[index].id;
4310     },
4311
4312     /**
4313      * Returns the column for a specified id.
4314      * @param {String} id The column id
4315      * @return {Object} the column
4316      */
4317     getColumnById : function(id){
4318         return this.lookup[id];
4319     },
4320
4321     
4322     /**
4323      * Returns the column for a specified dataIndex.
4324      * @param {String} dataIndex The column dataIndex
4325      * @return {Object|Boolean} the column or false if not found
4326      */
4327     getColumnByDataIndex: function(dataIndex){
4328         var index = this.findColumnIndex(dataIndex);
4329         return index > -1 ? this.config[index] : false;
4330     },
4331     
4332     /**
4333      * Returns the index for a specified column id.
4334      * @param {String} id The column id
4335      * @return {Number} the index, or -1 if not found
4336      */
4337     getIndexById : function(id){
4338         for(var i = 0, len = this.config.length; i < len; i++){
4339             if(this.config[i].id == id){
4340                 return i;
4341             }
4342         }
4343         return -1;
4344     },
4345     
4346     /**
4347      * Returns the index for a specified column dataIndex.
4348      * @param {String} dataIndex The column dataIndex
4349      * @return {Number} the index, or -1 if not found
4350      */
4351     
4352     findColumnIndex : function(dataIndex){
4353         for(var i = 0, len = this.config.length; i < len; i++){
4354             if(this.config[i].dataIndex == dataIndex){
4355                 return i;
4356             }
4357         }
4358         return -1;
4359     },
4360     
4361     
4362     moveColumn : function(oldIndex, newIndex){
4363         var c = this.config[oldIndex];
4364         this.config.splice(oldIndex, 1);
4365         this.config.splice(newIndex, 0, c);
4366         this.dataMap = null;
4367         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4368     },
4369
4370     isLocked : function(colIndex){
4371         return this.config[colIndex].locked === true;
4372     },
4373
4374     setLocked : function(colIndex, value, suppressEvent){
4375         if(this.isLocked(colIndex) == value){
4376             return;
4377         }
4378         this.config[colIndex].locked = value;
4379         if(!suppressEvent){
4380             this.fireEvent("columnlockchange", this, colIndex, value);
4381         }
4382     },
4383
4384     getTotalLockedWidth : function(){
4385         var totalWidth = 0;
4386         for(var i = 0; i < this.config.length; i++){
4387             if(this.isLocked(i) && !this.isHidden(i)){
4388                 this.totalWidth += this.getColumnWidth(i);
4389             }
4390         }
4391         return totalWidth;
4392     },
4393
4394     getLockedCount : function(){
4395         for(var i = 0, len = this.config.length; i < len; i++){
4396             if(!this.isLocked(i)){
4397                 return i;
4398             }
4399         }
4400     },
4401
4402     /**
4403      * Returns the number of columns.
4404      * @return {Number}
4405      */
4406     getColumnCount : function(visibleOnly){
4407         if(visibleOnly === true){
4408             var c = 0;
4409             for(var i = 0, len = this.config.length; i < len; i++){
4410                 if(!this.isHidden(i)){
4411                     c++;
4412                 }
4413             }
4414             return c;
4415         }
4416         return this.config.length;
4417     },
4418
4419     /**
4420      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4421      * @param {Function} fn
4422      * @param {Object} scope (optional)
4423      * @return {Array} result
4424      */
4425     getColumnsBy : function(fn, scope){
4426         var r = [];
4427         for(var i = 0, len = this.config.length; i < len; i++){
4428             var c = this.config[i];
4429             if(fn.call(scope||this, c, i) === true){
4430                 r[r.length] = c;
4431             }
4432         }
4433         return r;
4434     },
4435
4436     /**
4437      * Returns true if the specified column is sortable.
4438      * @param {Number} col The column index
4439      * @return {Boolean}
4440      */
4441     isSortable : function(col){
4442         if(typeof this.config[col].sortable == "undefined"){
4443             return this.defaultSortable;
4444         }
4445         return this.config[col].sortable;
4446     },
4447
4448     /**
4449      * Returns the rendering (formatting) function defined for the column.
4450      * @param {Number} col The column index.
4451      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4452      */
4453     getRenderer : function(col){
4454         if(!this.config[col].renderer){
4455             return Roo.grid.ColumnModel.defaultRenderer;
4456         }
4457         return this.config[col].renderer;
4458     },
4459
4460     /**
4461      * Sets the rendering (formatting) function for a column.
4462      * @param {Number} col The column index
4463      * @param {Function} fn The function to use to process the cell's raw data
4464      * to return HTML markup for the grid view. The render function is called with
4465      * the following parameters:<ul>
4466      * <li>Data value.</li>
4467      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4468      * <li>css A CSS style string to apply to the table cell.</li>
4469      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4470      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4471      * <li>Row index</li>
4472      * <li>Column index</li>
4473      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4474      */
4475     setRenderer : function(col, fn){
4476         this.config[col].renderer = fn;
4477     },
4478
4479     /**
4480      * Returns the width for the specified column.
4481      * @param {Number} col The column index
4482      * @return {Number}
4483      */
4484     getColumnWidth : function(col){
4485         return this.config[col].width * 1 || this.defaultWidth;
4486     },
4487
4488     /**
4489      * Sets the width for a column.
4490      * @param {Number} col The column index
4491      * @param {Number} width The new width
4492      */
4493     setColumnWidth : function(col, width, suppressEvent){
4494         this.config[col].width = width;
4495         this.totalWidth = null;
4496         if(!suppressEvent){
4497              this.fireEvent("widthchange", this, col, width);
4498         }
4499     },
4500
4501     /**
4502      * Returns the total width of all columns.
4503      * @param {Boolean} includeHidden True to include hidden column widths
4504      * @return {Number}
4505      */
4506     getTotalWidth : function(includeHidden){
4507         if(!this.totalWidth){
4508             this.totalWidth = 0;
4509             for(var i = 0, len = this.config.length; i < len; i++){
4510                 if(includeHidden || !this.isHidden(i)){
4511                     this.totalWidth += this.getColumnWidth(i);
4512                 }
4513             }
4514         }
4515         return this.totalWidth;
4516     },
4517
4518     /**
4519      * Returns the header for the specified column.
4520      * @param {Number} col The column index
4521      * @return {String}
4522      */
4523     getColumnHeader : function(col){
4524         return this.config[col].header;
4525     },
4526
4527     /**
4528      * Sets the header for a column.
4529      * @param {Number} col The column index
4530      * @param {String} header The new header
4531      */
4532     setColumnHeader : function(col, header){
4533         this.config[col].header = header;
4534         this.fireEvent("headerchange", this, col, header);
4535     },
4536
4537     /**
4538      * Returns the tooltip for the specified column.
4539      * @param {Number} col The column index
4540      * @return {String}
4541      */
4542     getColumnTooltip : function(col){
4543             return this.config[col].tooltip;
4544     },
4545     /**
4546      * Sets the tooltip for a column.
4547      * @param {Number} col The column index
4548      * @param {String} tooltip The new tooltip
4549      */
4550     setColumnTooltip : function(col, tooltip){
4551             this.config[col].tooltip = tooltip;
4552     },
4553
4554     /**
4555      * Returns the dataIndex for the specified column.
4556      * @param {Number} col The column index
4557      * @return {Number}
4558      */
4559     getDataIndex : function(col){
4560         return this.config[col].dataIndex;
4561     },
4562
4563     /**
4564      * Sets the dataIndex for a column.
4565      * @param {Number} col The column index
4566      * @param {Number} dataIndex The new dataIndex
4567      */
4568     setDataIndex : function(col, dataIndex){
4569         this.config[col].dataIndex = dataIndex;
4570     },
4571
4572     
4573     
4574     /**
4575      * Returns true if the cell is editable.
4576      * @param {Number} colIndex The column index
4577      * @param {Number} rowIndex The row index
4578      * @return {Boolean}
4579      */
4580     isCellEditable : function(colIndex, rowIndex){
4581         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4582     },
4583
4584     /**
4585      * Returns the editor defined for the cell/column.
4586      * return false or null to disable editing.
4587      * @param {Number} colIndex The column index
4588      * @param {Number} rowIndex The row index
4589      * @return {Object}
4590      */
4591     getCellEditor : function(colIndex, rowIndex){
4592         return this.config[colIndex].editor;
4593     },
4594
4595     /**
4596      * Sets if a column is editable.
4597      * @param {Number} col The column index
4598      * @param {Boolean} editable True if the column is editable
4599      */
4600     setEditable : function(col, editable){
4601         this.config[col].editable = editable;
4602     },
4603
4604
4605     /**
4606      * Returns true if the column is hidden.
4607      * @param {Number} colIndex The column index
4608      * @return {Boolean}
4609      */
4610     isHidden : function(colIndex){
4611         return this.config[colIndex].hidden;
4612     },
4613
4614
4615     /**
4616      * Returns true if the column width cannot be changed
4617      */
4618     isFixed : function(colIndex){
4619         return this.config[colIndex].fixed;
4620     },
4621
4622     /**
4623      * Returns true if the column can be resized
4624      * @return {Boolean}
4625      */
4626     isResizable : function(colIndex){
4627         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4628     },
4629     /**
4630      * Sets if a column is hidden.
4631      * @param {Number} colIndex The column index
4632      * @param {Boolean} hidden True if the column is hidden
4633      */
4634     setHidden : function(colIndex, hidden){
4635         this.config[colIndex].hidden = hidden;
4636         this.totalWidth = null;
4637         this.fireEvent("hiddenchange", this, colIndex, hidden);
4638     },
4639
4640     /**
4641      * Sets the editor for a column.
4642      * @param {Number} col The column index
4643      * @param {Object} editor The editor object
4644      */
4645     setEditor : function(col, editor){
4646         this.config[col].editor = editor;
4647     }
4648 });
4649
4650 Roo.grid.ColumnModel.defaultRenderer = function(value){
4651         if(typeof value == "string" && value.length < 1){
4652             return "&#160;";
4653         }
4654         return value;
4655 };
4656
4657 // Alias for backwards compatibility
4658 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4659 /*
4660  * Based on:
4661  * Ext JS Library 1.1.1
4662  * Copyright(c) 2006-2007, Ext JS, LLC.
4663  *
4664  * Originally Released Under LGPL - original licence link has changed is not relivant.
4665  *
4666  * Fork - LGPL
4667  * <script type="text/javascript">
4668  */
4669  
4670 /**
4671  * @class Roo.LoadMask
4672  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4673  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4674  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4675  * element's UpdateManager load indicator and will be destroyed after the initial load.
4676  * @constructor
4677  * Create a new LoadMask
4678  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4679  * @param {Object} config The config object
4680  */
4681 Roo.LoadMask = function(el, config){
4682     this.el = Roo.get(el);
4683     Roo.apply(this, config);
4684     if(this.store){
4685         this.store.on('beforeload', this.onBeforeLoad, this);
4686         this.store.on('load', this.onLoad, this);
4687         this.store.on('loadexception', this.onLoadException, this);
4688         this.removeMask = false;
4689     }else{
4690         var um = this.el.getUpdateManager();
4691         um.showLoadIndicator = false; // disable the default indicator
4692         um.on('beforeupdate', this.onBeforeLoad, this);
4693         um.on('update', this.onLoad, this);
4694         um.on('failure', this.onLoad, this);
4695         this.removeMask = true;
4696     }
4697 };
4698
4699 Roo.LoadMask.prototype = {
4700     /**
4701      * @cfg {Boolean} removeMask
4702      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4703      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4704      */
4705     /**
4706      * @cfg {String} msg
4707      * The text to display in a centered loading message box (defaults to 'Loading...')
4708      */
4709     msg : 'Loading...',
4710     /**
4711      * @cfg {String} msgCls
4712      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4713      */
4714     msgCls : 'x-mask-loading',
4715
4716     /**
4717      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4718      * @type Boolean
4719      */
4720     disabled: false,
4721
4722     /**
4723      * Disables the mask to prevent it from being displayed
4724      */
4725     disable : function(){
4726        this.disabled = true;
4727     },
4728
4729     /**
4730      * Enables the mask so that it can be displayed
4731      */
4732     enable : function(){
4733         this.disabled = false;
4734     },
4735     
4736     onLoadException : function()
4737     {
4738         Roo.log(arguments);
4739         
4740         if (typeof(arguments[3]) != 'undefined') {
4741             Roo.MessageBox.alert("Error loading",arguments[3]);
4742         } 
4743         /*
4744         try {
4745             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4746                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4747             }   
4748         } catch(e) {
4749             
4750         }
4751         */
4752     
4753         
4754         
4755         this.el.unmask(this.removeMask);
4756     },
4757     // private
4758     onLoad : function()
4759     {
4760         this.el.unmask(this.removeMask);
4761     },
4762
4763     // private
4764     onBeforeLoad : function(){
4765         if(!this.disabled){
4766             this.el.mask(this.msg, this.msgCls);
4767         }
4768     },
4769
4770     // private
4771     destroy : function(){
4772         if(this.store){
4773             this.store.un('beforeload', this.onBeforeLoad, this);
4774             this.store.un('load', this.onLoad, this);
4775             this.store.un('loadexception', this.onLoadException, this);
4776         }else{
4777             var um = this.el.getUpdateManager();
4778             um.un('beforeupdate', this.onBeforeLoad, this);
4779             um.un('update', this.onLoad, this);
4780             um.un('failure', this.onLoad, this);
4781         }
4782     }
4783 };/*
4784  * - LGPL
4785  *
4786  * table
4787  * 
4788  */
4789
4790 /**
4791  * @class Roo.bootstrap.Table
4792  * @extends Roo.bootstrap.Component
4793  * Bootstrap Table class
4794  * @cfg {String} cls table class
4795  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4796  * @cfg {String} bgcolor Specifies the background color for a table
4797  * @cfg {Number} border Specifies whether the table cells should have borders or not
4798  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4799  * @cfg {Number} cellspacing Specifies the space between cells
4800  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4801  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4802  * @cfg {String} sortable Specifies that the table should be sortable
4803  * @cfg {String} summary Specifies a summary of the content of a table
4804  * @cfg {Number} width Specifies the width of a table
4805  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4806  * 
4807  * @cfg {boolean} striped Should the rows be alternative striped
4808  * @cfg {boolean} bordered Add borders to the table
4809  * @cfg {boolean} hover Add hover highlighting
4810  * @cfg {boolean} condensed Format condensed
4811  * @cfg {boolean} responsive Format condensed
4812  * @cfg {Boolean} loadMask (true|false) default false
4813  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4814  * @cfg {Boolean} thead (true|false) generate thead, default true
4815  * @cfg {Boolean} RowSelection (true|false) default false
4816  * @cfg {Boolean} CellSelection (true|false) default false
4817  *
4818  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4819  
4820  * 
4821  * @constructor
4822  * Create a new Table
4823  * @param {Object} config The config object
4824  */
4825
4826 Roo.bootstrap.Table = function(config){
4827     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4828     
4829     if (this.sm) {
4830         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4831         this.sm = this.selModel;
4832         this.sm.xmodule = this.xmodule || false;
4833     }
4834     if (this.cm && typeof(this.cm.config) == 'undefined') {
4835         this.colModel = new Roo.grid.ColumnModel(this.cm);
4836         this.cm = this.colModel;
4837         this.cm.xmodule = this.xmodule || false;
4838     }
4839     if (this.store) {
4840         this.store= Roo.factory(this.store, Roo.data);
4841         this.ds = this.store;
4842         this.ds.xmodule = this.xmodule || false;
4843          
4844     }
4845     if (this.footer && this.store) {
4846         this.footer.dataSource = this.ds;
4847         this.footer = Roo.factory(this.footer);
4848     }
4849     
4850     /** @private */
4851     this.addEvents({
4852         /**
4853          * @event cellclick
4854          * Fires when a cell is clicked
4855          * @param {Roo.bootstrap.Table} this
4856          * @param {Roo.Element} el
4857          * @param {Number} rowIndex
4858          * @param {Number} columnIndex
4859          * @param {Roo.EventObject} e
4860          */
4861         "cellclick" : true,
4862         /**
4863          * @event celldblclick
4864          * Fires when a cell is double clicked
4865          * @param {Roo.bootstrap.Table} this
4866          * @param {Roo.Element} el
4867          * @param {Number} rowIndex
4868          * @param {Number} columnIndex
4869          * @param {Roo.EventObject} e
4870          */
4871         "celldblclick" : true,
4872         /**
4873          * @event rowclick
4874          * Fires when a row is clicked
4875          * @param {Roo.bootstrap.Table} this
4876          * @param {Roo.Element} el
4877          * @param {Number} rowIndex
4878          * @param {Roo.EventObject} e
4879          */
4880         "rowclick" : true,
4881         /**
4882          * @event rowdblclick
4883          * Fires when a row is double clicked
4884          * @param {Roo.bootstrap.Table} this
4885          * @param {Roo.Element} el
4886          * @param {Number} rowIndex
4887          * @param {Roo.EventObject} e
4888          */
4889         "rowdblclick" : true,
4890         /**
4891          * @event mouseover
4892          * Fires when a mouseover occur
4893          * @param {Roo.bootstrap.Table} this
4894          * @param {Roo.Element} el
4895          * @param {Number} rowIndex
4896          * @param {Number} columnIndex
4897          * @param {Roo.EventObject} e
4898          */
4899         "mouseover" : true,
4900         /**
4901          * @event mouseout
4902          * Fires when a mouseout occur
4903          * @param {Roo.bootstrap.Table} this
4904          * @param {Roo.Element} el
4905          * @param {Number} rowIndex
4906          * @param {Number} columnIndex
4907          * @param {Roo.EventObject} e
4908          */
4909         "mouseout" : true,
4910         /**
4911          * @event rowclass
4912          * Fires when a row is rendered, so you can change add a style to it.
4913          * @param {Roo.bootstrap.Table} this
4914          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4915          */
4916         'rowclass' : true
4917         
4918     });
4919 };
4920
4921 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4922     
4923     cls: false,
4924     align: false,
4925     bgcolor: false,
4926     border: false,
4927     cellpadding: false,
4928     cellspacing: false,
4929     frame: false,
4930     rules: false,
4931     sortable: false,
4932     summary: false,
4933     width: false,
4934     striped : false,
4935     bordered: false,
4936     hover:  false,
4937     condensed : false,
4938     responsive : false,
4939     sm : false,
4940     cm : false,
4941     store : false,
4942     loadMask : false,
4943     tfoot : true,
4944     thead : true,
4945     RowSelection : false,
4946     CellSelection : false,
4947     layout : false,
4948     
4949     // Roo.Element - the tbody
4950     mainBody: false, 
4951     
4952     getAutoCreate : function(){
4953         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4954         
4955         cfg = {
4956             tag: 'table',
4957             cls : 'table',
4958             cn : []
4959         }
4960             
4961         if (this.striped) {
4962             cfg.cls += ' table-striped';
4963         }
4964         
4965         if (this.hover) {
4966             cfg.cls += ' table-hover';
4967         }
4968         if (this.bordered) {
4969             cfg.cls += ' table-bordered';
4970         }
4971         if (this.condensed) {
4972             cfg.cls += ' table-condensed';
4973         }
4974         if (this.responsive) {
4975             cfg.cls += ' table-responsive';
4976         }
4977         
4978         if (this.cls) {
4979             cfg.cls+=  ' ' +this.cls;
4980         }
4981         
4982         // this lot should be simplifed...
4983         
4984         if (this.align) {
4985             cfg.align=this.align;
4986         }
4987         if (this.bgcolor) {
4988             cfg.bgcolor=this.bgcolor;
4989         }
4990         if (this.border) {
4991             cfg.border=this.border;
4992         }
4993         if (this.cellpadding) {
4994             cfg.cellpadding=this.cellpadding;
4995         }
4996         if (this.cellspacing) {
4997             cfg.cellspacing=this.cellspacing;
4998         }
4999         if (this.frame) {
5000             cfg.frame=this.frame;
5001         }
5002         if (this.rules) {
5003             cfg.rules=this.rules;
5004         }
5005         if (this.sortable) {
5006             cfg.sortable=this.sortable;
5007         }
5008         if (this.summary) {
5009             cfg.summary=this.summary;
5010         }
5011         if (this.width) {
5012             cfg.width=this.width;
5013         }
5014         if (this.layout) {
5015             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5016         }
5017         
5018         if(this.store || this.cm){
5019             if(this.thead){
5020                 cfg.cn.push(this.renderHeader());
5021             }
5022             
5023             cfg.cn.push(this.renderBody());
5024             
5025             if(this.tfoot){
5026                 cfg.cn.push(this.renderFooter());
5027             }
5028             
5029             cfg.cls+=  ' TableGrid';
5030         }
5031         
5032         return { cn : [ cfg ] };
5033     },
5034     
5035     initEvents : function()
5036     {   
5037         if(!this.store || !this.cm){
5038             return;
5039         }
5040         
5041         //Roo.log('initEvents with ds!!!!');
5042         
5043         this.mainBody = this.el.select('tbody', true).first();
5044         
5045         
5046         var _this = this;
5047         
5048         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5049             e.on('click', _this.sort, _this);
5050         });
5051         
5052         this.el.on("click", this.onClick, this);
5053         this.el.on("dblclick", this.onDblClick, this);
5054         
5055         this.parent().el.setStyle('position', 'relative');
5056         if (this.footer) {
5057             this.footer.parentId = this.id;
5058             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5059         }
5060         
5061         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5062         
5063         this.store.on('load', this.onLoad, this);
5064         this.store.on('beforeload', this.onBeforeLoad, this);
5065         this.store.on('update', this.onUpdate, this);
5066         
5067     },
5068     
5069     onMouseover : function(e, el)
5070     {
5071         var cell = Roo.get(el);
5072         
5073         if(!cell){
5074             return;
5075         }
5076         
5077         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5078             cell = cell.findParent('td', false, true);
5079         }
5080         
5081         var row = cell.findParent('tr', false, true);
5082         var cellIndex = cell.dom.cellIndex;
5083         var rowIndex = row.dom.rowIndex - 1; // start from 0
5084         
5085         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5086         
5087     },
5088     
5089     onMouseout : function(e, el)
5090     {
5091         var cell = Roo.get(el);
5092         
5093         if(!cell){
5094             return;
5095         }
5096         
5097         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5098             cell = cell.findParent('td', false, true);
5099         }
5100         
5101         var row = cell.findParent('tr', false, true);
5102         var cellIndex = cell.dom.cellIndex;
5103         var rowIndex = row.dom.rowIndex - 1; // start from 0
5104         
5105         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5106         
5107     },
5108     
5109     onClick : function(e, el)
5110     {
5111         var cell = Roo.get(el);
5112         
5113         if(!cell || (!this.CellSelection && !this.RowSelection)){
5114             return;
5115         }
5116         
5117         
5118         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5119             cell = cell.findParent('td', false, true);
5120         }
5121         
5122         var row = cell.findParent('tr', false, true);
5123         var cellIndex = cell.dom.cellIndex;
5124         var rowIndex = row.dom.rowIndex - 1;
5125         
5126         if(this.CellSelection){
5127             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5128         }
5129         
5130         if(this.RowSelection){
5131             this.fireEvent('rowclick', this, row, rowIndex, e);
5132         }
5133         
5134         
5135     },
5136     
5137     onDblClick : function(e,el)
5138     {
5139         var cell = Roo.get(el);
5140         
5141         if(!cell || (!this.CellSelection && !this.RowSelection)){
5142             return;
5143         }
5144         
5145         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5146             cell = cell.findParent('td', false, true);
5147         }
5148         
5149         var row = cell.findParent('tr', false, true);
5150         var cellIndex = cell.dom.cellIndex;
5151         var rowIndex = row.dom.rowIndex - 1;
5152         
5153         if(this.CellSelection){
5154             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5155         }
5156         
5157         if(this.RowSelection){
5158             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5159         }
5160     },
5161     
5162     sort : function(e,el)
5163     {
5164         var col = Roo.get(el)
5165         
5166         if(!col.hasClass('sortable')){
5167             return;
5168         }
5169         
5170         var sort = col.attr('sort');
5171         var dir = 'ASC';
5172         
5173         if(col.hasClass('glyphicon-arrow-up')){
5174             dir = 'DESC';
5175         }
5176         
5177         this.store.sortInfo = {field : sort, direction : dir};
5178         
5179         if (this.footer) {
5180             Roo.log("calling footer first");
5181             this.footer.onClick('first');
5182         } else {
5183         
5184             this.store.load({ params : { start : 0 } });
5185         }
5186     },
5187     
5188     renderHeader : function()
5189     {
5190         var header = {
5191             tag: 'thead',
5192             cn : []
5193         };
5194         
5195         var cm = this.cm;
5196         
5197         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5198             
5199             var config = cm.config[i];
5200                     
5201             var c = {
5202                 tag: 'th',
5203                 style : '',
5204                 html: cm.getColumnHeader(i)
5205             };
5206             
5207             if(typeof(config.hidden) != 'undefined' && config.hidden){
5208                 c.style += ' display:none;';
5209             }
5210             
5211             if(typeof(config.dataIndex) != 'undefined'){
5212                 c.sort = config.dataIndex;
5213             }
5214             
5215             if(typeof(config.sortable) != 'undefined' && config.sortable){
5216                 c.cls = 'sortable';
5217             }
5218             
5219             if(typeof(config.align) != 'undefined' && config.align.length){
5220                 c.style += ' text-align:' + config.align + ';';
5221             }
5222             
5223             if(typeof(config.width) != 'undefined'){
5224                 c.style += ' width:' + config.width + 'px;';
5225             }
5226             
5227             header.cn.push(c)
5228         }
5229         
5230         return header;
5231     },
5232     
5233     renderBody : function()
5234     {
5235         var body = {
5236             tag: 'tbody',
5237             cn : [
5238                 {
5239                     tag: 'tr',
5240                     cn : [
5241                         {
5242                             tag : 'td',
5243                             colspan :  this.cm.getColumnCount()
5244                         }
5245                     ]
5246                 }
5247             ]
5248         };
5249         
5250         return body;
5251     },
5252     
5253     renderFooter : function()
5254     {
5255         var footer = {
5256             tag: 'tfoot',
5257             cn : [
5258                 {
5259                     tag: 'tr',
5260                     cn : [
5261                         {
5262                             tag : 'td',
5263                             colspan :  this.cm.getColumnCount()
5264                         }
5265                     ]
5266                 }
5267             ]
5268         };
5269         
5270         return footer;
5271     },
5272     
5273     
5274     
5275     onLoad : function()
5276     {
5277         Roo.log('ds onload');
5278         this.clear();
5279         
5280         var _this = this;
5281         var cm = this.cm;
5282         var ds = this.store;
5283         
5284         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5285             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5286             
5287             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5288                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5289             }
5290             
5291             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5292                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5293             }
5294         });
5295         
5296         var tbody =  this.mainBody;
5297               
5298         if(ds.getCount() > 0){
5299             ds.data.each(function(d,rowIndex){
5300                 var row =  this.renderRow(cm, ds, rowIndex);
5301                 
5302                 tbody.createChild(row);
5303                 
5304                 var _this = this;
5305                 
5306                 if(row.cellObjects.length){
5307                     Roo.each(row.cellObjects, function(r){
5308                         _this.renderCellObject(r);
5309                     })
5310                 }
5311                 
5312             }, this);
5313         }
5314         
5315         Roo.each(this.el.select('tbody td', true).elements, function(e){
5316             e.on('mouseover', _this.onMouseover, _this);
5317         });
5318         
5319         Roo.each(this.el.select('tbody td', true).elements, function(e){
5320             e.on('mouseout', _this.onMouseout, _this);
5321         });
5322
5323         //if(this.loadMask){
5324         //    this.maskEl.hide();
5325         //}
5326     },
5327     
5328     
5329     onUpdate : function(ds,record)
5330     {
5331         this.refreshRow(record);
5332     },
5333     onRemove : function(ds, record, index, isUpdate){
5334         if(isUpdate !== true){
5335             this.fireEvent("beforerowremoved", this, index, record);
5336         }
5337         var bt = this.mainBody.dom;
5338         if(bt.rows[index]){
5339             bt.removeChild(bt.rows[index]);
5340         }
5341         
5342         if(isUpdate !== true){
5343             //this.stripeRows(index);
5344             //this.syncRowHeights(index, index);
5345             //this.layout();
5346             this.fireEvent("rowremoved", this, index, record);
5347         }
5348     },
5349     
5350     
5351     refreshRow : function(record){
5352         var ds = this.store, index;
5353         if(typeof record == 'number'){
5354             index = record;
5355             record = ds.getAt(index);
5356         }else{
5357             index = ds.indexOf(record);
5358         }
5359         this.insertRow(ds, index, true);
5360         this.onRemove(ds, record, index+1, true);
5361         //this.syncRowHeights(index, index);
5362         //this.layout();
5363         this.fireEvent("rowupdated", this, index, record);
5364     },
5365     
5366     insertRow : function(dm, rowIndex, isUpdate){
5367         
5368         if(!isUpdate){
5369             this.fireEvent("beforerowsinserted", this, rowIndex);
5370         }
5371             //var s = this.getScrollState();
5372         var row = this.renderRow(this.cm, this.store, rowIndex);
5373         // insert before rowIndex..
5374         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5375         
5376         var _this = this;
5377                 
5378         if(row.cellObjects.length){
5379             Roo.each(row.cellObjects, function(r){
5380                 _this.renderCellObject(r);
5381             })
5382         }
5383             
5384         if(!isUpdate){
5385             this.fireEvent("rowsinserted", this, rowIndex);
5386             //this.syncRowHeights(firstRow, lastRow);
5387             //this.stripeRows(firstRow);
5388             //this.layout();
5389         }
5390         
5391     },
5392     
5393     
5394     getRowDom : function(rowIndex)
5395     {
5396         // not sure if I need to check this.. but let's do it anyway..
5397         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5398                 this.mainBody.dom.rows[rowIndex] : false
5399     },
5400     // returns the object tree for a tr..
5401   
5402     
5403     renderRow : function(cm, ds, rowIndex) {
5404         
5405         var d = ds.getAt(rowIndex);
5406         
5407         var row = {
5408             tag : 'tr',
5409             cn : []
5410         };
5411             
5412         var cellObjects = [];
5413         
5414         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5415             var config = cm.config[i];
5416             
5417             var renderer = cm.getRenderer(i);
5418             var value = '';
5419             var id = false;
5420             
5421             if(typeof(renderer) !== 'undefined'){
5422                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5423             }
5424             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5425             // and are rendered into the cells after the row is rendered - using the id for the element.
5426             
5427             if(typeof(value) === 'object'){
5428                 id = Roo.id();
5429                 cellObjects.push({
5430                     container : id,
5431                     cfg : value 
5432                 })
5433             }
5434             
5435             var rowcfg = {
5436                 record: d,
5437                 rowIndex : rowIndex,
5438                 colIndex : i,
5439                 rowClass : ''
5440             }
5441
5442             this.fireEvent('rowclass', this, rowcfg);
5443             
5444             var td = {
5445                 tag: 'td',
5446                 cls : rowcfg.rowClass,
5447                 style: '',
5448                 html: (typeof(value) === 'object') ? '' : value
5449             };
5450             
5451             if (id) {
5452                 td.id = id;
5453             }
5454             
5455             if(typeof(config.hidden) != 'undefined' && config.hidden){
5456                 td.style += ' display:none;';
5457             }
5458             
5459             if(typeof(config.align) != 'undefined' && config.align.length){
5460                 td.style += ' text-align:' + config.align + ';';
5461             }
5462             
5463             if(typeof(config.width) != 'undefined'){
5464                 td.style += ' width:' +  config.width + 'px;';
5465             }
5466              
5467             row.cn.push(td);
5468            
5469         }
5470         
5471         row.cellObjects = cellObjects;
5472         
5473         return row;
5474           
5475     },
5476     
5477     
5478     
5479     onBeforeLoad : function()
5480     {
5481         //Roo.log('ds onBeforeLoad');
5482         
5483         //this.clear();
5484         
5485         //if(this.loadMask){
5486         //    this.maskEl.show();
5487         //}
5488     },
5489     
5490     clear : function()
5491     {
5492         this.el.select('tbody', true).first().dom.innerHTML = '';
5493     },
5494     
5495     getSelectionModel : function(){
5496         if(!this.selModel){
5497             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5498         }
5499         return this.selModel;
5500     },
5501     /*
5502      * Render the Roo.bootstrap object from renderder
5503      */
5504     renderCellObject : function(r)
5505     {
5506         var _this = this;
5507         
5508         var t = r.cfg.render(r.container);
5509         
5510         if(r.cfg.cn){
5511             Roo.each(r.cfg.cn, function(c){
5512                 var child = {
5513                     container: t.getChildContainer(),
5514                     cfg: c
5515                 }
5516                 _this.renderCellObject(child);
5517             })
5518         }
5519     }
5520    
5521 });
5522
5523  
5524
5525  /*
5526  * - LGPL
5527  *
5528  * table cell
5529  * 
5530  */
5531
5532 /**
5533  * @class Roo.bootstrap.TableCell
5534  * @extends Roo.bootstrap.Component
5535  * Bootstrap TableCell class
5536  * @cfg {String} html cell contain text
5537  * @cfg {String} cls cell class
5538  * @cfg {String} tag cell tag (td|th) default td
5539  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5540  * @cfg {String} align Aligns the content in a cell
5541  * @cfg {String} axis Categorizes cells
5542  * @cfg {String} bgcolor Specifies the background color of a cell
5543  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5544  * @cfg {Number} colspan Specifies the number of columns a cell should span
5545  * @cfg {String} headers Specifies one or more header cells a cell is related to
5546  * @cfg {Number} height Sets the height of a cell
5547  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5548  * @cfg {Number} rowspan Sets the number of rows a cell should span
5549  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5550  * @cfg {String} valign Vertical aligns the content in a cell
5551  * @cfg {Number} width Specifies the width of a cell
5552  * 
5553  * @constructor
5554  * Create a new TableCell
5555  * @param {Object} config The config object
5556  */
5557
5558 Roo.bootstrap.TableCell = function(config){
5559     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5560 };
5561
5562 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5563     
5564     html: false,
5565     cls: false,
5566     tag: false,
5567     abbr: false,
5568     align: false,
5569     axis: false,
5570     bgcolor: false,
5571     charoff: false,
5572     colspan: false,
5573     headers: false,
5574     height: false,
5575     nowrap: false,
5576     rowspan: false,
5577     scope: false,
5578     valign: false,
5579     width: false,
5580     
5581     
5582     getAutoCreate : function(){
5583         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5584         
5585         cfg = {
5586             tag: 'td'
5587         }
5588         
5589         if(this.tag){
5590             cfg.tag = this.tag;
5591         }
5592         
5593         if (this.html) {
5594             cfg.html=this.html
5595         }
5596         if (this.cls) {
5597             cfg.cls=this.cls
5598         }
5599         if (this.abbr) {
5600             cfg.abbr=this.abbr
5601         }
5602         if (this.align) {
5603             cfg.align=this.align
5604         }
5605         if (this.axis) {
5606             cfg.axis=this.axis
5607         }
5608         if (this.bgcolor) {
5609             cfg.bgcolor=this.bgcolor
5610         }
5611         if (this.charoff) {
5612             cfg.charoff=this.charoff
5613         }
5614         if (this.colspan) {
5615             cfg.colspan=this.colspan
5616         }
5617         if (this.headers) {
5618             cfg.headers=this.headers
5619         }
5620         if (this.height) {
5621             cfg.height=this.height
5622         }
5623         if (this.nowrap) {
5624             cfg.nowrap=this.nowrap
5625         }
5626         if (this.rowspan) {
5627             cfg.rowspan=this.rowspan
5628         }
5629         if (this.scope) {
5630             cfg.scope=this.scope
5631         }
5632         if (this.valign) {
5633             cfg.valign=this.valign
5634         }
5635         if (this.width) {
5636             cfg.width=this.width
5637         }
5638         
5639         
5640         return cfg;
5641     }
5642    
5643 });
5644
5645  
5646
5647  /*
5648  * - LGPL
5649  *
5650  * table row
5651  * 
5652  */
5653
5654 /**
5655  * @class Roo.bootstrap.TableRow
5656  * @extends Roo.bootstrap.Component
5657  * Bootstrap TableRow class
5658  * @cfg {String} cls row class
5659  * @cfg {String} align Aligns the content in a table row
5660  * @cfg {String} bgcolor Specifies a background color for a table row
5661  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5662  * @cfg {String} valign Vertical aligns the content in a table row
5663  * 
5664  * @constructor
5665  * Create a new TableRow
5666  * @param {Object} config The config object
5667  */
5668
5669 Roo.bootstrap.TableRow = function(config){
5670     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5671 };
5672
5673 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5674     
5675     cls: false,
5676     align: false,
5677     bgcolor: false,
5678     charoff: false,
5679     valign: false,
5680     
5681     getAutoCreate : function(){
5682         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5683         
5684         cfg = {
5685             tag: 'tr'
5686         }
5687             
5688         if(this.cls){
5689             cfg.cls = this.cls;
5690         }
5691         if(this.align){
5692             cfg.align = this.align;
5693         }
5694         if(this.bgcolor){
5695             cfg.bgcolor = this.bgcolor;
5696         }
5697         if(this.charoff){
5698             cfg.charoff = this.charoff;
5699         }
5700         if(this.valign){
5701             cfg.valign = this.valign;
5702         }
5703         
5704         return cfg;
5705     }
5706    
5707 });
5708
5709  
5710
5711  /*
5712  * - LGPL
5713  *
5714  * table body
5715  * 
5716  */
5717
5718 /**
5719  * @class Roo.bootstrap.TableBody
5720  * @extends Roo.bootstrap.Component
5721  * Bootstrap TableBody class
5722  * @cfg {String} cls element class
5723  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5724  * @cfg {String} align Aligns the content inside the element
5725  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5726  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5727  * 
5728  * @constructor
5729  * Create a new TableBody
5730  * @param {Object} config The config object
5731  */
5732
5733 Roo.bootstrap.TableBody = function(config){
5734     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5735 };
5736
5737 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5738     
5739     cls: false,
5740     tag: false,
5741     align: false,
5742     charoff: false,
5743     valign: false,
5744     
5745     getAutoCreate : function(){
5746         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5747         
5748         cfg = {
5749             tag: 'tbody'
5750         }
5751             
5752         if (this.cls) {
5753             cfg.cls=this.cls
5754         }
5755         if(this.tag){
5756             cfg.tag = this.tag;
5757         }
5758         
5759         if(this.align){
5760             cfg.align = this.align;
5761         }
5762         if(this.charoff){
5763             cfg.charoff = this.charoff;
5764         }
5765         if(this.valign){
5766             cfg.valign = this.valign;
5767         }
5768         
5769         return cfg;
5770     }
5771     
5772     
5773 //    initEvents : function()
5774 //    {
5775 //        
5776 //        if(!this.store){
5777 //            return;
5778 //        }
5779 //        
5780 //        this.store = Roo.factory(this.store, Roo.data);
5781 //        this.store.on('load', this.onLoad, this);
5782 //        
5783 //        this.store.load();
5784 //        
5785 //    },
5786 //    
5787 //    onLoad: function () 
5788 //    {   
5789 //        this.fireEvent('load', this);
5790 //    }
5791 //    
5792 //   
5793 });
5794
5795  
5796
5797  /*
5798  * Based on:
5799  * Ext JS Library 1.1.1
5800  * Copyright(c) 2006-2007, Ext JS, LLC.
5801  *
5802  * Originally Released Under LGPL - original licence link has changed is not relivant.
5803  *
5804  * Fork - LGPL
5805  * <script type="text/javascript">
5806  */
5807
5808 // as we use this in bootstrap.
5809 Roo.namespace('Roo.form');
5810  /**
5811  * @class Roo.form.Action
5812  * Internal Class used to handle form actions
5813  * @constructor
5814  * @param {Roo.form.BasicForm} el The form element or its id
5815  * @param {Object} config Configuration options
5816  */
5817
5818  
5819  
5820 // define the action interface
5821 Roo.form.Action = function(form, options){
5822     this.form = form;
5823     this.options = options || {};
5824 };
5825 /**
5826  * Client Validation Failed
5827  * @const 
5828  */
5829 Roo.form.Action.CLIENT_INVALID = 'client';
5830 /**
5831  * Server Validation Failed
5832  * @const 
5833  */
5834 Roo.form.Action.SERVER_INVALID = 'server';
5835  /**
5836  * Connect to Server Failed
5837  * @const 
5838  */
5839 Roo.form.Action.CONNECT_FAILURE = 'connect';
5840 /**
5841  * Reading Data from Server Failed
5842  * @const 
5843  */
5844 Roo.form.Action.LOAD_FAILURE = 'load';
5845
5846 Roo.form.Action.prototype = {
5847     type : 'default',
5848     failureType : undefined,
5849     response : undefined,
5850     result : undefined,
5851
5852     // interface method
5853     run : function(options){
5854
5855     },
5856
5857     // interface method
5858     success : function(response){
5859
5860     },
5861
5862     // interface method
5863     handleResponse : function(response){
5864
5865     },
5866
5867     // default connection failure
5868     failure : function(response){
5869         
5870         this.response = response;
5871         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5872         this.form.afterAction(this, false);
5873     },
5874
5875     processResponse : function(response){
5876         this.response = response;
5877         if(!response.responseText){
5878             return true;
5879         }
5880         this.result = this.handleResponse(response);
5881         return this.result;
5882     },
5883
5884     // utility functions used internally
5885     getUrl : function(appendParams){
5886         var url = this.options.url || this.form.url || this.form.el.dom.action;
5887         if(appendParams){
5888             var p = this.getParams();
5889             if(p){
5890                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5891             }
5892         }
5893         return url;
5894     },
5895
5896     getMethod : function(){
5897         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5898     },
5899
5900     getParams : function(){
5901         var bp = this.form.baseParams;
5902         var p = this.options.params;
5903         if(p){
5904             if(typeof p == "object"){
5905                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5906             }else if(typeof p == 'string' && bp){
5907                 p += '&' + Roo.urlEncode(bp);
5908             }
5909         }else if(bp){
5910             p = Roo.urlEncode(bp);
5911         }
5912         return p;
5913     },
5914
5915     createCallback : function(){
5916         return {
5917             success: this.success,
5918             failure: this.failure,
5919             scope: this,
5920             timeout: (this.form.timeout*1000),
5921             upload: this.form.fileUpload ? this.success : undefined
5922         };
5923     }
5924 };
5925
5926 Roo.form.Action.Submit = function(form, options){
5927     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5928 };
5929
5930 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5931     type : 'submit',
5932
5933     haveProgress : false,
5934     uploadComplete : false,
5935     
5936     // uploadProgress indicator.
5937     uploadProgress : function()
5938     {
5939         if (!this.form.progressUrl) {
5940             return;
5941         }
5942         
5943         if (!this.haveProgress) {
5944             Roo.MessageBox.progress("Uploading", "Uploading");
5945         }
5946         if (this.uploadComplete) {
5947            Roo.MessageBox.hide();
5948            return;
5949         }
5950         
5951         this.haveProgress = true;
5952    
5953         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5954         
5955         var c = new Roo.data.Connection();
5956         c.request({
5957             url : this.form.progressUrl,
5958             params: {
5959                 id : uid
5960             },
5961             method: 'GET',
5962             success : function(req){
5963                //console.log(data);
5964                 var rdata = false;
5965                 var edata;
5966                 try  {
5967                    rdata = Roo.decode(req.responseText)
5968                 } catch (e) {
5969                     Roo.log("Invalid data from server..");
5970                     Roo.log(edata);
5971                     return;
5972                 }
5973                 if (!rdata || !rdata.success) {
5974                     Roo.log(rdata);
5975                     Roo.MessageBox.alert(Roo.encode(rdata));
5976                     return;
5977                 }
5978                 var data = rdata.data;
5979                 
5980                 if (this.uploadComplete) {
5981                    Roo.MessageBox.hide();
5982                    return;
5983                 }
5984                    
5985                 if (data){
5986                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5987                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5988                     );
5989                 }
5990                 this.uploadProgress.defer(2000,this);
5991             },
5992        
5993             failure: function(data) {
5994                 Roo.log('progress url failed ');
5995                 Roo.log(data);
5996             },
5997             scope : this
5998         });
5999            
6000     },
6001     
6002     
6003     run : function()
6004     {
6005         // run get Values on the form, so it syncs any secondary forms.
6006         this.form.getValues();
6007         
6008         var o = this.options;
6009         var method = this.getMethod();
6010         var isPost = method == 'POST';
6011         if(o.clientValidation === false || this.form.isValid()){
6012             
6013             if (this.form.progressUrl) {
6014                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6015                     (new Date() * 1) + '' + Math.random());
6016                     
6017             } 
6018             
6019             
6020             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6021                 form:this.form.el.dom,
6022                 url:this.getUrl(!isPost),
6023                 method: method,
6024                 params:isPost ? this.getParams() : null,
6025                 isUpload: this.form.fileUpload
6026             }));
6027             
6028             this.uploadProgress();
6029
6030         }else if (o.clientValidation !== false){ // client validation failed
6031             this.failureType = Roo.form.Action.CLIENT_INVALID;
6032             this.form.afterAction(this, false);
6033         }
6034     },
6035
6036     success : function(response)
6037     {
6038         this.uploadComplete= true;
6039         if (this.haveProgress) {
6040             Roo.MessageBox.hide();
6041         }
6042         
6043         
6044         var result = this.processResponse(response);
6045         if(result === true || result.success){
6046             this.form.afterAction(this, true);
6047             return;
6048         }
6049         if(result.errors){
6050             this.form.markInvalid(result.errors);
6051             this.failureType = Roo.form.Action.SERVER_INVALID;
6052         }
6053         this.form.afterAction(this, false);
6054     },
6055     failure : function(response)
6056     {
6057         this.uploadComplete= true;
6058         if (this.haveProgress) {
6059             Roo.MessageBox.hide();
6060         }
6061         
6062         this.response = response;
6063         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6064         this.form.afterAction(this, false);
6065     },
6066     
6067     handleResponse : function(response){
6068         if(this.form.errorReader){
6069             var rs = this.form.errorReader.read(response);
6070             var errors = [];
6071             if(rs.records){
6072                 for(var i = 0, len = rs.records.length; i < len; i++) {
6073                     var r = rs.records[i];
6074                     errors[i] = r.data;
6075                 }
6076             }
6077             if(errors.length < 1){
6078                 errors = null;
6079             }
6080             return {
6081                 success : rs.success,
6082                 errors : errors
6083             };
6084         }
6085         var ret = false;
6086         try {
6087             ret = Roo.decode(response.responseText);
6088         } catch (e) {
6089             ret = {
6090                 success: false,
6091                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6092                 errors : []
6093             };
6094         }
6095         return ret;
6096         
6097     }
6098 });
6099
6100
6101 Roo.form.Action.Load = function(form, options){
6102     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6103     this.reader = this.form.reader;
6104 };
6105
6106 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6107     type : 'load',
6108
6109     run : function(){
6110         
6111         Roo.Ajax.request(Roo.apply(
6112                 this.createCallback(), {
6113                     method:this.getMethod(),
6114                     url:this.getUrl(false),
6115                     params:this.getParams()
6116         }));
6117     },
6118
6119     success : function(response){
6120         
6121         var result = this.processResponse(response);
6122         if(result === true || !result.success || !result.data){
6123             this.failureType = Roo.form.Action.LOAD_FAILURE;
6124             this.form.afterAction(this, false);
6125             return;
6126         }
6127         this.form.clearInvalid();
6128         this.form.setValues(result.data);
6129         this.form.afterAction(this, true);
6130     },
6131
6132     handleResponse : function(response){
6133         if(this.form.reader){
6134             var rs = this.form.reader.read(response);
6135             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6136             return {
6137                 success : rs.success,
6138                 data : data
6139             };
6140         }
6141         return Roo.decode(response.responseText);
6142     }
6143 });
6144
6145 Roo.form.Action.ACTION_TYPES = {
6146     'load' : Roo.form.Action.Load,
6147     'submit' : Roo.form.Action.Submit
6148 };/*
6149  * - LGPL
6150  *
6151  * form
6152  * 
6153  */
6154
6155 /**
6156  * @class Roo.bootstrap.Form
6157  * @extends Roo.bootstrap.Component
6158  * Bootstrap Form class
6159  * @cfg {String} method  GET | POST (default POST)
6160  * @cfg {String} labelAlign top | left (default top)
6161  * @cfg {String} align left  | right - for navbars
6162  * @cfg {Boolean} loadMask load mask when submit (default true)
6163
6164  * 
6165  * @constructor
6166  * Create a new Form
6167  * @param {Object} config The config object
6168  */
6169
6170
6171 Roo.bootstrap.Form = function(config){
6172     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6173     this.addEvents({
6174         /**
6175          * @event clientvalidation
6176          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6177          * @param {Form} this
6178          * @param {Boolean} valid true if the form has passed client-side validation
6179          */
6180         clientvalidation: true,
6181         /**
6182          * @event beforeaction
6183          * Fires before any action is performed. Return false to cancel the action.
6184          * @param {Form} this
6185          * @param {Action} action The action to be performed
6186          */
6187         beforeaction: true,
6188         /**
6189          * @event actionfailed
6190          * Fires when an action fails.
6191          * @param {Form} this
6192          * @param {Action} action The action that failed
6193          */
6194         actionfailed : true,
6195         /**
6196          * @event actioncomplete
6197          * Fires when an action is completed.
6198          * @param {Form} this
6199          * @param {Action} action The action that completed
6200          */
6201         actioncomplete : true
6202     });
6203     
6204 };
6205
6206 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6207       
6208      /**
6209      * @cfg {String} method
6210      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6211      */
6212     method : 'POST',
6213     /**
6214      * @cfg {String} url
6215      * The URL to use for form actions if one isn't supplied in the action options.
6216      */
6217     /**
6218      * @cfg {Boolean} fileUpload
6219      * Set to true if this form is a file upload.
6220      */
6221      
6222     /**
6223      * @cfg {Object} baseParams
6224      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6225      */
6226       
6227     /**
6228      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6229      */
6230     timeout: 30,
6231     /**
6232      * @cfg {Sting} align (left|right) for navbar forms
6233      */
6234     align : 'left',
6235
6236     // private
6237     activeAction : null,
6238  
6239     /**
6240      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6241      * element by passing it or its id or mask the form itself by passing in true.
6242      * @type Mixed
6243      */
6244     waitMsgTarget : false,
6245     
6246     loadMask : true,
6247     
6248     getAutoCreate : function(){
6249         
6250         var cfg = {
6251             tag: 'form',
6252             method : this.method || 'POST',
6253             id : this.id || Roo.id(),
6254             cls : ''
6255         }
6256         if (this.parent().xtype.match(/^Nav/)) {
6257             cfg.cls = 'navbar-form navbar-' + this.align;
6258             
6259         }
6260         
6261         if (this.labelAlign == 'left' ) {
6262             cfg.cls += ' form-horizontal';
6263         }
6264         
6265         
6266         return cfg;
6267     },
6268     initEvents : function()
6269     {
6270         this.el.on('submit', this.onSubmit, this);
6271         // this was added as random key presses on the form where triggering form submit.
6272         this.el.on('keypress', function(e) {
6273             if (e.getCharCode() != 13) {
6274                 return true;
6275             }
6276             // we might need to allow it for textareas.. and some other items.
6277             // check e.getTarget().
6278             
6279             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6280                 return true;
6281             }
6282         
6283             Roo.log("keypress blocked");
6284             
6285             e.preventDefault();
6286             return false;
6287         });
6288         
6289     },
6290     // private
6291     onSubmit : function(e){
6292         e.stopEvent();
6293     },
6294     
6295      /**
6296      * Returns true if client-side validation on the form is successful.
6297      * @return Boolean
6298      */
6299     isValid : function(){
6300         var items = this.getItems();
6301         var valid = true;
6302         items.each(function(f){
6303            if(!f.validate()){
6304                valid = false;
6305                
6306            }
6307         });
6308         return valid;
6309     },
6310     /**
6311      * Returns true if any fields in this form have changed since their original load.
6312      * @return Boolean
6313      */
6314     isDirty : function(){
6315         var dirty = false;
6316         var items = this.getItems();
6317         items.each(function(f){
6318            if(f.isDirty()){
6319                dirty = true;
6320                return false;
6321            }
6322            return true;
6323         });
6324         return dirty;
6325     },
6326      /**
6327      * Performs a predefined action (submit or load) or custom actions you define on this form.
6328      * @param {String} actionName The name of the action type
6329      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6330      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6331      * accept other config options):
6332      * <pre>
6333 Property          Type             Description
6334 ----------------  ---------------  ----------------------------------------------------------------------------------
6335 url               String           The url for the action (defaults to the form's url)
6336 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6337 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6338 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6339                                    validate the form on the client (defaults to false)
6340      * </pre>
6341      * @return {BasicForm} this
6342      */
6343     doAction : function(action, options){
6344         if(typeof action == 'string'){
6345             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6346         }
6347         if(this.fireEvent('beforeaction', this, action) !== false){
6348             this.beforeAction(action);
6349             action.run.defer(100, action);
6350         }
6351         return this;
6352     },
6353     
6354     // private
6355     beforeAction : function(action){
6356         var o = action.options;
6357         
6358         if(this.loadMask){
6359             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6360         }
6361         // not really supported yet.. ??
6362         
6363         //if(this.waitMsgTarget === true){
6364         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6365         //}else if(this.waitMsgTarget){
6366         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6367         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6368         //}else {
6369         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6370        // }
6371          
6372     },
6373
6374     // private
6375     afterAction : function(action, success){
6376         this.activeAction = null;
6377         var o = action.options;
6378         
6379         //if(this.waitMsgTarget === true){
6380             this.el.unmask();
6381         //}else if(this.waitMsgTarget){
6382         //    this.waitMsgTarget.unmask();
6383         //}else{
6384         //    Roo.MessageBox.updateProgress(1);
6385         //    Roo.MessageBox.hide();
6386        // }
6387         // 
6388         if(success){
6389             if(o.reset){
6390                 this.reset();
6391             }
6392             Roo.callback(o.success, o.scope, [this, action]);
6393             this.fireEvent('actioncomplete', this, action);
6394             
6395         }else{
6396             
6397             // failure condition..
6398             // we have a scenario where updates need confirming.
6399             // eg. if a locking scenario exists..
6400             // we look for { errors : { needs_confirm : true }} in the response.
6401             if (
6402                 (typeof(action.result) != 'undefined')  &&
6403                 (typeof(action.result.errors) != 'undefined')  &&
6404                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6405            ){
6406                 var _t = this;
6407                 Roo.log("not supported yet");
6408                  /*
6409                 
6410                 Roo.MessageBox.confirm(
6411                     "Change requires confirmation",
6412                     action.result.errorMsg,
6413                     function(r) {
6414                         if (r != 'yes') {
6415                             return;
6416                         }
6417                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6418                     }
6419                     
6420                 );
6421                 */
6422                 
6423                 
6424                 return;
6425             }
6426             
6427             Roo.callback(o.failure, o.scope, [this, action]);
6428             // show an error message if no failed handler is set..
6429             if (!this.hasListener('actionfailed')) {
6430                 Roo.log("need to add dialog support");
6431                 /*
6432                 Roo.MessageBox.alert("Error",
6433                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6434                         action.result.errorMsg :
6435                         "Saving Failed, please check your entries or try again"
6436                 );
6437                 */
6438             }
6439             
6440             this.fireEvent('actionfailed', this, action);
6441         }
6442         
6443     },
6444     /**
6445      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6446      * @param {String} id The value to search for
6447      * @return Field
6448      */
6449     findField : function(id){
6450         var items = this.getItems();
6451         var field = items.get(id);
6452         if(!field){
6453              items.each(function(f){
6454                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6455                     field = f;
6456                     return false;
6457                 }
6458                 return true;
6459             });
6460         }
6461         return field || null;
6462     },
6463      /**
6464      * Mark fields in this form invalid in bulk.
6465      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6466      * @return {BasicForm} this
6467      */
6468     markInvalid : function(errors){
6469         if(errors instanceof Array){
6470             for(var i = 0, len = errors.length; i < len; i++){
6471                 var fieldError = errors[i];
6472                 var f = this.findField(fieldError.id);
6473                 if(f){
6474                     f.markInvalid(fieldError.msg);
6475                 }
6476             }
6477         }else{
6478             var field, id;
6479             for(id in errors){
6480                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6481                     field.markInvalid(errors[id]);
6482                 }
6483             }
6484         }
6485         //Roo.each(this.childForms || [], function (f) {
6486         //    f.markInvalid(errors);
6487         //});
6488         
6489         return this;
6490     },
6491
6492     /**
6493      * Set values for fields in this form in bulk.
6494      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6495      * @return {BasicForm} this
6496      */
6497     setValues : function(values){
6498         if(values instanceof Array){ // array of objects
6499             for(var i = 0, len = values.length; i < len; i++){
6500                 var v = values[i];
6501                 var f = this.findField(v.id);
6502                 if(f){
6503                     f.setValue(v.value);
6504                     if(this.trackResetOnLoad){
6505                         f.originalValue = f.getValue();
6506                     }
6507                 }
6508             }
6509         }else{ // object hash
6510             var field, id;
6511             for(id in values){
6512                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6513                     
6514                     if (field.setFromData && 
6515                         field.valueField && 
6516                         field.displayField &&
6517                         // combos' with local stores can 
6518                         // be queried via setValue()
6519                         // to set their value..
6520                         (field.store && !field.store.isLocal)
6521                         ) {
6522                         // it's a combo
6523                         var sd = { };
6524                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6525                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6526                         field.setFromData(sd);
6527                         
6528                     } else {
6529                         field.setValue(values[id]);
6530                     }
6531                     
6532                     
6533                     if(this.trackResetOnLoad){
6534                         field.originalValue = field.getValue();
6535                     }
6536                 }
6537             }
6538         }
6539          
6540         //Roo.each(this.childForms || [], function (f) {
6541         //    f.setValues(values);
6542         //});
6543                 
6544         return this;
6545     },
6546
6547     /**
6548      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6549      * they are returned as an array.
6550      * @param {Boolean} asString
6551      * @return {Object}
6552      */
6553     getValues : function(asString){
6554         //if (this.childForms) {
6555             // copy values from the child forms
6556         //    Roo.each(this.childForms, function (f) {
6557         //        this.setValues(f.getValues());
6558         //    }, this);
6559         //}
6560         
6561         
6562         
6563         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6564         if(asString === true){
6565             return fs;
6566         }
6567         return Roo.urlDecode(fs);
6568     },
6569     
6570     /**
6571      * Returns the fields in this form as an object with key/value pairs. 
6572      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6573      * @return {Object}
6574      */
6575     getFieldValues : function(with_hidden)
6576     {
6577         var items = this.getItems();
6578         var ret = {};
6579         items.each(function(f){
6580             if (!f.getName()) {
6581                 return;
6582             }
6583             var v = f.getValue();
6584             if (f.inputType =='radio') {
6585                 if (typeof(ret[f.getName()]) == 'undefined') {
6586                     ret[f.getName()] = ''; // empty..
6587                 }
6588                 
6589                 if (!f.el.dom.checked) {
6590                     return;
6591                     
6592                 }
6593                 v = f.el.dom.value;
6594                 
6595             }
6596             
6597             // not sure if this supported any more..
6598             if ((typeof(v) == 'object') && f.getRawValue) {
6599                 v = f.getRawValue() ; // dates..
6600             }
6601             // combo boxes where name != hiddenName...
6602             if (f.name != f.getName()) {
6603                 ret[f.name] = f.getRawValue();
6604             }
6605             ret[f.getName()] = v;
6606         });
6607         
6608         return ret;
6609     },
6610
6611     /**
6612      * Clears all invalid messages in this form.
6613      * @return {BasicForm} this
6614      */
6615     clearInvalid : function(){
6616         var items = this.getItems();
6617         
6618         items.each(function(f){
6619            f.clearInvalid();
6620         });
6621         
6622         
6623         
6624         return this;
6625     },
6626
6627     /**
6628      * Resets this form.
6629      * @return {BasicForm} this
6630      */
6631     reset : function(){
6632         var items = this.getItems();
6633         items.each(function(f){
6634             f.reset();
6635         });
6636         
6637         Roo.each(this.childForms || [], function (f) {
6638             f.reset();
6639         });
6640        
6641         
6642         return this;
6643     },
6644     getItems : function()
6645     {
6646         var r=new Roo.util.MixedCollection(false, function(o){
6647             return o.id || (o.id = Roo.id());
6648         });
6649         var iter = function(el) {
6650             if (el.inputEl) {
6651                 r.add(el);
6652             }
6653             if (!el.items) {
6654                 return;
6655             }
6656             Roo.each(el.items,function(e) {
6657                 iter(e);
6658             });
6659             
6660             
6661         };
6662         iter(this);
6663         return r;
6664         
6665         
6666         
6667         
6668     }
6669     
6670 });
6671
6672  
6673 /*
6674  * Based on:
6675  * Ext JS Library 1.1.1
6676  * Copyright(c) 2006-2007, Ext JS, LLC.
6677  *
6678  * Originally Released Under LGPL - original licence link has changed is not relivant.
6679  *
6680  * Fork - LGPL
6681  * <script type="text/javascript">
6682  */
6683 /**
6684  * @class Roo.form.VTypes
6685  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6686  * @singleton
6687  */
6688 Roo.form.VTypes = function(){
6689     // closure these in so they are only created once.
6690     var alpha = /^[a-zA-Z_]+$/;
6691     var alphanum = /^[a-zA-Z0-9_]+$/;
6692     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6693     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6694
6695     // All these messages and functions are configurable
6696     return {
6697         /**
6698          * The function used to validate email addresses
6699          * @param {String} value The email address
6700          */
6701         'email' : function(v){
6702             return email.test(v);
6703         },
6704         /**
6705          * The error text to display when the email validation function returns false
6706          * @type String
6707          */
6708         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6709         /**
6710          * The keystroke filter mask to be applied on email input
6711          * @type RegExp
6712          */
6713         'emailMask' : /[a-z0-9_\.\-@]/i,
6714
6715         /**
6716          * The function used to validate URLs
6717          * @param {String} value The URL
6718          */
6719         'url' : function(v){
6720             return url.test(v);
6721         },
6722         /**
6723          * The error text to display when the url validation function returns false
6724          * @type String
6725          */
6726         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6727         
6728         /**
6729          * The function used to validate alpha values
6730          * @param {String} value The value
6731          */
6732         'alpha' : function(v){
6733             return alpha.test(v);
6734         },
6735         /**
6736          * The error text to display when the alpha validation function returns false
6737          * @type String
6738          */
6739         'alphaText' : 'This field should only contain letters and _',
6740         /**
6741          * The keystroke filter mask to be applied on alpha input
6742          * @type RegExp
6743          */
6744         'alphaMask' : /[a-z_]/i,
6745
6746         /**
6747          * The function used to validate alphanumeric values
6748          * @param {String} value The value
6749          */
6750         'alphanum' : function(v){
6751             return alphanum.test(v);
6752         },
6753         /**
6754          * The error text to display when the alphanumeric validation function returns false
6755          * @type String
6756          */
6757         'alphanumText' : 'This field should only contain letters, numbers and _',
6758         /**
6759          * The keystroke filter mask to be applied on alphanumeric input
6760          * @type RegExp
6761          */
6762         'alphanumMask' : /[a-z0-9_]/i
6763     };
6764 }();/*
6765  * - LGPL
6766  *
6767  * Input
6768  * 
6769  */
6770
6771 /**
6772  * @class Roo.bootstrap.Input
6773  * @extends Roo.bootstrap.Component
6774  * Bootstrap Input class
6775  * @cfg {Boolean} disabled is it disabled
6776  * @cfg {String} fieldLabel - the label associated
6777  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6778  * @cfg {String} name name of the input
6779  * @cfg {string} fieldLabel - the label associated
6780  * @cfg {string}  inputType - input / file submit ...
6781  * @cfg {string} placeholder - placeholder to put in text.
6782  * @cfg {string}  before - input group add on before
6783  * @cfg {string} after - input group add on after
6784  * @cfg {string} size - (lg|sm) or leave empty..
6785  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6786  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6787  * @cfg {Number} md colspan out of 12 for computer-sized screens
6788  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6789  * @cfg {string} value default value of the input
6790  * @cfg {Number} labelWidth set the width of label (0-12)
6791  * @cfg {String} labelAlign (top|left)
6792  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6793  * @cfg {String} align (left|center|right) Default left
6794  * 
6795  * 
6796  * @constructor
6797  * Create a new Input
6798  * @param {Object} config The config object
6799  */
6800
6801 Roo.bootstrap.Input = function(config){
6802     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6803    
6804         this.addEvents({
6805             /**
6806              * @event focus
6807              * Fires when this field receives input focus.
6808              * @param {Roo.form.Field} this
6809              */
6810             focus : true,
6811             /**
6812              * @event blur
6813              * Fires when this field loses input focus.
6814              * @param {Roo.form.Field} this
6815              */
6816             blur : true,
6817             /**
6818              * @event specialkey
6819              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6820              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6821              * @param {Roo.form.Field} this
6822              * @param {Roo.EventObject} e The event object
6823              */
6824             specialkey : true,
6825             /**
6826              * @event change
6827              * Fires just before the field blurs if the field value has changed.
6828              * @param {Roo.form.Field} this
6829              * @param {Mixed} newValue The new value
6830              * @param {Mixed} oldValue The original value
6831              */
6832             change : true,
6833             /**
6834              * @event invalid
6835              * Fires after the field has been marked as invalid.
6836              * @param {Roo.form.Field} this
6837              * @param {String} msg The validation message
6838              */
6839             invalid : true,
6840             /**
6841              * @event valid
6842              * Fires after the field has been validated with no errors.
6843              * @param {Roo.form.Field} this
6844              */
6845             valid : true,
6846              /**
6847              * @event keyup
6848              * Fires after the key up
6849              * @param {Roo.form.Field} this
6850              * @param {Roo.EventObject}  e The event Object
6851              */
6852             keyup : true
6853         });
6854 };
6855
6856 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6857      /**
6858      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6859       automatic validation (defaults to "keyup").
6860      */
6861     validationEvent : "keyup",
6862      /**
6863      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6864      */
6865     validateOnBlur : true,
6866     /**
6867      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6868      */
6869     validationDelay : 250,
6870      /**
6871      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6872      */
6873     focusClass : "x-form-focus",  // not needed???
6874     
6875        
6876     /**
6877      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6878      */
6879     invalidClass : "has-error",
6880     
6881     /**
6882      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6883      */
6884     selectOnFocus : false,
6885     
6886      /**
6887      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6888      */
6889     maskRe : null,
6890        /**
6891      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6892      */
6893     vtype : null,
6894     
6895       /**
6896      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6897      */
6898     disableKeyFilter : false,
6899     
6900        /**
6901      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6902      */
6903     disabled : false,
6904      /**
6905      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6906      */
6907     allowBlank : true,
6908     /**
6909      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6910      */
6911     blankText : "This field is required",
6912     
6913      /**
6914      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6915      */
6916     minLength : 0,
6917     /**
6918      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6919      */
6920     maxLength : Number.MAX_VALUE,
6921     /**
6922      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6923      */
6924     minLengthText : "The minimum length for this field is {0}",
6925     /**
6926      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6927      */
6928     maxLengthText : "The maximum length for this field is {0}",
6929   
6930     
6931     /**
6932      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6933      * If available, this function will be called only after the basic validators all return true, and will be passed the
6934      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6935      */
6936     validator : null,
6937     /**
6938      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6939      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6940      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6941      */
6942     regex : null,
6943     /**
6944      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6945      */
6946     regexText : "",
6947     
6948     
6949     
6950     fieldLabel : '',
6951     inputType : 'text',
6952     
6953     name : false,
6954     placeholder: false,
6955     before : false,
6956     after : false,
6957     size : false,
6958     // private
6959     hasFocus : false,
6960     preventMark: false,
6961     isFormField : true,
6962     value : '',
6963     labelWidth : 2,
6964     labelAlign : false,
6965     readOnly : false,
6966     align : false,
6967     formatedValue : false,
6968     
6969     parentLabelAlign : function()
6970     {
6971         var parent = this;
6972         while (parent.parent()) {
6973             parent = parent.parent();
6974             if (typeof(parent.labelAlign) !='undefined') {
6975                 return parent.labelAlign;
6976             }
6977         }
6978         return 'left';
6979         
6980     },
6981     
6982     getAutoCreate : function(){
6983         
6984         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6985         
6986         var id = Roo.id();
6987         
6988         var cfg = {};
6989         
6990         if(this.inputType != 'hidden'){
6991             cfg.cls = 'form-group' //input-group
6992         }
6993         
6994         var input =  {
6995             tag: 'input',
6996             id : id,
6997             type : this.inputType,
6998             value : this.value,
6999             cls : 'form-control',
7000             placeholder : this.placeholder || ''
7001             
7002         };
7003         
7004         if(this.align){
7005             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7006         }
7007         
7008         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7009             input.maxLength = this.maxLength;
7010         }
7011         
7012         if (this.disabled) {
7013             input.disabled=true;
7014         }
7015         
7016         if (this.readOnly) {
7017             input.readonly=true;
7018         }
7019         
7020         if (this.name) {
7021             input.name = this.name;
7022         }
7023         if (this.size) {
7024             input.cls += ' input-' + this.size;
7025         }
7026         var settings=this;
7027         ['xs','sm','md','lg'].map(function(size){
7028             if (settings[size]) {
7029                 cfg.cls += ' col-' + size + '-' + settings[size];
7030             }
7031         });
7032         
7033         var inputblock = input;
7034         
7035         if (this.before || this.after) {
7036             
7037             inputblock = {
7038                 cls : 'input-group',
7039                 cn :  [] 
7040             };
7041             if (this.before && typeof(this.before) == 'string') {
7042                 
7043                 inputblock.cn.push({
7044                     tag :'span',
7045                     cls : 'roo-input-before input-group-addon',
7046                     html : this.before
7047                 });
7048             }
7049             if (this.before && typeof(this.before) == 'object') {
7050                 this.before = Roo.factory(this.before);
7051                 Roo.log(this.before);
7052                 inputblock.cn.push({
7053                     tag :'span',
7054                     cls : 'roo-input-before input-group-' +
7055                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7056                 });
7057             }
7058             
7059             inputblock.cn.push(input);
7060             
7061             if (this.after && typeof(this.after) == 'string') {
7062                 inputblock.cn.push({
7063                     tag :'span',
7064                     cls : 'roo-input-after input-group-addon',
7065                     html : this.after
7066                 });
7067             }
7068             if (this.after && typeof(this.after) == 'object') {
7069                 this.after = Roo.factory(this.after);
7070                 Roo.log(this.after);
7071                 inputblock.cn.push({
7072                     tag :'span',
7073                     cls : 'roo-input-after input-group-' +
7074                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7075                 });
7076             }
7077         };
7078         
7079         if (align ==='left' && this.fieldLabel.length) {
7080                 Roo.log("left and has label");
7081                 cfg.cn = [
7082                     
7083                     {
7084                         tag: 'label',
7085                         'for' :  id,
7086                         cls : 'control-label col-sm-' + this.labelWidth,
7087                         html : this.fieldLabel
7088                         
7089                     },
7090                     {
7091                         cls : "col-sm-" + (12 - this.labelWidth), 
7092                         cn: [
7093                             inputblock
7094                         ]
7095                     }
7096                     
7097                 ];
7098         } else if ( this.fieldLabel.length) {
7099                 Roo.log(" label");
7100                  cfg.cn = [
7101                    
7102                     {
7103                         tag: 'label',
7104                         //cls : 'input-group-addon',
7105                         html : this.fieldLabel
7106                         
7107                     },
7108                     
7109                     inputblock
7110                     
7111                 ];
7112
7113         } else {
7114             
7115                 Roo.log(" no label && no align");
7116                 cfg.cn = [
7117                     
7118                         inputblock
7119                     
7120                 ];
7121                 
7122                 
7123         };
7124         Roo.log('input-parentType: ' + this.parentType);
7125         
7126         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7127            cfg.cls += ' navbar-form';
7128            Roo.log(cfg);
7129         }
7130         
7131         return cfg;
7132         
7133     },
7134     /**
7135      * return the real input element.
7136      */
7137     inputEl: function ()
7138     {
7139         return this.el.select('input.form-control',true).first();
7140     },
7141     setDisabled : function(v)
7142     {
7143         var i  = this.inputEl().dom;
7144         if (!v) {
7145             i.removeAttribute('disabled');
7146             return;
7147             
7148         }
7149         i.setAttribute('disabled','true');
7150     },
7151     initEvents : function()
7152     {
7153         
7154         this.inputEl().on("keydown" , this.fireKey,  this);
7155         this.inputEl().on("focus", this.onFocus,  this);
7156         this.inputEl().on("blur", this.onBlur,  this);
7157         
7158         this.inputEl().relayEvent('keyup', this);
7159
7160         // reference to original value for reset
7161         this.originalValue = this.getValue();
7162         //Roo.form.TextField.superclass.initEvents.call(this);
7163         if(this.validationEvent == 'keyup'){
7164             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7165             this.inputEl().on('keyup', this.filterValidation, this);
7166         }
7167         else if(this.validationEvent !== false){
7168             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7169         }
7170         
7171         if(this.selectOnFocus){
7172             this.on("focus", this.preFocus, this);
7173             
7174         }
7175         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7176             this.inputEl().on("keypress", this.filterKeys, this);
7177         }
7178        /* if(this.grow){
7179             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7180             this.el.on("click", this.autoSize,  this);
7181         }
7182         */
7183         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7184             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7185         }
7186         
7187         if (typeof(this.before) == 'object') {
7188             this.before.render(this.el.select('.roo-input-before',true).first());
7189         }
7190         if (typeof(this.after) == 'object') {
7191             this.after.render(this.el.select('.roo-input-after',true).first());
7192         }
7193         
7194         
7195     },
7196     filterValidation : function(e){
7197         if(!e.isNavKeyPress()){
7198             this.validationTask.delay(this.validationDelay);
7199         }
7200     },
7201      /**
7202      * Validates the field value
7203      * @return {Boolean} True if the value is valid, else false
7204      */
7205     validate : function(){
7206         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7207         if(this.disabled || this.validateValue(this.getRawValue())){
7208             this.clearInvalid();
7209             return true;
7210         }
7211         return false;
7212     },
7213     
7214     
7215     /**
7216      * Validates a value according to the field's validation rules and marks the field as invalid
7217      * if the validation fails
7218      * @param {Mixed} value The value to validate
7219      * @return {Boolean} True if the value is valid, else false
7220      */
7221     validateValue : function(value){
7222         if(value.length < 1)  { // if it's blank
7223              if(this.allowBlank){
7224                 this.clearInvalid();
7225                 return true;
7226              }else{
7227                 this.markInvalid(this.blankText);
7228                 return false;
7229              }
7230         }
7231         if(value.length < this.minLength){
7232             this.markInvalid(String.format(this.minLengthText, this.minLength));
7233             return false;
7234         }
7235         if(value.length > this.maxLength){
7236             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7237             return false;
7238         }
7239         if(this.vtype){
7240             var vt = Roo.form.VTypes;
7241             if(!vt[this.vtype](value, this)){
7242                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7243                 return false;
7244             }
7245         }
7246         if(typeof this.validator == "function"){
7247             var msg = this.validator(value);
7248             if(msg !== true){
7249                 this.markInvalid(msg);
7250                 return false;
7251             }
7252         }
7253         if(this.regex && !this.regex.test(value)){
7254             this.markInvalid(this.regexText);
7255             return false;
7256         }
7257         return true;
7258     },
7259
7260     
7261     
7262      // private
7263     fireKey : function(e){
7264         //Roo.log('field ' + e.getKey());
7265         if(e.isNavKeyPress()){
7266             this.fireEvent("specialkey", this, e);
7267         }
7268     },
7269     focus : function (selectText){
7270         if(this.rendered){
7271             this.inputEl().focus();
7272             if(selectText === true){
7273                 this.inputEl().dom.select();
7274             }
7275         }
7276         return this;
7277     } ,
7278     
7279     onFocus : function(){
7280         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7281            // this.el.addClass(this.focusClass);
7282         }
7283         if(!this.hasFocus){
7284             this.hasFocus = true;
7285             this.startValue = this.getValue();
7286             this.fireEvent("focus", this);
7287         }
7288     },
7289     
7290     beforeBlur : Roo.emptyFn,
7291
7292     
7293     // private
7294     onBlur : function(){
7295         this.beforeBlur();
7296         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7297             //this.el.removeClass(this.focusClass);
7298         }
7299         this.hasFocus = false;
7300         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7301             this.validate();
7302         }
7303         var v = this.getValue();
7304         if(String(v) !== String(this.startValue)){
7305             this.fireEvent('change', this, v, this.startValue);
7306         }
7307         this.fireEvent("blur", this);
7308     },
7309     
7310     /**
7311      * Resets the current field value to the originally loaded value and clears any validation messages
7312      */
7313     reset : function(){
7314         this.setValue(this.originalValue);
7315         this.clearInvalid();
7316     },
7317      /**
7318      * Returns the name of the field
7319      * @return {Mixed} name The name field
7320      */
7321     getName: function(){
7322         return this.name;
7323     },
7324      /**
7325      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7326      * @return {Mixed} value The field value
7327      */
7328     getValue : function(){
7329         
7330         var v = this.inputEl().getValue();
7331         
7332         return v;
7333     },
7334     /**
7335      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7336      * @return {Mixed} value The field value
7337      */
7338     getRawValue : function(){
7339         var v = this.inputEl().getValue();
7340         
7341         return v;
7342     },
7343     
7344     /**
7345      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7346      * @param {Mixed} value The value to set
7347      */
7348     setRawValue : function(v){
7349         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7350     },
7351     
7352     selectText : function(start, end){
7353         var v = this.getRawValue();
7354         if(v.length > 0){
7355             start = start === undefined ? 0 : start;
7356             end = end === undefined ? v.length : end;
7357             var d = this.inputEl().dom;
7358             if(d.setSelectionRange){
7359                 d.setSelectionRange(start, end);
7360             }else if(d.createTextRange){
7361                 var range = d.createTextRange();
7362                 range.moveStart("character", start);
7363                 range.moveEnd("character", v.length-end);
7364                 range.select();
7365             }
7366         }
7367     },
7368     
7369     /**
7370      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7371      * @param {Mixed} value The value to set
7372      */
7373     setValue : function(v){
7374         this.value = v;
7375         if(this.rendered){
7376             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7377             this.validate();
7378         }
7379     },
7380     
7381     /*
7382     processValue : function(value){
7383         if(this.stripCharsRe){
7384             var newValue = value.replace(this.stripCharsRe, '');
7385             if(newValue !== value){
7386                 this.setRawValue(newValue);
7387                 return newValue;
7388             }
7389         }
7390         return value;
7391     },
7392   */
7393     preFocus : function(){
7394         
7395         if(this.selectOnFocus){
7396             this.inputEl().dom.select();
7397         }
7398     },
7399     filterKeys : function(e){
7400         var k = e.getKey();
7401         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7402             return;
7403         }
7404         var c = e.getCharCode(), cc = String.fromCharCode(c);
7405         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7406             return;
7407         }
7408         if(!this.maskRe.test(cc)){
7409             e.stopEvent();
7410         }
7411     },
7412      /**
7413      * Clear any invalid styles/messages for this field
7414      */
7415     clearInvalid : function(){
7416         
7417         if(!this.el || this.preventMark){ // not rendered
7418             return;
7419         }
7420         this.el.removeClass(this.invalidClass);
7421         /*
7422         switch(this.msgTarget){
7423             case 'qtip':
7424                 this.el.dom.qtip = '';
7425                 break;
7426             case 'title':
7427                 this.el.dom.title = '';
7428                 break;
7429             case 'under':
7430                 if(this.errorEl){
7431                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7432                 }
7433                 break;
7434             case 'side':
7435                 if(this.errorIcon){
7436                     this.errorIcon.dom.qtip = '';
7437                     this.errorIcon.hide();
7438                     this.un('resize', this.alignErrorIcon, this);
7439                 }
7440                 break;
7441             default:
7442                 var t = Roo.getDom(this.msgTarget);
7443                 t.innerHTML = '';
7444                 t.style.display = 'none';
7445                 break;
7446         }
7447         */
7448         this.fireEvent('valid', this);
7449     },
7450      /**
7451      * Mark this field as invalid
7452      * @param {String} msg The validation message
7453      */
7454     markInvalid : function(msg){
7455         if(!this.el  || this.preventMark){ // not rendered
7456             return;
7457         }
7458         this.el.addClass(this.invalidClass);
7459         /*
7460         msg = msg || this.invalidText;
7461         switch(this.msgTarget){
7462             case 'qtip':
7463                 this.el.dom.qtip = msg;
7464                 this.el.dom.qclass = 'x-form-invalid-tip';
7465                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7466                     Roo.QuickTips.enable();
7467                 }
7468                 break;
7469             case 'title':
7470                 this.el.dom.title = msg;
7471                 break;
7472             case 'under':
7473                 if(!this.errorEl){
7474                     var elp = this.el.findParent('.x-form-element', 5, true);
7475                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7476                     this.errorEl.setWidth(elp.getWidth(true)-20);
7477                 }
7478                 this.errorEl.update(msg);
7479                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7480                 break;
7481             case 'side':
7482                 if(!this.errorIcon){
7483                     var elp = this.el.findParent('.x-form-element', 5, true);
7484                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7485                 }
7486                 this.alignErrorIcon();
7487                 this.errorIcon.dom.qtip = msg;
7488                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7489                 this.errorIcon.show();
7490                 this.on('resize', this.alignErrorIcon, this);
7491                 break;
7492             default:
7493                 var t = Roo.getDom(this.msgTarget);
7494                 t.innerHTML = msg;
7495                 t.style.display = this.msgDisplay;
7496                 break;
7497         }
7498         */
7499         this.fireEvent('invalid', this, msg);
7500     },
7501     // private
7502     SafariOnKeyDown : function(event)
7503     {
7504         // this is a workaround for a password hang bug on chrome/ webkit.
7505         
7506         var isSelectAll = false;
7507         
7508         if(this.inputEl().dom.selectionEnd > 0){
7509             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7510         }
7511         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7512             event.preventDefault();
7513             this.setValue('');
7514             return;
7515         }
7516         
7517         if(isSelectAll){ // backspace and delete key
7518             
7519             event.preventDefault();
7520             // this is very hacky as keydown always get's upper case.
7521             //
7522             var cc = String.fromCharCode(event.getCharCode());
7523             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7524             
7525         }
7526     },
7527     adjustWidth : function(tag, w){
7528         tag = tag.toLowerCase();
7529         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7530             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7531                 if(tag == 'input'){
7532                     return w + 2;
7533                 }
7534                 if(tag == 'textarea'){
7535                     return w-2;
7536                 }
7537             }else if(Roo.isOpera){
7538                 if(tag == 'input'){
7539                     return w + 2;
7540                 }
7541                 if(tag == 'textarea'){
7542                     return w-2;
7543                 }
7544             }
7545         }
7546         return w;
7547     }
7548     
7549 });
7550
7551  
7552 /*
7553  * - LGPL
7554  *
7555  * Input
7556  * 
7557  */
7558
7559 /**
7560  * @class Roo.bootstrap.TextArea
7561  * @extends Roo.bootstrap.Input
7562  * Bootstrap TextArea class
7563  * @cfg {Number} cols Specifies the visible width of a text area
7564  * @cfg {Number} rows Specifies the visible number of lines in a text area
7565  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7566  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7567  * @cfg {string} html text
7568  * 
7569  * @constructor
7570  * Create a new TextArea
7571  * @param {Object} config The config object
7572  */
7573
7574 Roo.bootstrap.TextArea = function(config){
7575     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7576    
7577 };
7578
7579 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7580      
7581     cols : false,
7582     rows : 5,
7583     readOnly : false,
7584     warp : 'soft',
7585     resize : false,
7586     value: false,
7587     html: false,
7588     
7589     getAutoCreate : function(){
7590         
7591         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7592         
7593         var id = Roo.id();
7594         
7595         var cfg = {};
7596         
7597         var input =  {
7598             tag: 'textarea',
7599             id : id,
7600             warp : this.warp,
7601             rows : this.rows,
7602             value : this.value || '',
7603             html: this.html || '',
7604             cls : 'form-control',
7605             placeholder : this.placeholder || '' 
7606             
7607         };
7608         
7609         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7610             input.maxLength = this.maxLength;
7611         }
7612         
7613         if(this.resize){
7614             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7615         }
7616         
7617         if(this.cols){
7618             input.cols = this.cols;
7619         }
7620         
7621         if (this.readOnly) {
7622             input.readonly = true;
7623         }
7624         
7625         if (this.name) {
7626             input.name = this.name;
7627         }
7628         
7629         if (this.size) {
7630             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7631         }
7632         
7633         var settings=this;
7634         ['xs','sm','md','lg'].map(function(size){
7635             if (settings[size]) {
7636                 cfg.cls += ' col-' + size + '-' + settings[size];
7637             }
7638         });
7639         
7640         var inputblock = input;
7641         
7642         if (this.before || this.after) {
7643             
7644             inputblock = {
7645                 cls : 'input-group',
7646                 cn :  [] 
7647             };
7648             if (this.before) {
7649                 inputblock.cn.push({
7650                     tag :'span',
7651                     cls : 'input-group-addon',
7652                     html : this.before
7653                 });
7654             }
7655             inputblock.cn.push(input);
7656             if (this.after) {
7657                 inputblock.cn.push({
7658                     tag :'span',
7659                     cls : 'input-group-addon',
7660                     html : this.after
7661                 });
7662             }
7663             
7664         }
7665         
7666         if (align ==='left' && this.fieldLabel.length) {
7667                 Roo.log("left and has label");
7668                 cfg.cn = [
7669                     
7670                     {
7671                         tag: 'label',
7672                         'for' :  id,
7673                         cls : 'control-label col-sm-' + this.labelWidth,
7674                         html : this.fieldLabel
7675                         
7676                     },
7677                     {
7678                         cls : "col-sm-" + (12 - this.labelWidth), 
7679                         cn: [
7680                             inputblock
7681                         ]
7682                     }
7683                     
7684                 ];
7685         } else if ( this.fieldLabel.length) {
7686                 Roo.log(" label");
7687                  cfg.cn = [
7688                    
7689                     {
7690                         tag: 'label',
7691                         //cls : 'input-group-addon',
7692                         html : this.fieldLabel
7693                         
7694                     },
7695                     
7696                     inputblock
7697                     
7698                 ];
7699
7700         } else {
7701             
7702                    Roo.log(" no label && no align");
7703                 cfg.cn = [
7704                     
7705                         inputblock
7706                     
7707                 ];
7708                 
7709                 
7710         }
7711         
7712         if (this.disabled) {
7713             input.disabled=true;
7714         }
7715         
7716         return cfg;
7717         
7718     },
7719     /**
7720      * return the real textarea element.
7721      */
7722     inputEl: function ()
7723     {
7724         return this.el.select('textarea.form-control',true).first();
7725     }
7726 });
7727
7728  
7729 /*
7730  * - LGPL
7731  *
7732  * trigger field - base class for combo..
7733  * 
7734  */
7735  
7736 /**
7737  * @class Roo.bootstrap.TriggerField
7738  * @extends Roo.bootstrap.Input
7739  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7740  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7741  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7742  * for which you can provide a custom implementation.  For example:
7743  * <pre><code>
7744 var trigger = new Roo.bootstrap.TriggerField();
7745 trigger.onTriggerClick = myTriggerFn;
7746 trigger.applyTo('my-field');
7747 </code></pre>
7748  *
7749  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7750  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7751  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7752  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7753  * @constructor
7754  * Create a new TriggerField.
7755  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7756  * to the base TextField)
7757  */
7758 Roo.bootstrap.TriggerField = function(config){
7759     this.mimicing = false;
7760     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7761 };
7762
7763 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7764     /**
7765      * @cfg {String} triggerClass A CSS class to apply to the trigger
7766      */
7767      /**
7768      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7769      */
7770     hideTrigger:false,
7771
7772     /** @cfg {Boolean} grow @hide */
7773     /** @cfg {Number} growMin @hide */
7774     /** @cfg {Number} growMax @hide */
7775
7776     /**
7777      * @hide 
7778      * @method
7779      */
7780     autoSize: Roo.emptyFn,
7781     // private
7782     monitorTab : true,
7783     // private
7784     deferHeight : true,
7785
7786     
7787     actionMode : 'wrap',
7788     
7789     
7790     
7791     getAutoCreate : function(){
7792        
7793         var align = this.labelAlign || this.parentLabelAlign();
7794         
7795         var id = Roo.id();
7796         
7797         var cfg = {
7798             cls: 'form-group' //input-group
7799         };
7800         
7801         
7802         var input =  {
7803             tag: 'input',
7804             id : id,
7805             type : this.inputType,
7806             cls : 'form-control',
7807             autocomplete: 'off',
7808             placeholder : this.placeholder || '' 
7809             
7810         };
7811         if (this.name) {
7812             input.name = this.name;
7813         }
7814         if (this.size) {
7815             input.cls += ' input-' + this.size;
7816         }
7817         
7818         if (this.disabled) {
7819             input.disabled=true;
7820         }
7821         
7822         var inputblock = input;
7823         
7824         if (this.before || this.after) {
7825             
7826             inputblock = {
7827                 cls : 'input-group',
7828                 cn :  [] 
7829             };
7830             if (this.before) {
7831                 inputblock.cn.push({
7832                     tag :'span',
7833                     cls : 'input-group-addon',
7834                     html : this.before
7835                 });
7836             }
7837             inputblock.cn.push(input);
7838             if (this.after) {
7839                 inputblock.cn.push({
7840                     tag :'span',
7841                     cls : 'input-group-addon',
7842                     html : this.after
7843                 });
7844             }
7845             
7846         };
7847         
7848         var box = {
7849             tag: 'div',
7850             cn: [
7851                 {
7852                     tag: 'input',
7853                     type : 'hidden',
7854                     cls: 'form-hidden-field'
7855                 },
7856                 inputblock
7857             ]
7858             
7859         };
7860         
7861         if(this.multiple){
7862             Roo.log('multiple');
7863             
7864             box = {
7865                 tag: 'div',
7866                 cn: [
7867                     {
7868                         tag: 'input',
7869                         type : 'hidden',
7870                         cls: 'form-hidden-field'
7871                     },
7872                     {
7873                         tag: 'ul',
7874                         cls: 'select2-choices',
7875                         cn:[
7876                             {
7877                                 tag: 'li',
7878                                 cls: 'select2-search-field',
7879                                 cn: [
7880
7881                                     inputblock
7882                                 ]
7883                             }
7884                         ]
7885                     }
7886                 ]
7887             }
7888         };
7889         
7890         var combobox = {
7891             cls: 'select2-container input-group',
7892             cn: [
7893                 box
7894 //                {
7895 //                    tag: 'ul',
7896 //                    cls: 'typeahead typeahead-long dropdown-menu',
7897 //                    style: 'display:none'
7898 //                }
7899             ]
7900         };
7901         
7902         if(!this.multiple && this.showToggleBtn){
7903             combobox.cn.push({
7904                 tag :'span',
7905                 cls : 'input-group-addon btn dropdown-toggle',
7906                 cn : [
7907                     {
7908                         tag: 'span',
7909                         cls: 'caret'
7910                     },
7911                     {
7912                         tag: 'span',
7913                         cls: 'combobox-clear',
7914                         cn  : [
7915                             {
7916                                 tag : 'i',
7917                                 cls: 'icon-remove'
7918                             }
7919                         ]
7920                     }
7921                 ]
7922
7923             })
7924         }
7925         
7926         if(this.multiple){
7927             combobox.cls += ' select2-container-multi';
7928         }
7929         
7930         if (align ==='left' && this.fieldLabel.length) {
7931             
7932                 Roo.log("left and has label");
7933                 cfg.cn = [
7934                     
7935                     {
7936                         tag: 'label',
7937                         'for' :  id,
7938                         cls : 'control-label col-sm-' + this.labelWidth,
7939                         html : this.fieldLabel
7940                         
7941                     },
7942                     {
7943                         cls : "col-sm-" + (12 - this.labelWidth), 
7944                         cn: [
7945                             combobox
7946                         ]
7947                     }
7948                     
7949                 ];
7950         } else if ( this.fieldLabel.length) {
7951                 Roo.log(" label");
7952                  cfg.cn = [
7953                    
7954                     {
7955                         tag: 'label',
7956                         //cls : 'input-group-addon',
7957                         html : this.fieldLabel
7958                         
7959                     },
7960                     
7961                     combobox
7962                     
7963                 ];
7964
7965         } else {
7966             
7967                 Roo.log(" no label && no align");
7968                 cfg = combobox
7969                      
7970                 
7971         }
7972          
7973         var settings=this;
7974         ['xs','sm','md','lg'].map(function(size){
7975             if (settings[size]) {
7976                 cfg.cls += ' col-' + size + '-' + settings[size];
7977             }
7978         });
7979         
7980         return cfg;
7981         
7982     },
7983     
7984     
7985     
7986     // private
7987     onResize : function(w, h){
7988 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7989 //        if(typeof w == 'number'){
7990 //            var x = w - this.trigger.getWidth();
7991 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7992 //            this.trigger.setStyle('left', x+'px');
7993 //        }
7994     },
7995
7996     // private
7997     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7998
7999     // private
8000     getResizeEl : function(){
8001         return this.inputEl();
8002     },
8003
8004     // private
8005     getPositionEl : function(){
8006         return this.inputEl();
8007     },
8008
8009     // private
8010     alignErrorIcon : function(){
8011         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8012     },
8013
8014     // private
8015     initEvents : function(){
8016         
8017         this.createList();
8018         
8019         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8020         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8021         if(!this.multiple && this.showToggleBtn){
8022             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8023             if(this.hideTrigger){
8024                 this.trigger.setDisplayed(false);
8025             }
8026             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8027         }
8028         
8029         if(this.multiple){
8030             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8031         }
8032         
8033         //this.trigger.addClassOnOver('x-form-trigger-over');
8034         //this.trigger.addClassOnClick('x-form-trigger-click');
8035         
8036         //if(!this.width){
8037         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8038         //}
8039     },
8040     
8041     createList : function()
8042     {
8043         this.list = Roo.get(document.body).createChild({
8044             tag: 'ul',
8045             cls: 'typeahead typeahead-long dropdown-menu',
8046             style: 'display:none'
8047         });
8048         
8049         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8050         
8051     },
8052
8053     // private
8054     initTrigger : function(){
8055        
8056     },
8057
8058     // private
8059     onDestroy : function(){
8060         if(this.trigger){
8061             this.trigger.removeAllListeners();
8062           //  this.trigger.remove();
8063         }
8064         //if(this.wrap){
8065         //    this.wrap.remove();
8066         //}
8067         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8068     },
8069
8070     // private
8071     onFocus : function(){
8072         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8073         /*
8074         if(!this.mimicing){
8075             this.wrap.addClass('x-trigger-wrap-focus');
8076             this.mimicing = true;
8077             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8078             if(this.monitorTab){
8079                 this.el.on("keydown", this.checkTab, this);
8080             }
8081         }
8082         */
8083     },
8084
8085     // private
8086     checkTab : function(e){
8087         if(e.getKey() == e.TAB){
8088             this.triggerBlur();
8089         }
8090     },
8091
8092     // private
8093     onBlur : function(){
8094         // do nothing
8095     },
8096
8097     // private
8098     mimicBlur : function(e, t){
8099         /*
8100         if(!this.wrap.contains(t) && this.validateBlur()){
8101             this.triggerBlur();
8102         }
8103         */
8104     },
8105
8106     // private
8107     triggerBlur : function(){
8108         this.mimicing = false;
8109         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8110         if(this.monitorTab){
8111             this.el.un("keydown", this.checkTab, this);
8112         }
8113         //this.wrap.removeClass('x-trigger-wrap-focus');
8114         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8115     },
8116
8117     // private
8118     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8119     validateBlur : function(e, t){
8120         return true;
8121     },
8122
8123     // private
8124     onDisable : function(){
8125         this.inputEl().dom.disabled = true;
8126         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8127         //if(this.wrap){
8128         //    this.wrap.addClass('x-item-disabled');
8129         //}
8130     },
8131
8132     // private
8133     onEnable : function(){
8134         this.inputEl().dom.disabled = false;
8135         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8136         //if(this.wrap){
8137         //    this.el.removeClass('x-item-disabled');
8138         //}
8139     },
8140
8141     // private
8142     onShow : function(){
8143         var ae = this.getActionEl();
8144         
8145         if(ae){
8146             ae.dom.style.display = '';
8147             ae.dom.style.visibility = 'visible';
8148         }
8149     },
8150
8151     // private
8152     
8153     onHide : function(){
8154         var ae = this.getActionEl();
8155         ae.dom.style.display = 'none';
8156     },
8157
8158     /**
8159      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8160      * by an implementing function.
8161      * @method
8162      * @param {EventObject} e
8163      */
8164     onTriggerClick : Roo.emptyFn
8165 });
8166  /*
8167  * Based on:
8168  * Ext JS Library 1.1.1
8169  * Copyright(c) 2006-2007, Ext JS, LLC.
8170  *
8171  * Originally Released Under LGPL - original licence link has changed is not relivant.
8172  *
8173  * Fork - LGPL
8174  * <script type="text/javascript">
8175  */
8176
8177
8178 /**
8179  * @class Roo.data.SortTypes
8180  * @singleton
8181  * Defines the default sorting (casting?) comparison functions used when sorting data.
8182  */
8183 Roo.data.SortTypes = {
8184     /**
8185      * Default sort that does nothing
8186      * @param {Mixed} s The value being converted
8187      * @return {Mixed} The comparison value
8188      */
8189     none : function(s){
8190         return s;
8191     },
8192     
8193     /**
8194      * The regular expression used to strip tags
8195      * @type {RegExp}
8196      * @property
8197      */
8198     stripTagsRE : /<\/?[^>]+>/gi,
8199     
8200     /**
8201      * Strips all HTML tags to sort on text only
8202      * @param {Mixed} s The value being converted
8203      * @return {String} The comparison value
8204      */
8205     asText : function(s){
8206         return String(s).replace(this.stripTagsRE, "");
8207     },
8208     
8209     /**
8210      * Strips all HTML tags to sort on text only - Case insensitive
8211      * @param {Mixed} s The value being converted
8212      * @return {String} The comparison value
8213      */
8214     asUCText : function(s){
8215         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8216     },
8217     
8218     /**
8219      * Case insensitive string
8220      * @param {Mixed} s The value being converted
8221      * @return {String} The comparison value
8222      */
8223     asUCString : function(s) {
8224         return String(s).toUpperCase();
8225     },
8226     
8227     /**
8228      * Date sorting
8229      * @param {Mixed} s The value being converted
8230      * @return {Number} The comparison value
8231      */
8232     asDate : function(s) {
8233         if(!s){
8234             return 0;
8235         }
8236         if(s instanceof Date){
8237             return s.getTime();
8238         }
8239         return Date.parse(String(s));
8240     },
8241     
8242     /**
8243      * Float sorting
8244      * @param {Mixed} s The value being converted
8245      * @return {Float} The comparison value
8246      */
8247     asFloat : function(s) {
8248         var val = parseFloat(String(s).replace(/,/g, ""));
8249         if(isNaN(val)) val = 0;
8250         return val;
8251     },
8252     
8253     /**
8254      * Integer sorting
8255      * @param {Mixed} s The value being converted
8256      * @return {Number} The comparison value
8257      */
8258     asInt : function(s) {
8259         var val = parseInt(String(s).replace(/,/g, ""));
8260         if(isNaN(val)) val = 0;
8261         return val;
8262     }
8263 };/*
8264  * Based on:
8265  * Ext JS Library 1.1.1
8266  * Copyright(c) 2006-2007, Ext JS, LLC.
8267  *
8268  * Originally Released Under LGPL - original licence link has changed is not relivant.
8269  *
8270  * Fork - LGPL
8271  * <script type="text/javascript">
8272  */
8273
8274 /**
8275 * @class Roo.data.Record
8276  * Instances of this class encapsulate both record <em>definition</em> information, and record
8277  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8278  * to access Records cached in an {@link Roo.data.Store} object.<br>
8279  * <p>
8280  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8281  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8282  * objects.<br>
8283  * <p>
8284  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8285  * @constructor
8286  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8287  * {@link #create}. The parameters are the same.
8288  * @param {Array} data An associative Array of data values keyed by the field name.
8289  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8290  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8291  * not specified an integer id is generated.
8292  */
8293 Roo.data.Record = function(data, id){
8294     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8295     this.data = data;
8296 };
8297
8298 /**
8299  * Generate a constructor for a specific record layout.
8300  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8301  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8302  * Each field definition object may contain the following properties: <ul>
8303  * <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,
8304  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8305  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8306  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8307  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8308  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8309  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8310  * this may be omitted.</p></li>
8311  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8312  * <ul><li>auto (Default, implies no conversion)</li>
8313  * <li>string</li>
8314  * <li>int</li>
8315  * <li>float</li>
8316  * <li>boolean</li>
8317  * <li>date</li></ul></p></li>
8318  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8319  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8320  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8321  * by the Reader into an object that will be stored in the Record. It is passed the
8322  * following parameters:<ul>
8323  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8324  * </ul></p></li>
8325  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8326  * </ul>
8327  * <br>usage:<br><pre><code>
8328 var TopicRecord = Roo.data.Record.create(
8329     {name: 'title', mapping: 'topic_title'},
8330     {name: 'author', mapping: 'username'},
8331     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8332     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8333     {name: 'lastPoster', mapping: 'user2'},
8334     {name: 'excerpt', mapping: 'post_text'}
8335 );
8336
8337 var myNewRecord = new TopicRecord({
8338     title: 'Do my job please',
8339     author: 'noobie',
8340     totalPosts: 1,
8341     lastPost: new Date(),
8342     lastPoster: 'Animal',
8343     excerpt: 'No way dude!'
8344 });
8345 myStore.add(myNewRecord);
8346 </code></pre>
8347  * @method create
8348  * @static
8349  */
8350 Roo.data.Record.create = function(o){
8351     var f = function(){
8352         f.superclass.constructor.apply(this, arguments);
8353     };
8354     Roo.extend(f, Roo.data.Record);
8355     var p = f.prototype;
8356     p.fields = new Roo.util.MixedCollection(false, function(field){
8357         return field.name;
8358     });
8359     for(var i = 0, len = o.length; i < len; i++){
8360         p.fields.add(new Roo.data.Field(o[i]));
8361     }
8362     f.getField = function(name){
8363         return p.fields.get(name);  
8364     };
8365     return f;
8366 };
8367
8368 Roo.data.Record.AUTO_ID = 1000;
8369 Roo.data.Record.EDIT = 'edit';
8370 Roo.data.Record.REJECT = 'reject';
8371 Roo.data.Record.COMMIT = 'commit';
8372
8373 Roo.data.Record.prototype = {
8374     /**
8375      * Readonly flag - true if this record has been modified.
8376      * @type Boolean
8377      */
8378     dirty : false,
8379     editing : false,
8380     error: null,
8381     modified: null,
8382
8383     // private
8384     join : function(store){
8385         this.store = store;
8386     },
8387
8388     /**
8389      * Set the named field to the specified value.
8390      * @param {String} name The name of the field to set.
8391      * @param {Object} value The value to set the field to.
8392      */
8393     set : function(name, value){
8394         if(this.data[name] == value){
8395             return;
8396         }
8397         this.dirty = true;
8398         if(!this.modified){
8399             this.modified = {};
8400         }
8401         if(typeof this.modified[name] == 'undefined'){
8402             this.modified[name] = this.data[name];
8403         }
8404         this.data[name] = value;
8405         if(!this.editing && this.store){
8406             this.store.afterEdit(this);
8407         }       
8408     },
8409
8410     /**
8411      * Get the value of the named field.
8412      * @param {String} name The name of the field to get the value of.
8413      * @return {Object} The value of the field.
8414      */
8415     get : function(name){
8416         return this.data[name]; 
8417     },
8418
8419     // private
8420     beginEdit : function(){
8421         this.editing = true;
8422         this.modified = {}; 
8423     },
8424
8425     // private
8426     cancelEdit : function(){
8427         this.editing = false;
8428         delete this.modified;
8429     },
8430
8431     // private
8432     endEdit : function(){
8433         this.editing = false;
8434         if(this.dirty && this.store){
8435             this.store.afterEdit(this);
8436         }
8437     },
8438
8439     /**
8440      * Usually called by the {@link Roo.data.Store} which owns the Record.
8441      * Rejects all changes made to the Record since either creation, or the last commit operation.
8442      * Modified fields are reverted to their original values.
8443      * <p>
8444      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8445      * of reject operations.
8446      */
8447     reject : function(){
8448         var m = this.modified;
8449         for(var n in m){
8450             if(typeof m[n] != "function"){
8451                 this.data[n] = m[n];
8452             }
8453         }
8454         this.dirty = false;
8455         delete this.modified;
8456         this.editing = false;
8457         if(this.store){
8458             this.store.afterReject(this);
8459         }
8460     },
8461
8462     /**
8463      * Usually called by the {@link Roo.data.Store} which owns the Record.
8464      * Commits all changes made to the Record since either creation, or the last commit operation.
8465      * <p>
8466      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8467      * of commit operations.
8468      */
8469     commit : function(){
8470         this.dirty = false;
8471         delete this.modified;
8472         this.editing = false;
8473         if(this.store){
8474             this.store.afterCommit(this);
8475         }
8476     },
8477
8478     // private
8479     hasError : function(){
8480         return this.error != null;
8481     },
8482
8483     // private
8484     clearError : function(){
8485         this.error = null;
8486     },
8487
8488     /**
8489      * Creates a copy of this record.
8490      * @param {String} id (optional) A new record id if you don't want to use this record's id
8491      * @return {Record}
8492      */
8493     copy : function(newId) {
8494         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8495     }
8496 };/*
8497  * Based on:
8498  * Ext JS Library 1.1.1
8499  * Copyright(c) 2006-2007, Ext JS, LLC.
8500  *
8501  * Originally Released Under LGPL - original licence link has changed is not relivant.
8502  *
8503  * Fork - LGPL
8504  * <script type="text/javascript">
8505  */
8506
8507
8508
8509 /**
8510  * @class Roo.data.Store
8511  * @extends Roo.util.Observable
8512  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8513  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8514  * <p>
8515  * 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
8516  * has no knowledge of the format of the data returned by the Proxy.<br>
8517  * <p>
8518  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8519  * instances from the data object. These records are cached and made available through accessor functions.
8520  * @constructor
8521  * Creates a new Store.
8522  * @param {Object} config A config object containing the objects needed for the Store to access data,
8523  * and read the data into Records.
8524  */
8525 Roo.data.Store = function(config){
8526     this.data = new Roo.util.MixedCollection(false);
8527     this.data.getKey = function(o){
8528         return o.id;
8529     };
8530     this.baseParams = {};
8531     // private
8532     this.paramNames = {
8533         "start" : "start",
8534         "limit" : "limit",
8535         "sort" : "sort",
8536         "dir" : "dir",
8537         "multisort" : "_multisort"
8538     };
8539
8540     if(config && config.data){
8541         this.inlineData = config.data;
8542         delete config.data;
8543     }
8544
8545     Roo.apply(this, config);
8546     
8547     if(this.reader){ // reader passed
8548         this.reader = Roo.factory(this.reader, Roo.data);
8549         this.reader.xmodule = this.xmodule || false;
8550         if(!this.recordType){
8551             this.recordType = this.reader.recordType;
8552         }
8553         if(this.reader.onMetaChange){
8554             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8555         }
8556     }
8557
8558     if(this.recordType){
8559         this.fields = this.recordType.prototype.fields;
8560     }
8561     this.modified = [];
8562
8563     this.addEvents({
8564         /**
8565          * @event datachanged
8566          * Fires when the data cache has changed, and a widget which is using this Store
8567          * as a Record cache should refresh its view.
8568          * @param {Store} this
8569          */
8570         datachanged : true,
8571         /**
8572          * @event metachange
8573          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8574          * @param {Store} this
8575          * @param {Object} meta The JSON metadata
8576          */
8577         metachange : true,
8578         /**
8579          * @event add
8580          * Fires when Records have been added to the Store
8581          * @param {Store} this
8582          * @param {Roo.data.Record[]} records The array of Records added
8583          * @param {Number} index The index at which the record(s) were added
8584          */
8585         add : true,
8586         /**
8587          * @event remove
8588          * Fires when a Record has been removed from the Store
8589          * @param {Store} this
8590          * @param {Roo.data.Record} record The Record that was removed
8591          * @param {Number} index The index at which the record was removed
8592          */
8593         remove : true,
8594         /**
8595          * @event update
8596          * Fires when a Record has been updated
8597          * @param {Store} this
8598          * @param {Roo.data.Record} record The Record that was updated
8599          * @param {String} operation The update operation being performed.  Value may be one of:
8600          * <pre><code>
8601  Roo.data.Record.EDIT
8602  Roo.data.Record.REJECT
8603  Roo.data.Record.COMMIT
8604          * </code></pre>
8605          */
8606         update : true,
8607         /**
8608          * @event clear
8609          * Fires when the data cache has been cleared.
8610          * @param {Store} this
8611          */
8612         clear : true,
8613         /**
8614          * @event beforeload
8615          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8616          * the load action will be canceled.
8617          * @param {Store} this
8618          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8619          */
8620         beforeload : true,
8621         /**
8622          * @event beforeloadadd
8623          * Fires after a new set of Records has been loaded.
8624          * @param {Store} this
8625          * @param {Roo.data.Record[]} records The Records that were loaded
8626          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8627          */
8628         beforeloadadd : true,
8629         /**
8630          * @event load
8631          * Fires after a new set of Records has been loaded, before they are added to the store.
8632          * @param {Store} this
8633          * @param {Roo.data.Record[]} records The Records that were loaded
8634          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8635          * @params {Object} return from reader
8636          */
8637         load : true,
8638         /**
8639          * @event loadexception
8640          * Fires if an exception occurs in the Proxy during loading.
8641          * Called with the signature of the Proxy's "loadexception" event.
8642          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8643          * 
8644          * @param {Proxy} 
8645          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8646          * @param {Object} load options 
8647          * @param {Object} jsonData from your request (normally this contains the Exception)
8648          */
8649         loadexception : true
8650     });
8651     
8652     if(this.proxy){
8653         this.proxy = Roo.factory(this.proxy, Roo.data);
8654         this.proxy.xmodule = this.xmodule || false;
8655         this.relayEvents(this.proxy,  ["loadexception"]);
8656     }
8657     this.sortToggle = {};
8658     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8659
8660     Roo.data.Store.superclass.constructor.call(this);
8661
8662     if(this.inlineData){
8663         this.loadData(this.inlineData);
8664         delete this.inlineData;
8665     }
8666 };
8667
8668 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8669      /**
8670     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8671     * without a remote query - used by combo/forms at present.
8672     */
8673     
8674     /**
8675     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8676     */
8677     /**
8678     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8679     */
8680     /**
8681     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8682     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8683     */
8684     /**
8685     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8686     * on any HTTP request
8687     */
8688     /**
8689     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8690     */
8691     /**
8692     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8693     */
8694     multiSort: false,
8695     /**
8696     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8697     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8698     */
8699     remoteSort : false,
8700
8701     /**
8702     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8703      * loaded or when a record is removed. (defaults to false).
8704     */
8705     pruneModifiedRecords : false,
8706
8707     // private
8708     lastOptions : null,
8709
8710     /**
8711      * Add Records to the Store and fires the add event.
8712      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8713      */
8714     add : function(records){
8715         records = [].concat(records);
8716         for(var i = 0, len = records.length; i < len; i++){
8717             records[i].join(this);
8718         }
8719         var index = this.data.length;
8720         this.data.addAll(records);
8721         this.fireEvent("add", this, records, index);
8722     },
8723
8724     /**
8725      * Remove a Record from the Store and fires the remove event.
8726      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8727      */
8728     remove : function(record){
8729         var index = this.data.indexOf(record);
8730         this.data.removeAt(index);
8731         if(this.pruneModifiedRecords){
8732             this.modified.remove(record);
8733         }
8734         this.fireEvent("remove", this, record, index);
8735     },
8736
8737     /**
8738      * Remove all Records from the Store and fires the clear event.
8739      */
8740     removeAll : function(){
8741         this.data.clear();
8742         if(this.pruneModifiedRecords){
8743             this.modified = [];
8744         }
8745         this.fireEvent("clear", this);
8746     },
8747
8748     /**
8749      * Inserts Records to the Store at the given index and fires the add event.
8750      * @param {Number} index The start index at which to insert the passed Records.
8751      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8752      */
8753     insert : function(index, records){
8754         records = [].concat(records);
8755         for(var i = 0, len = records.length; i < len; i++){
8756             this.data.insert(index, records[i]);
8757             records[i].join(this);
8758         }
8759         this.fireEvent("add", this, records, index);
8760     },
8761
8762     /**
8763      * Get the index within the cache of the passed Record.
8764      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8765      * @return {Number} The index of the passed Record. Returns -1 if not found.
8766      */
8767     indexOf : function(record){
8768         return this.data.indexOf(record);
8769     },
8770
8771     /**
8772      * Get the index within the cache of the Record with the passed id.
8773      * @param {String} id The id of the Record to find.
8774      * @return {Number} The index of the Record. Returns -1 if not found.
8775      */
8776     indexOfId : function(id){
8777         return this.data.indexOfKey(id);
8778     },
8779
8780     /**
8781      * Get the Record with the specified id.
8782      * @param {String} id The id of the Record to find.
8783      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8784      */
8785     getById : function(id){
8786         return this.data.key(id);
8787     },
8788
8789     /**
8790      * Get the Record at the specified index.
8791      * @param {Number} index The index of the Record to find.
8792      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8793      */
8794     getAt : function(index){
8795         return this.data.itemAt(index);
8796     },
8797
8798     /**
8799      * Returns a range of Records between specified indices.
8800      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8801      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8802      * @return {Roo.data.Record[]} An array of Records
8803      */
8804     getRange : function(start, end){
8805         return this.data.getRange(start, end);
8806     },
8807
8808     // private
8809     storeOptions : function(o){
8810         o = Roo.apply({}, o);
8811         delete o.callback;
8812         delete o.scope;
8813         this.lastOptions = o;
8814     },
8815
8816     /**
8817      * Loads the Record cache from the configured Proxy using the configured Reader.
8818      * <p>
8819      * If using remote paging, then the first load call must specify the <em>start</em>
8820      * and <em>limit</em> properties in the options.params property to establish the initial
8821      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8822      * <p>
8823      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8824      * and this call will return before the new data has been loaded. Perform any post-processing
8825      * in a callback function, or in a "load" event handler.</strong>
8826      * <p>
8827      * @param {Object} options An object containing properties which control loading options:<ul>
8828      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8829      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8830      * passed the following arguments:<ul>
8831      * <li>r : Roo.data.Record[]</li>
8832      * <li>options: Options object from the load call</li>
8833      * <li>success: Boolean success indicator</li></ul></li>
8834      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8835      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8836      * </ul>
8837      */
8838     load : function(options){
8839         options = options || {};
8840         if(this.fireEvent("beforeload", this, options) !== false){
8841             this.storeOptions(options);
8842             var p = Roo.apply(options.params || {}, this.baseParams);
8843             // if meta was not loaded from remote source.. try requesting it.
8844             if (!this.reader.metaFromRemote) {
8845                 p._requestMeta = 1;
8846             }
8847             if(this.sortInfo && this.remoteSort){
8848                 var pn = this.paramNames;
8849                 p[pn["sort"]] = this.sortInfo.field;
8850                 p[pn["dir"]] = this.sortInfo.direction;
8851             }
8852             if (this.multiSort) {
8853                 var pn = this.paramNames;
8854                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8855             }
8856             
8857             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8858         }
8859     },
8860
8861     /**
8862      * Reloads the Record cache from the configured Proxy using the configured Reader and
8863      * the options from the last load operation performed.
8864      * @param {Object} options (optional) An object containing properties which may override the options
8865      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8866      * the most recently used options are reused).
8867      */
8868     reload : function(options){
8869         this.load(Roo.applyIf(options||{}, this.lastOptions));
8870     },
8871
8872     // private
8873     // Called as a callback by the Reader during a load operation.
8874     loadRecords : function(o, options, success){
8875         if(!o || success === false){
8876             if(success !== false){
8877                 this.fireEvent("load", this, [], options, o);
8878             }
8879             if(options.callback){
8880                 options.callback.call(options.scope || this, [], options, false);
8881             }
8882             return;
8883         }
8884         // if data returned failure - throw an exception.
8885         if (o.success === false) {
8886             // show a message if no listener is registered.
8887             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8888                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8889             }
8890             // loadmask wil be hooked into this..
8891             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8892             return;
8893         }
8894         var r = o.records, t = o.totalRecords || r.length;
8895         
8896         this.fireEvent("beforeloadadd", this, r, options, o);
8897         
8898         if(!options || options.add !== true){
8899             if(this.pruneModifiedRecords){
8900                 this.modified = [];
8901             }
8902             for(var i = 0, len = r.length; i < len; i++){
8903                 r[i].join(this);
8904             }
8905             if(this.snapshot){
8906                 this.data = this.snapshot;
8907                 delete this.snapshot;
8908             }
8909             this.data.clear();
8910             this.data.addAll(r);
8911             this.totalLength = t;
8912             this.applySort();
8913             this.fireEvent("datachanged", this);
8914         }else{
8915             this.totalLength = Math.max(t, this.data.length+r.length);
8916             this.add(r);
8917         }
8918         this.fireEvent("load", this, r, options, o);
8919         if(options.callback){
8920             options.callback.call(options.scope || this, r, options, true);
8921         }
8922     },
8923
8924
8925     /**
8926      * Loads data from a passed data block. A Reader which understands the format of the data
8927      * must have been configured in the constructor.
8928      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8929      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8930      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8931      */
8932     loadData : function(o, append){
8933         var r = this.reader.readRecords(o);
8934         this.loadRecords(r, {add: append}, true);
8935     },
8936
8937     /**
8938      * Gets the number of cached records.
8939      * <p>
8940      * <em>If using paging, this may not be the total size of the dataset. If the data object
8941      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8942      * the data set size</em>
8943      */
8944     getCount : function(){
8945         return this.data.length || 0;
8946     },
8947
8948     /**
8949      * Gets the total number of records in the dataset as returned by the server.
8950      * <p>
8951      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8952      * the dataset size</em>
8953      */
8954     getTotalCount : function(){
8955         return this.totalLength || 0;
8956     },
8957
8958     /**
8959      * Returns the sort state of the Store as an object with two properties:
8960      * <pre><code>
8961  field {String} The name of the field by which the Records are sorted
8962  direction {String} The sort order, "ASC" or "DESC"
8963      * </code></pre>
8964      */
8965     getSortState : function(){
8966         return this.sortInfo;
8967     },
8968
8969     // private
8970     applySort : function(){
8971         if(this.sortInfo && !this.remoteSort){
8972             var s = this.sortInfo, f = s.field;
8973             var st = this.fields.get(f).sortType;
8974             var fn = function(r1, r2){
8975                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8976                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8977             };
8978             this.data.sort(s.direction, fn);
8979             if(this.snapshot && this.snapshot != this.data){
8980                 this.snapshot.sort(s.direction, fn);
8981             }
8982         }
8983     },
8984
8985     /**
8986      * Sets the default sort column and order to be used by the next load operation.
8987      * @param {String} fieldName The name of the field to sort by.
8988      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8989      */
8990     setDefaultSort : function(field, dir){
8991         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8992     },
8993
8994     /**
8995      * Sort the Records.
8996      * If remote sorting is used, the sort is performed on the server, and the cache is
8997      * reloaded. If local sorting is used, the cache is sorted internally.
8998      * @param {String} fieldName The name of the field to sort by.
8999      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9000      */
9001     sort : function(fieldName, dir){
9002         var f = this.fields.get(fieldName);
9003         if(!dir){
9004             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9005             
9006             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9007                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9008             }else{
9009                 dir = f.sortDir;
9010             }
9011         }
9012         this.sortToggle[f.name] = dir;
9013         this.sortInfo = {field: f.name, direction: dir};
9014         if(!this.remoteSort){
9015             this.applySort();
9016             this.fireEvent("datachanged", this);
9017         }else{
9018             this.load(this.lastOptions);
9019         }
9020     },
9021
9022     /**
9023      * Calls the specified function for each of the Records in the cache.
9024      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9025      * Returning <em>false</em> aborts and exits the iteration.
9026      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9027      */
9028     each : function(fn, scope){
9029         this.data.each(fn, scope);
9030     },
9031
9032     /**
9033      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9034      * (e.g., during paging).
9035      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9036      */
9037     getModifiedRecords : function(){
9038         return this.modified;
9039     },
9040
9041     // private
9042     createFilterFn : function(property, value, anyMatch){
9043         if(!value.exec){ // not a regex
9044             value = String(value);
9045             if(value.length == 0){
9046                 return false;
9047             }
9048             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9049         }
9050         return function(r){
9051             return value.test(r.data[property]);
9052         };
9053     },
9054
9055     /**
9056      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9057      * @param {String} property A field on your records
9058      * @param {Number} start The record index to start at (defaults to 0)
9059      * @param {Number} end The last record index to include (defaults to length - 1)
9060      * @return {Number} The sum
9061      */
9062     sum : function(property, start, end){
9063         var rs = this.data.items, v = 0;
9064         start = start || 0;
9065         end = (end || end === 0) ? end : rs.length-1;
9066
9067         for(var i = start; i <= end; i++){
9068             v += (rs[i].data[property] || 0);
9069         }
9070         return v;
9071     },
9072
9073     /**
9074      * Filter the records by a specified property.
9075      * @param {String} field A field on your records
9076      * @param {String/RegExp} value Either a string that the field
9077      * should start with or a RegExp to test against the field
9078      * @param {Boolean} anyMatch True to match any part not just the beginning
9079      */
9080     filter : function(property, value, anyMatch){
9081         var fn = this.createFilterFn(property, value, anyMatch);
9082         return fn ? this.filterBy(fn) : this.clearFilter();
9083     },
9084
9085     /**
9086      * Filter by a function. The specified function will be called with each
9087      * record in this data source. If the function returns true the record is included,
9088      * otherwise it is filtered.
9089      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9090      * @param {Object} scope (optional) The scope of the function (defaults to this)
9091      */
9092     filterBy : function(fn, scope){
9093         this.snapshot = this.snapshot || this.data;
9094         this.data = this.queryBy(fn, scope||this);
9095         this.fireEvent("datachanged", this);
9096     },
9097
9098     /**
9099      * Query the records by a specified property.
9100      * @param {String} field A field on your records
9101      * @param {String/RegExp} value Either a string that the field
9102      * should start with or a RegExp to test against the field
9103      * @param {Boolean} anyMatch True to match any part not just the beginning
9104      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9105      */
9106     query : function(property, value, anyMatch){
9107         var fn = this.createFilterFn(property, value, anyMatch);
9108         return fn ? this.queryBy(fn) : this.data.clone();
9109     },
9110
9111     /**
9112      * Query by a function. The specified function will be called with each
9113      * record in this data source. If the function returns true the record is included
9114      * in the results.
9115      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9116      * @param {Object} scope (optional) The scope of the function (defaults to this)
9117       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9118      **/
9119     queryBy : function(fn, scope){
9120         var data = this.snapshot || this.data;
9121         return data.filterBy(fn, scope||this);
9122     },
9123
9124     /**
9125      * Collects unique values for a particular dataIndex from this store.
9126      * @param {String} dataIndex The property to collect
9127      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9128      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9129      * @return {Array} An array of the unique values
9130      **/
9131     collect : function(dataIndex, allowNull, bypassFilter){
9132         var d = (bypassFilter === true && this.snapshot) ?
9133                 this.snapshot.items : this.data.items;
9134         var v, sv, r = [], l = {};
9135         for(var i = 0, len = d.length; i < len; i++){
9136             v = d[i].data[dataIndex];
9137             sv = String(v);
9138             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9139                 l[sv] = true;
9140                 r[r.length] = v;
9141             }
9142         }
9143         return r;
9144     },
9145
9146     /**
9147      * Revert to a view of the Record cache with no filtering applied.
9148      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9149      */
9150     clearFilter : function(suppressEvent){
9151         if(this.snapshot && this.snapshot != this.data){
9152             this.data = this.snapshot;
9153             delete this.snapshot;
9154             if(suppressEvent !== true){
9155                 this.fireEvent("datachanged", this);
9156             }
9157         }
9158     },
9159
9160     // private
9161     afterEdit : function(record){
9162         if(this.modified.indexOf(record) == -1){
9163             this.modified.push(record);
9164         }
9165         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9166     },
9167     
9168     // private
9169     afterReject : function(record){
9170         this.modified.remove(record);
9171         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9172     },
9173
9174     // private
9175     afterCommit : function(record){
9176         this.modified.remove(record);
9177         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9178     },
9179
9180     /**
9181      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9182      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9183      */
9184     commitChanges : function(){
9185         var m = this.modified.slice(0);
9186         this.modified = [];
9187         for(var i = 0, len = m.length; i < len; i++){
9188             m[i].commit();
9189         }
9190     },
9191
9192     /**
9193      * Cancel outstanding changes on all changed records.
9194      */
9195     rejectChanges : function(){
9196         var m = this.modified.slice(0);
9197         this.modified = [];
9198         for(var i = 0, len = m.length; i < len; i++){
9199             m[i].reject();
9200         }
9201     },
9202
9203     onMetaChange : function(meta, rtype, o){
9204         this.recordType = rtype;
9205         this.fields = rtype.prototype.fields;
9206         delete this.snapshot;
9207         this.sortInfo = meta.sortInfo || this.sortInfo;
9208         this.modified = [];
9209         this.fireEvent('metachange', this, this.reader.meta);
9210     },
9211     
9212     moveIndex : function(data, type)
9213     {
9214         var index = this.indexOf(data);
9215         
9216         var newIndex = index + type;
9217         
9218         this.remove(data);
9219         
9220         this.insert(newIndex, data);
9221         
9222     }
9223 });/*
9224  * Based on:
9225  * Ext JS Library 1.1.1
9226  * Copyright(c) 2006-2007, Ext JS, LLC.
9227  *
9228  * Originally Released Under LGPL - original licence link has changed is not relivant.
9229  *
9230  * Fork - LGPL
9231  * <script type="text/javascript">
9232  */
9233
9234 /**
9235  * @class Roo.data.SimpleStore
9236  * @extends Roo.data.Store
9237  * Small helper class to make creating Stores from Array data easier.
9238  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9239  * @cfg {Array} fields An array of field definition objects, or field name strings.
9240  * @cfg {Array} data The multi-dimensional array of data
9241  * @constructor
9242  * @param {Object} config
9243  */
9244 Roo.data.SimpleStore = function(config){
9245     Roo.data.SimpleStore.superclass.constructor.call(this, {
9246         isLocal : true,
9247         reader: new Roo.data.ArrayReader({
9248                 id: config.id
9249             },
9250             Roo.data.Record.create(config.fields)
9251         ),
9252         proxy : new Roo.data.MemoryProxy(config.data)
9253     });
9254     this.load();
9255 };
9256 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9257  * Based on:
9258  * Ext JS Library 1.1.1
9259  * Copyright(c) 2006-2007, Ext JS, LLC.
9260  *
9261  * Originally Released Under LGPL - original licence link has changed is not relivant.
9262  *
9263  * Fork - LGPL
9264  * <script type="text/javascript">
9265  */
9266
9267 /**
9268 /**
9269  * @extends Roo.data.Store
9270  * @class Roo.data.JsonStore
9271  * Small helper class to make creating Stores for JSON data easier. <br/>
9272 <pre><code>
9273 var store = new Roo.data.JsonStore({
9274     url: 'get-images.php',
9275     root: 'images',
9276     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9277 });
9278 </code></pre>
9279  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9280  * JsonReader and HttpProxy (unless inline data is provided).</b>
9281  * @cfg {Array} fields An array of field definition objects, or field name strings.
9282  * @constructor
9283  * @param {Object} config
9284  */
9285 Roo.data.JsonStore = function(c){
9286     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9287         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9288         reader: new Roo.data.JsonReader(c, c.fields)
9289     }));
9290 };
9291 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9292  * Based on:
9293  * Ext JS Library 1.1.1
9294  * Copyright(c) 2006-2007, Ext JS, LLC.
9295  *
9296  * Originally Released Under LGPL - original licence link has changed is not relivant.
9297  *
9298  * Fork - LGPL
9299  * <script type="text/javascript">
9300  */
9301
9302  
9303 Roo.data.Field = function(config){
9304     if(typeof config == "string"){
9305         config = {name: config};
9306     }
9307     Roo.apply(this, config);
9308     
9309     if(!this.type){
9310         this.type = "auto";
9311     }
9312     
9313     var st = Roo.data.SortTypes;
9314     // named sortTypes are supported, here we look them up
9315     if(typeof this.sortType == "string"){
9316         this.sortType = st[this.sortType];
9317     }
9318     
9319     // set default sortType for strings and dates
9320     if(!this.sortType){
9321         switch(this.type){
9322             case "string":
9323                 this.sortType = st.asUCString;
9324                 break;
9325             case "date":
9326                 this.sortType = st.asDate;
9327                 break;
9328             default:
9329                 this.sortType = st.none;
9330         }
9331     }
9332
9333     // define once
9334     var stripRe = /[\$,%]/g;
9335
9336     // prebuilt conversion function for this field, instead of
9337     // switching every time we're reading a value
9338     if(!this.convert){
9339         var cv, dateFormat = this.dateFormat;
9340         switch(this.type){
9341             case "":
9342             case "auto":
9343             case undefined:
9344                 cv = function(v){ return v; };
9345                 break;
9346             case "string":
9347                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9348                 break;
9349             case "int":
9350                 cv = function(v){
9351                     return v !== undefined && v !== null && v !== '' ?
9352                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9353                     };
9354                 break;
9355             case "float":
9356                 cv = function(v){
9357                     return v !== undefined && v !== null && v !== '' ?
9358                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9359                     };
9360                 break;
9361             case "bool":
9362             case "boolean":
9363                 cv = function(v){ return v === true || v === "true" || v == 1; };
9364                 break;
9365             case "date":
9366                 cv = function(v){
9367                     if(!v){
9368                         return '';
9369                     }
9370                     if(v instanceof Date){
9371                         return v;
9372                     }
9373                     if(dateFormat){
9374                         if(dateFormat == "timestamp"){
9375                             return new Date(v*1000);
9376                         }
9377                         return Date.parseDate(v, dateFormat);
9378                     }
9379                     var parsed = Date.parse(v);
9380                     return parsed ? new Date(parsed) : null;
9381                 };
9382              break;
9383             
9384         }
9385         this.convert = cv;
9386     }
9387 };
9388
9389 Roo.data.Field.prototype = {
9390     dateFormat: null,
9391     defaultValue: "",
9392     mapping: null,
9393     sortType : null,
9394     sortDir : "ASC"
9395 };/*
9396  * Based on:
9397  * Ext JS Library 1.1.1
9398  * Copyright(c) 2006-2007, Ext JS, LLC.
9399  *
9400  * Originally Released Under LGPL - original licence link has changed is not relivant.
9401  *
9402  * Fork - LGPL
9403  * <script type="text/javascript">
9404  */
9405  
9406 // Base class for reading structured data from a data source.  This class is intended to be
9407 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9408
9409 /**
9410  * @class Roo.data.DataReader
9411  * Base class for reading structured data from a data source.  This class is intended to be
9412  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9413  */
9414
9415 Roo.data.DataReader = function(meta, recordType){
9416     
9417     this.meta = meta;
9418     
9419     this.recordType = recordType instanceof Array ? 
9420         Roo.data.Record.create(recordType) : recordType;
9421 };
9422
9423 Roo.data.DataReader.prototype = {
9424      /**
9425      * Create an empty record
9426      * @param {Object} data (optional) - overlay some values
9427      * @return {Roo.data.Record} record created.
9428      */
9429     newRow :  function(d) {
9430         var da =  {};
9431         this.recordType.prototype.fields.each(function(c) {
9432             switch( c.type) {
9433                 case 'int' : da[c.name] = 0; break;
9434                 case 'date' : da[c.name] = new Date(); break;
9435                 case 'float' : da[c.name] = 0.0; break;
9436                 case 'boolean' : da[c.name] = false; break;
9437                 default : da[c.name] = ""; break;
9438             }
9439             
9440         });
9441         return new this.recordType(Roo.apply(da, d));
9442     }
9443     
9444 };/*
9445  * Based on:
9446  * Ext JS Library 1.1.1
9447  * Copyright(c) 2006-2007, Ext JS, LLC.
9448  *
9449  * Originally Released Under LGPL - original licence link has changed is not relivant.
9450  *
9451  * Fork - LGPL
9452  * <script type="text/javascript">
9453  */
9454
9455 /**
9456  * @class Roo.data.DataProxy
9457  * @extends Roo.data.Observable
9458  * This class is an abstract base class for implementations which provide retrieval of
9459  * unformatted data objects.<br>
9460  * <p>
9461  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9462  * (of the appropriate type which knows how to parse the data object) to provide a block of
9463  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9464  * <p>
9465  * Custom implementations must implement the load method as described in
9466  * {@link Roo.data.HttpProxy#load}.
9467  */
9468 Roo.data.DataProxy = function(){
9469     this.addEvents({
9470         /**
9471          * @event beforeload
9472          * Fires before a network request is made to retrieve a data object.
9473          * @param {Object} This DataProxy object.
9474          * @param {Object} params The params parameter to the load function.
9475          */
9476         beforeload : true,
9477         /**
9478          * @event load
9479          * Fires before the load method's callback is called.
9480          * @param {Object} This DataProxy object.
9481          * @param {Object} o The data object.
9482          * @param {Object} arg The callback argument object passed to the load function.
9483          */
9484         load : true,
9485         /**
9486          * @event loadexception
9487          * Fires if an Exception occurs during data retrieval.
9488          * @param {Object} This DataProxy object.
9489          * @param {Object} o The data object.
9490          * @param {Object} arg The callback argument object passed to the load function.
9491          * @param {Object} e The Exception.
9492          */
9493         loadexception : true
9494     });
9495     Roo.data.DataProxy.superclass.constructor.call(this);
9496 };
9497
9498 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9499
9500     /**
9501      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9502      */
9503 /*
9504  * Based on:
9505  * Ext JS Library 1.1.1
9506  * Copyright(c) 2006-2007, Ext JS, LLC.
9507  *
9508  * Originally Released Under LGPL - original licence link has changed is not relivant.
9509  *
9510  * Fork - LGPL
9511  * <script type="text/javascript">
9512  */
9513 /**
9514  * @class Roo.data.MemoryProxy
9515  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9516  * to the Reader when its load method is called.
9517  * @constructor
9518  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9519  */
9520 Roo.data.MemoryProxy = function(data){
9521     if (data.data) {
9522         data = data.data;
9523     }
9524     Roo.data.MemoryProxy.superclass.constructor.call(this);
9525     this.data = data;
9526 };
9527
9528 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9529     /**
9530      * Load data from the requested source (in this case an in-memory
9531      * data object passed to the constructor), read the data object into
9532      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9533      * process that block using the passed callback.
9534      * @param {Object} params This parameter is not used by the MemoryProxy class.
9535      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9536      * object into a block of Roo.data.Records.
9537      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9538      * The function must be passed <ul>
9539      * <li>The Record block object</li>
9540      * <li>The "arg" argument from the load function</li>
9541      * <li>A boolean success indicator</li>
9542      * </ul>
9543      * @param {Object} scope The scope in which to call the callback
9544      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9545      */
9546     load : function(params, reader, callback, scope, arg){
9547         params = params || {};
9548         var result;
9549         try {
9550             result = reader.readRecords(this.data);
9551         }catch(e){
9552             this.fireEvent("loadexception", this, arg, null, e);
9553             callback.call(scope, null, arg, false);
9554             return;
9555         }
9556         callback.call(scope, result, arg, true);
9557     },
9558     
9559     // private
9560     update : function(params, records){
9561         
9562     }
9563 });/*
9564  * Based on:
9565  * Ext JS Library 1.1.1
9566  * Copyright(c) 2006-2007, Ext JS, LLC.
9567  *
9568  * Originally Released Under LGPL - original licence link has changed is not relivant.
9569  *
9570  * Fork - LGPL
9571  * <script type="text/javascript">
9572  */
9573 /**
9574  * @class Roo.data.HttpProxy
9575  * @extends Roo.data.DataProxy
9576  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9577  * configured to reference a certain URL.<br><br>
9578  * <p>
9579  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9580  * from which the running page was served.<br><br>
9581  * <p>
9582  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9583  * <p>
9584  * Be aware that to enable the browser to parse an XML document, the server must set
9585  * the Content-Type header in the HTTP response to "text/xml".
9586  * @constructor
9587  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9588  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9589  * will be used to make the request.
9590  */
9591 Roo.data.HttpProxy = function(conn){
9592     Roo.data.HttpProxy.superclass.constructor.call(this);
9593     // is conn a conn config or a real conn?
9594     this.conn = conn;
9595     this.useAjax = !conn || !conn.events;
9596   
9597 };
9598
9599 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9600     // thse are take from connection...
9601     
9602     /**
9603      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9604      */
9605     /**
9606      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9607      * extra parameters to each request made by this object. (defaults to undefined)
9608      */
9609     /**
9610      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9611      *  to each request made by this object. (defaults to undefined)
9612      */
9613     /**
9614      * @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)
9615      */
9616     /**
9617      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9618      */
9619      /**
9620      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9621      * @type Boolean
9622      */
9623   
9624
9625     /**
9626      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9627      * @type Boolean
9628      */
9629     /**
9630      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9631      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9632      * a finer-grained basis than the DataProxy events.
9633      */
9634     getConnection : function(){
9635         return this.useAjax ? Roo.Ajax : this.conn;
9636     },
9637
9638     /**
9639      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9640      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9641      * process that block using the passed callback.
9642      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9643      * for the request to the remote server.
9644      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9645      * object into a block of Roo.data.Records.
9646      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9647      * The function must be passed <ul>
9648      * <li>The Record block object</li>
9649      * <li>The "arg" argument from the load function</li>
9650      * <li>A boolean success indicator</li>
9651      * </ul>
9652      * @param {Object} scope The scope in which to call the callback
9653      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9654      */
9655     load : function(params, reader, callback, scope, arg){
9656         if(this.fireEvent("beforeload", this, params) !== false){
9657             var  o = {
9658                 params : params || {},
9659                 request: {
9660                     callback : callback,
9661                     scope : scope,
9662                     arg : arg
9663                 },
9664                 reader: reader,
9665                 callback : this.loadResponse,
9666                 scope: this
9667             };
9668             if(this.useAjax){
9669                 Roo.applyIf(o, this.conn);
9670                 if(this.activeRequest){
9671                     Roo.Ajax.abort(this.activeRequest);
9672                 }
9673                 this.activeRequest = Roo.Ajax.request(o);
9674             }else{
9675                 this.conn.request(o);
9676             }
9677         }else{
9678             callback.call(scope||this, null, arg, false);
9679         }
9680     },
9681
9682     // private
9683     loadResponse : function(o, success, response){
9684         delete this.activeRequest;
9685         if(!success){
9686             this.fireEvent("loadexception", this, o, response);
9687             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9688             return;
9689         }
9690         var result;
9691         try {
9692             result = o.reader.read(response);
9693         }catch(e){
9694             this.fireEvent("loadexception", this, o, response, e);
9695             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9696             return;
9697         }
9698         
9699         this.fireEvent("load", this, o, o.request.arg);
9700         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9701     },
9702
9703     // private
9704     update : function(dataSet){
9705
9706     },
9707
9708     // private
9709     updateResponse : function(dataSet){
9710
9711     }
9712 });/*
9713  * Based on:
9714  * Ext JS Library 1.1.1
9715  * Copyright(c) 2006-2007, Ext JS, LLC.
9716  *
9717  * Originally Released Under LGPL - original licence link has changed is not relivant.
9718  *
9719  * Fork - LGPL
9720  * <script type="text/javascript">
9721  */
9722
9723 /**
9724  * @class Roo.data.ScriptTagProxy
9725  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9726  * other than the originating domain of the running page.<br><br>
9727  * <p>
9728  * <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
9729  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9730  * <p>
9731  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9732  * source code that is used as the source inside a &lt;script> tag.<br><br>
9733  * <p>
9734  * In order for the browser to process the returned data, the server must wrap the data object
9735  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9736  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9737  * depending on whether the callback name was passed:
9738  * <p>
9739  * <pre><code>
9740 boolean scriptTag = false;
9741 String cb = request.getParameter("callback");
9742 if (cb != null) {
9743     scriptTag = true;
9744     response.setContentType("text/javascript");
9745 } else {
9746     response.setContentType("application/x-json");
9747 }
9748 Writer out = response.getWriter();
9749 if (scriptTag) {
9750     out.write(cb + "(");
9751 }
9752 out.print(dataBlock.toJsonString());
9753 if (scriptTag) {
9754     out.write(");");
9755 }
9756 </pre></code>
9757  *
9758  * @constructor
9759  * @param {Object} config A configuration object.
9760  */
9761 Roo.data.ScriptTagProxy = function(config){
9762     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9763     Roo.apply(this, config);
9764     this.head = document.getElementsByTagName("head")[0];
9765 };
9766
9767 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9768
9769 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9770     /**
9771      * @cfg {String} url The URL from which to request the data object.
9772      */
9773     /**
9774      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9775      */
9776     timeout : 30000,
9777     /**
9778      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9779      * the server the name of the callback function set up by the load call to process the returned data object.
9780      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9781      * javascript output which calls this named function passing the data object as its only parameter.
9782      */
9783     callbackParam : "callback",
9784     /**
9785      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9786      * name to the request.
9787      */
9788     nocache : true,
9789
9790     /**
9791      * Load data from the configured URL, read the data object into
9792      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9793      * process that block using the passed callback.
9794      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9795      * for the request to the remote server.
9796      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9797      * object into a block of Roo.data.Records.
9798      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9799      * The function must be passed <ul>
9800      * <li>The Record block object</li>
9801      * <li>The "arg" argument from the load function</li>
9802      * <li>A boolean success indicator</li>
9803      * </ul>
9804      * @param {Object} scope The scope in which to call the callback
9805      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9806      */
9807     load : function(params, reader, callback, scope, arg){
9808         if(this.fireEvent("beforeload", this, params) !== false){
9809
9810             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9811
9812             var url = this.url;
9813             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9814             if(this.nocache){
9815                 url += "&_dc=" + (new Date().getTime());
9816             }
9817             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9818             var trans = {
9819                 id : transId,
9820                 cb : "stcCallback"+transId,
9821                 scriptId : "stcScript"+transId,
9822                 params : params,
9823                 arg : arg,
9824                 url : url,
9825                 callback : callback,
9826                 scope : scope,
9827                 reader : reader
9828             };
9829             var conn = this;
9830
9831             window[trans.cb] = function(o){
9832                 conn.handleResponse(o, trans);
9833             };
9834
9835             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9836
9837             if(this.autoAbort !== false){
9838                 this.abort();
9839             }
9840
9841             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9842
9843             var script = document.createElement("script");
9844             script.setAttribute("src", url);
9845             script.setAttribute("type", "text/javascript");
9846             script.setAttribute("id", trans.scriptId);
9847             this.head.appendChild(script);
9848
9849             this.trans = trans;
9850         }else{
9851             callback.call(scope||this, null, arg, false);
9852         }
9853     },
9854
9855     // private
9856     isLoading : function(){
9857         return this.trans ? true : false;
9858     },
9859
9860     /**
9861      * Abort the current server request.
9862      */
9863     abort : function(){
9864         if(this.isLoading()){
9865             this.destroyTrans(this.trans);
9866         }
9867     },
9868
9869     // private
9870     destroyTrans : function(trans, isLoaded){
9871         this.head.removeChild(document.getElementById(trans.scriptId));
9872         clearTimeout(trans.timeoutId);
9873         if(isLoaded){
9874             window[trans.cb] = undefined;
9875             try{
9876                 delete window[trans.cb];
9877             }catch(e){}
9878         }else{
9879             // if hasn't been loaded, wait for load to remove it to prevent script error
9880             window[trans.cb] = function(){
9881                 window[trans.cb] = undefined;
9882                 try{
9883                     delete window[trans.cb];
9884                 }catch(e){}
9885             };
9886         }
9887     },
9888
9889     // private
9890     handleResponse : function(o, trans){
9891         this.trans = false;
9892         this.destroyTrans(trans, true);
9893         var result;
9894         try {
9895             result = trans.reader.readRecords(o);
9896         }catch(e){
9897             this.fireEvent("loadexception", this, o, trans.arg, e);
9898             trans.callback.call(trans.scope||window, null, trans.arg, false);
9899             return;
9900         }
9901         this.fireEvent("load", this, o, trans.arg);
9902         trans.callback.call(trans.scope||window, result, trans.arg, true);
9903     },
9904
9905     // private
9906     handleFailure : function(trans){
9907         this.trans = false;
9908         this.destroyTrans(trans, false);
9909         this.fireEvent("loadexception", this, null, trans.arg);
9910         trans.callback.call(trans.scope||window, null, trans.arg, false);
9911     }
9912 });/*
9913  * Based on:
9914  * Ext JS Library 1.1.1
9915  * Copyright(c) 2006-2007, Ext JS, LLC.
9916  *
9917  * Originally Released Under LGPL - original licence link has changed is not relivant.
9918  *
9919  * Fork - LGPL
9920  * <script type="text/javascript">
9921  */
9922
9923 /**
9924  * @class Roo.data.JsonReader
9925  * @extends Roo.data.DataReader
9926  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9927  * based on mappings in a provided Roo.data.Record constructor.
9928  * 
9929  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9930  * in the reply previously. 
9931  * 
9932  * <p>
9933  * Example code:
9934  * <pre><code>
9935 var RecordDef = Roo.data.Record.create([
9936     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9937     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9938 ]);
9939 var myReader = new Roo.data.JsonReader({
9940     totalProperty: "results",    // The property which contains the total dataset size (optional)
9941     root: "rows",                // The property which contains an Array of row objects
9942     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9943 }, RecordDef);
9944 </code></pre>
9945  * <p>
9946  * This would consume a JSON file like this:
9947  * <pre><code>
9948 { 'results': 2, 'rows': [
9949     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9950     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9951 }
9952 </code></pre>
9953  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9954  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9955  * paged from the remote server.
9956  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9957  * @cfg {String} root name of the property which contains the Array of row objects.
9958  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9959  * @constructor
9960  * Create a new JsonReader
9961  * @param {Object} meta Metadata configuration options
9962  * @param {Object} recordType Either an Array of field definition objects,
9963  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9964  */
9965 Roo.data.JsonReader = function(meta, recordType){
9966     
9967     meta = meta || {};
9968     // set some defaults:
9969     Roo.applyIf(meta, {
9970         totalProperty: 'total',
9971         successProperty : 'success',
9972         root : 'data',
9973         id : 'id'
9974     });
9975     
9976     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9977 };
9978 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9979     
9980     /**
9981      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9982      * Used by Store query builder to append _requestMeta to params.
9983      * 
9984      */
9985     metaFromRemote : false,
9986     /**
9987      * This method is only used by a DataProxy which has retrieved data from a remote server.
9988      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9989      * @return {Object} data A data block which is used by an Roo.data.Store object as
9990      * a cache of Roo.data.Records.
9991      */
9992     read : function(response){
9993         var json = response.responseText;
9994        
9995         var o = /* eval:var:o */ eval("("+json+")");
9996         if(!o) {
9997             throw {message: "JsonReader.read: Json object not found"};
9998         }
9999         
10000         if(o.metaData){
10001             
10002             delete this.ef;
10003             this.metaFromRemote = true;
10004             this.meta = o.metaData;
10005             this.recordType = Roo.data.Record.create(o.metaData.fields);
10006             this.onMetaChange(this.meta, this.recordType, o);
10007         }
10008         return this.readRecords(o);
10009     },
10010
10011     // private function a store will implement
10012     onMetaChange : function(meta, recordType, o){
10013
10014     },
10015
10016     /**
10017          * @ignore
10018          */
10019     simpleAccess: function(obj, subsc) {
10020         return obj[subsc];
10021     },
10022
10023         /**
10024          * @ignore
10025          */
10026     getJsonAccessor: function(){
10027         var re = /[\[\.]/;
10028         return function(expr) {
10029             try {
10030                 return(re.test(expr))
10031                     ? new Function("obj", "return obj." + expr)
10032                     : function(obj){
10033                         return obj[expr];
10034                     };
10035             } catch(e){}
10036             return Roo.emptyFn;
10037         };
10038     }(),
10039
10040     /**
10041      * Create a data block containing Roo.data.Records from an XML document.
10042      * @param {Object} o An object which contains an Array of row objects in the property specified
10043      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10044      * which contains the total size of the dataset.
10045      * @return {Object} data A data block which is used by an Roo.data.Store object as
10046      * a cache of Roo.data.Records.
10047      */
10048     readRecords : function(o){
10049         /**
10050          * After any data loads, the raw JSON data is available for further custom processing.
10051          * @type Object
10052          */
10053         this.o = o;
10054         var s = this.meta, Record = this.recordType,
10055             f = Record.prototype.fields, fi = f.items, fl = f.length;
10056
10057 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10058         if (!this.ef) {
10059             if(s.totalProperty) {
10060                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10061                 }
10062                 if(s.successProperty) {
10063                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10064                 }
10065                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10066                 if (s.id) {
10067                         var g = this.getJsonAccessor(s.id);
10068                         this.getId = function(rec) {
10069                                 var r = g(rec);
10070                                 return (r === undefined || r === "") ? null : r;
10071                         };
10072                 } else {
10073                         this.getId = function(){return null;};
10074                 }
10075             this.ef = [];
10076             for(var jj = 0; jj < fl; jj++){
10077                 f = fi[jj];
10078                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10079                 this.ef[jj] = this.getJsonAccessor(map);
10080             }
10081         }
10082
10083         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10084         if(s.totalProperty){
10085             var vt = parseInt(this.getTotal(o), 10);
10086             if(!isNaN(vt)){
10087                 totalRecords = vt;
10088             }
10089         }
10090         if(s.successProperty){
10091             var vs = this.getSuccess(o);
10092             if(vs === false || vs === 'false'){
10093                 success = false;
10094             }
10095         }
10096         var records = [];
10097             for(var i = 0; i < c; i++){
10098                     var n = root[i];
10099                 var values = {};
10100                 var id = this.getId(n);
10101                 for(var j = 0; j < fl; j++){
10102                     f = fi[j];
10103                 var v = this.ef[j](n);
10104                 if (!f.convert) {
10105                     Roo.log('missing convert for ' + f.name);
10106                     Roo.log(f);
10107                     continue;
10108                 }
10109                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10110                 }
10111                 var record = new Record(values, id);
10112                 record.json = n;
10113                 records[i] = record;
10114             }
10115             return {
10116             raw : o,
10117                 success : success,
10118                 records : records,
10119                 totalRecords : totalRecords
10120             };
10121     }
10122 });/*
10123  * Based on:
10124  * Ext JS Library 1.1.1
10125  * Copyright(c) 2006-2007, Ext JS, LLC.
10126  *
10127  * Originally Released Under LGPL - original licence link has changed is not relivant.
10128  *
10129  * Fork - LGPL
10130  * <script type="text/javascript">
10131  */
10132
10133 /**
10134  * @class Roo.data.ArrayReader
10135  * @extends Roo.data.DataReader
10136  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10137  * Each element of that Array represents a row of data fields. The
10138  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10139  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10140  * <p>
10141  * Example code:.
10142  * <pre><code>
10143 var RecordDef = Roo.data.Record.create([
10144     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10145     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10146 ]);
10147 var myReader = new Roo.data.ArrayReader({
10148     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10149 }, RecordDef);
10150 </code></pre>
10151  * <p>
10152  * This would consume an Array like this:
10153  * <pre><code>
10154 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10155   </code></pre>
10156  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10157  * @constructor
10158  * Create a new JsonReader
10159  * @param {Object} meta Metadata configuration options.
10160  * @param {Object} recordType Either an Array of field definition objects
10161  * as specified to {@link Roo.data.Record#create},
10162  * or an {@link Roo.data.Record} object
10163  * created using {@link Roo.data.Record#create}.
10164  */
10165 Roo.data.ArrayReader = function(meta, recordType){
10166     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10167 };
10168
10169 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10170     /**
10171      * Create a data block containing Roo.data.Records from an XML document.
10172      * @param {Object} o An Array of row objects which represents the dataset.
10173      * @return {Object} data A data block which is used by an Roo.data.Store object as
10174      * a cache of Roo.data.Records.
10175      */
10176     readRecords : function(o){
10177         var sid = this.meta ? this.meta.id : null;
10178         var recordType = this.recordType, fields = recordType.prototype.fields;
10179         var records = [];
10180         var root = o;
10181             for(var i = 0; i < root.length; i++){
10182                     var n = root[i];
10183                 var values = {};
10184                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10185                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10186                 var f = fields.items[j];
10187                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10188                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10189                 v = f.convert(v);
10190                 values[f.name] = v;
10191             }
10192                 var record = new recordType(values, id);
10193                 record.json = n;
10194                 records[records.length] = record;
10195             }
10196             return {
10197                 records : records,
10198                 totalRecords : records.length
10199             };
10200     }
10201 });/*
10202  * - LGPL
10203  * * 
10204  */
10205
10206 /**
10207  * @class Roo.bootstrap.ComboBox
10208  * @extends Roo.bootstrap.TriggerField
10209  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10210  * @cfg {Boolean} append (true|false) default false
10211  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10212  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10213  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10214  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10215  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10216  * @constructor
10217  * Create a new ComboBox.
10218  * @param {Object} config Configuration options
10219  */
10220 Roo.bootstrap.ComboBox = function(config){
10221     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10222     this.addEvents({
10223         /**
10224          * @event expand
10225          * Fires when the dropdown list is expanded
10226              * @param {Roo.bootstrap.ComboBox} combo This combo box
10227              */
10228         'expand' : true,
10229         /**
10230          * @event collapse
10231          * Fires when the dropdown list is collapsed
10232              * @param {Roo.bootstrap.ComboBox} combo This combo box
10233              */
10234         'collapse' : true,
10235         /**
10236          * @event beforeselect
10237          * Fires before a list item is selected. Return false to cancel the selection.
10238              * @param {Roo.bootstrap.ComboBox} combo This combo box
10239              * @param {Roo.data.Record} record The data record returned from the underlying store
10240              * @param {Number} index The index of the selected item in the dropdown list
10241              */
10242         'beforeselect' : true,
10243         /**
10244          * @event select
10245          * Fires when a list item is selected
10246              * @param {Roo.bootstrap.ComboBox} combo This combo box
10247              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10248              * @param {Number} index The index of the selected item in the dropdown list
10249              */
10250         'select' : true,
10251         /**
10252          * @event beforequery
10253          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10254          * The event object passed has these properties:
10255              * @param {Roo.bootstrap.ComboBox} combo This combo box
10256              * @param {String} query The query
10257              * @param {Boolean} forceAll true to force "all" query
10258              * @param {Boolean} cancel true to cancel the query
10259              * @param {Object} e The query event object
10260              */
10261         'beforequery': true,
10262          /**
10263          * @event add
10264          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10265              * @param {Roo.bootstrap.ComboBox} combo This combo box
10266              */
10267         'add' : true,
10268         /**
10269          * @event edit
10270          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10271              * @param {Roo.bootstrap.ComboBox} combo This combo box
10272              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10273              */
10274         'edit' : true,
10275         /**
10276          * @event remove
10277          * Fires when the remove value from the combobox array
10278              * @param {Roo.bootstrap.ComboBox} combo This combo box
10279              */
10280         'remove' : true
10281         
10282     });
10283     
10284     this.item = [];
10285     this.tickItems = [];
10286     
10287     this.selectedIndex = -1;
10288     if(this.mode == 'local'){
10289         if(config.queryDelay === undefined){
10290             this.queryDelay = 10;
10291         }
10292         if(config.minChars === undefined){
10293             this.minChars = 0;
10294         }
10295     }
10296 };
10297
10298 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10299      
10300     /**
10301      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10302      * rendering into an Roo.Editor, defaults to false)
10303      */
10304     /**
10305      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10306      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10307      */
10308     /**
10309      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10310      */
10311     /**
10312      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10313      * the dropdown list (defaults to undefined, with no header element)
10314      */
10315
10316      /**
10317      * @cfg {String/Roo.Template} tpl The template to use to render the output
10318      */
10319      
10320      /**
10321      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10322      */
10323     listWidth: undefined,
10324     /**
10325      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10326      * mode = 'remote' or 'text' if mode = 'local')
10327      */
10328     displayField: undefined,
10329     /**
10330      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10331      * mode = 'remote' or 'value' if mode = 'local'). 
10332      * Note: use of a valueField requires the user make a selection
10333      * in order for a value to be mapped.
10334      */
10335     valueField: undefined,
10336     
10337     
10338     /**
10339      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10340      * field's data value (defaults to the underlying DOM element's name)
10341      */
10342     hiddenName: undefined,
10343     /**
10344      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10345      */
10346     listClass: '',
10347     /**
10348      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10349      */
10350     selectedClass: 'active',
10351     
10352     /**
10353      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10354      */
10355     shadow:'sides',
10356     /**
10357      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10358      * anchor positions (defaults to 'tl-bl')
10359      */
10360     listAlign: 'tl-bl?',
10361     /**
10362      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10363      */
10364     maxHeight: 300,
10365     /**
10366      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10367      * query specified by the allQuery config option (defaults to 'query')
10368      */
10369     triggerAction: 'query',
10370     /**
10371      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10372      * (defaults to 4, does not apply if editable = false)
10373      */
10374     minChars : 4,
10375     /**
10376      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10377      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10378      */
10379     typeAhead: false,
10380     /**
10381      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10382      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10383      */
10384     queryDelay: 500,
10385     /**
10386      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10387      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10388      */
10389     pageSize: 0,
10390     /**
10391      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10392      * when editable = true (defaults to false)
10393      */
10394     selectOnFocus:false,
10395     /**
10396      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10397      */
10398     queryParam: 'query',
10399     /**
10400      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10401      * when mode = 'remote' (defaults to 'Loading...')
10402      */
10403     loadingText: 'Loading...',
10404     /**
10405      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10406      */
10407     resizable: false,
10408     /**
10409      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10410      */
10411     handleHeight : 8,
10412     /**
10413      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10414      * traditional select (defaults to true)
10415      */
10416     editable: true,
10417     /**
10418      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10419      */
10420     allQuery: '',
10421     /**
10422      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10423      */
10424     mode: 'remote',
10425     /**
10426      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10427      * listWidth has a higher value)
10428      */
10429     minListWidth : 70,
10430     /**
10431      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10432      * allow the user to set arbitrary text into the field (defaults to false)
10433      */
10434     forceSelection:false,
10435     /**
10436      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10437      * if typeAhead = true (defaults to 250)
10438      */
10439     typeAheadDelay : 250,
10440     /**
10441      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10442      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10443      */
10444     valueNotFoundText : undefined,
10445     /**
10446      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10447      */
10448     blockFocus : false,
10449     
10450     /**
10451      * @cfg {Boolean} disableClear Disable showing of clear button.
10452      */
10453     disableClear : false,
10454     /**
10455      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10456      */
10457     alwaysQuery : false,
10458     
10459     /**
10460      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10461      */
10462     multiple : false,
10463     
10464     //private
10465     addicon : false,
10466     editicon: false,
10467     
10468     page: 0,
10469     hasQuery: false,
10470     append: false,
10471     loadNext: false,
10472     autoFocus : true,
10473     tickable : false,
10474     btnPosition : 'right',
10475     triggerList : true,
10476     showToggleBtn : true,
10477     // element that contains real text value.. (when hidden is used..)
10478     
10479     getAutoCreate : function()
10480     {
10481         var cfg = false;
10482         
10483         /*
10484          *  Normal ComboBox
10485          */
10486         if(!this.tickable){
10487             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10488             return cfg;
10489         }
10490         
10491         /*
10492          *  ComboBox with tickable selections
10493          */
10494              
10495         var align = this.labelAlign || this.parentLabelAlign();
10496         
10497         cfg = {
10498             cls : 'form-group roo-combobox-tickable' //input-group
10499         };
10500         
10501         
10502         var buttons = {
10503             tag : 'div',
10504             cls : 'tickable-buttons',
10505             cn : [
10506                 {
10507                     tag : 'button',
10508                     type : 'button',
10509                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10510                     html : 'Edit'
10511                 },
10512                 {
10513                     tag : 'button',
10514                     type : 'button',
10515                     name : 'ok',
10516                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10517                     html : 'Done'
10518                 },
10519                 {
10520                     tag : 'button',
10521                     type : 'button',
10522                     name : 'cancel',
10523                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10524                     html : 'Cancel'
10525                 }
10526             ]
10527         };
10528         
10529         var _this = this;
10530         Roo.each(buttons.cn, function(c){
10531             if (_this.size) {
10532                 c.cls += ' btn-' + _this.size;
10533             }
10534
10535             if (_this.disabled) {
10536                 c.disabled = true;
10537             }
10538         });
10539         
10540         var box = {
10541             tag: 'div',
10542             cn: [
10543                 {
10544                     tag: 'input',
10545                     type : 'hidden',
10546                     cls: 'form-hidden-field'
10547                 },
10548                 {
10549                     tag: 'ul',
10550                     cls: 'select2-choices',
10551                     cn:[
10552                         {
10553                             tag: 'li',
10554                             cls: 'select2-search-field',
10555                             cn: [
10556
10557                                 buttons
10558                             ]
10559                         }
10560                     ]
10561                 }
10562             ]
10563         }
10564         
10565         var combobox = {
10566             cls: 'select2-container input-group select2-container-multi',
10567             cn: [
10568                 box
10569 //                {
10570 //                    tag: 'ul',
10571 //                    cls: 'typeahead typeahead-long dropdown-menu',
10572 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10573 //                }
10574             ]
10575         };
10576         
10577         if (align ==='left' && this.fieldLabel.length) {
10578             
10579                 Roo.log("left and has label");
10580                 cfg.cn = [
10581                     
10582                     {
10583                         tag: 'label',
10584                         'for' :  id,
10585                         cls : 'control-label col-sm-' + this.labelWidth,
10586                         html : this.fieldLabel
10587                         
10588                     },
10589                     {
10590                         cls : "col-sm-" + (12 - this.labelWidth), 
10591                         cn: [
10592                             combobox
10593                         ]
10594                     }
10595                     
10596                 ];
10597         } else if ( this.fieldLabel.length) {
10598                 Roo.log(" label");
10599                  cfg.cn = [
10600                    
10601                     {
10602                         tag: 'label',
10603                         //cls : 'input-group-addon',
10604                         html : this.fieldLabel
10605                         
10606                     },
10607                     
10608                     combobox
10609                     
10610                 ];
10611
10612         } else {
10613             
10614                 Roo.log(" no label && no align");
10615                 cfg = combobox
10616                      
10617                 
10618         }
10619          
10620         var settings=this;
10621         ['xs','sm','md','lg'].map(function(size){
10622             if (settings[size]) {
10623                 cfg.cls += ' col-' + size + '-' + settings[size];
10624             }
10625         });
10626         
10627         return cfg;
10628         
10629     },
10630     
10631     // private
10632     initEvents: function()
10633     {
10634         
10635         if (!this.store) {
10636             throw "can not find store for combo";
10637         }
10638         this.store = Roo.factory(this.store, Roo.data);
10639         
10640         if(this.tickable){
10641             this.initTickableEvents();
10642             return;
10643         }
10644         
10645         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10646         
10647         if(this.hiddenName){
10648             
10649             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10650             
10651             this.hiddenField.dom.value =
10652                 this.hiddenValue !== undefined ? this.hiddenValue :
10653                 this.value !== undefined ? this.value : '';
10654
10655             // prevent input submission
10656             this.el.dom.removeAttribute('name');
10657             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10658              
10659              
10660         }
10661         //if(Roo.isGecko){
10662         //    this.el.dom.setAttribute('autocomplete', 'off');
10663         //}
10664         
10665         var cls = 'x-combo-list';
10666         
10667         //this.list = new Roo.Layer({
10668         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10669         //});
10670         
10671         var _this = this;
10672         
10673         (function(){
10674             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10675             _this.list.setWidth(lw);
10676         }).defer(100);
10677         
10678         this.list.on('mouseover', this.onViewOver, this);
10679         this.list.on('mousemove', this.onViewMove, this);
10680         
10681         this.list.on('scroll', this.onViewScroll, this);
10682         
10683         /*
10684         this.list.swallowEvent('mousewheel');
10685         this.assetHeight = 0;
10686
10687         if(this.title){
10688             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10689             this.assetHeight += this.header.getHeight();
10690         }
10691
10692         this.innerList = this.list.createChild({cls:cls+'-inner'});
10693         this.innerList.on('mouseover', this.onViewOver, this);
10694         this.innerList.on('mousemove', this.onViewMove, this);
10695         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10696         
10697         if(this.allowBlank && !this.pageSize && !this.disableClear){
10698             this.footer = this.list.createChild({cls:cls+'-ft'});
10699             this.pageTb = new Roo.Toolbar(this.footer);
10700            
10701         }
10702         if(this.pageSize){
10703             this.footer = this.list.createChild({cls:cls+'-ft'});
10704             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10705                     {pageSize: this.pageSize});
10706             
10707         }
10708         
10709         if (this.pageTb && this.allowBlank && !this.disableClear) {
10710             var _this = this;
10711             this.pageTb.add(new Roo.Toolbar.Fill(), {
10712                 cls: 'x-btn-icon x-btn-clear',
10713                 text: '&#160;',
10714                 handler: function()
10715                 {
10716                     _this.collapse();
10717                     _this.clearValue();
10718                     _this.onSelect(false, -1);
10719                 }
10720             });
10721         }
10722         if (this.footer) {
10723             this.assetHeight += this.footer.getHeight();
10724         }
10725         */
10726             
10727         if(!this.tpl){
10728             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10729         }
10730
10731         this.view = new Roo.View(this.list, this.tpl, {
10732             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10733         });
10734         //this.view.wrapEl.setDisplayed(false);
10735         this.view.on('click', this.onViewClick, this);
10736         
10737         
10738         
10739         this.store.on('beforeload', this.onBeforeLoad, this);
10740         this.store.on('load', this.onLoad, this);
10741         this.store.on('loadexception', this.onLoadException, this);
10742         /*
10743         if(this.resizable){
10744             this.resizer = new Roo.Resizable(this.list,  {
10745                pinned:true, handles:'se'
10746             });
10747             this.resizer.on('resize', function(r, w, h){
10748                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10749                 this.listWidth = w;
10750                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10751                 this.restrictHeight();
10752             }, this);
10753             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10754         }
10755         */
10756         if(!this.editable){
10757             this.editable = true;
10758             this.setEditable(false);
10759         }
10760         
10761         /*
10762         
10763         if (typeof(this.events.add.listeners) != 'undefined') {
10764             
10765             this.addicon = this.wrap.createChild(
10766                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10767        
10768             this.addicon.on('click', function(e) {
10769                 this.fireEvent('add', this);
10770             }, this);
10771         }
10772         if (typeof(this.events.edit.listeners) != 'undefined') {
10773             
10774             this.editicon = this.wrap.createChild(
10775                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10776             if (this.addicon) {
10777                 this.editicon.setStyle('margin-left', '40px');
10778             }
10779             this.editicon.on('click', function(e) {
10780                 
10781                 // we fire even  if inothing is selected..
10782                 this.fireEvent('edit', this, this.lastData );
10783                 
10784             }, this);
10785         }
10786         */
10787         
10788         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10789             "up" : function(e){
10790                 this.inKeyMode = true;
10791                 this.selectPrev();
10792             },
10793
10794             "down" : function(e){
10795                 if(!this.isExpanded()){
10796                     this.onTriggerClick();
10797                 }else{
10798                     this.inKeyMode = true;
10799                     this.selectNext();
10800                 }
10801             },
10802
10803             "enter" : function(e){
10804 //                this.onViewClick();
10805                 //return true;
10806                 this.collapse();
10807                 
10808                 if(this.fireEvent("specialkey", this, e)){
10809                     this.onViewClick(false);
10810                 }
10811                 
10812                 return true;
10813             },
10814
10815             "esc" : function(e){
10816                 this.collapse();
10817             },
10818
10819             "tab" : function(e){
10820                 this.collapse();
10821                 
10822                 if(this.fireEvent("specialkey", this, e)){
10823                     this.onViewClick(false);
10824                 }
10825                 
10826                 return true;
10827             },
10828
10829             scope : this,
10830
10831             doRelay : function(foo, bar, hname){
10832                 if(hname == 'down' || this.scope.isExpanded()){
10833                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10834                 }
10835                 return true;
10836             },
10837
10838             forceKeyDown: true
10839         });
10840         
10841         
10842         this.queryDelay = Math.max(this.queryDelay || 10,
10843                 this.mode == 'local' ? 10 : 250);
10844         
10845         
10846         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10847         
10848         if(this.typeAhead){
10849             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10850         }
10851         if(this.editable !== false){
10852             this.inputEl().on("keyup", this.onKeyUp, this);
10853         }
10854         if(this.forceSelection){
10855             this.inputEl().on('blur', this.doForce, this);
10856         }
10857         
10858         if(this.multiple){
10859             this.choices = this.el.select('ul.select2-choices', true).first();
10860             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10861         }
10862     },
10863     
10864     initTickableEvents: function()
10865     {   
10866         this.createList();
10867         
10868         if(this.hiddenName){
10869             
10870             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10871             
10872             this.hiddenField.dom.value =
10873                 this.hiddenValue !== undefined ? this.hiddenValue :
10874                 this.value !== undefined ? this.value : '';
10875
10876             // prevent input submission
10877             this.el.dom.removeAttribute('name');
10878             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10879              
10880              
10881         }
10882         
10883 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10884         
10885         this.choices = this.el.select('ul.select2-choices', true).first();
10886         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10887         if(this.triggerList){
10888             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10889         }
10890          
10891         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10892         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10893         
10894         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10895         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10896         
10897         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10898         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10899         
10900         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10901         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10902         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10903         
10904         this.okBtn.hide();
10905         this.cancelBtn.hide();
10906         
10907         var _this = this;
10908         
10909         (function(){
10910             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10911             _this.list.setWidth(lw);
10912         }).defer(100);
10913         
10914         this.list.on('mouseover', this.onViewOver, this);
10915         this.list.on('mousemove', this.onViewMove, this);
10916         
10917         this.list.on('scroll', this.onViewScroll, this);
10918         
10919         if(!this.tpl){
10920             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>';
10921         }
10922
10923         this.view = new Roo.View(this.list, this.tpl, {
10924             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10925         });
10926         
10927         //this.view.wrapEl.setDisplayed(false);
10928         this.view.on('click', this.onViewClick, this);
10929         
10930         
10931         
10932         this.store.on('beforeload', this.onBeforeLoad, this);
10933         this.store.on('load', this.onLoad, this);
10934         this.store.on('loadexception', this.onLoadException, this);
10935         
10936 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10937 //            "up" : function(e){
10938 //                this.inKeyMode = true;
10939 //                this.selectPrev();
10940 //            },
10941 //
10942 //            "down" : function(e){
10943 //                if(!this.isExpanded()){
10944 //                    this.onTriggerClick();
10945 //                }else{
10946 //                    this.inKeyMode = true;
10947 //                    this.selectNext();
10948 //                }
10949 //            },
10950 //
10951 //            "enter" : function(e){
10952 ////                this.onViewClick();
10953 //                //return true;
10954 //                this.collapse();
10955 //                
10956 //                if(this.fireEvent("specialkey", this, e)){
10957 //                    this.onViewClick(false);
10958 //                }
10959 //                
10960 //                return true;
10961 //            },
10962 //
10963 //            "esc" : function(e){
10964 //                this.collapse();
10965 //            },
10966 //
10967 //            "tab" : function(e){
10968 //                this.collapse();
10969 //                
10970 //                if(this.fireEvent("specialkey", this, e)){
10971 //                    this.onViewClick(false);
10972 //                }
10973 //                
10974 //                return true;
10975 //            },
10976 //
10977 //            scope : this,
10978 //
10979 //            doRelay : function(foo, bar, hname){
10980 //                if(hname == 'down' || this.scope.isExpanded()){
10981 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10982 //                }
10983 //                return true;
10984 //            },
10985 //
10986 //            forceKeyDown: true
10987 //        });
10988         
10989         
10990         this.queryDelay = Math.max(this.queryDelay || 10,
10991                 this.mode == 'local' ? 10 : 250);
10992         
10993         
10994         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10995         
10996         if(this.typeAhead){
10997             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10998         }
10999     },
11000
11001     onDestroy : function(){
11002         if(this.view){
11003             this.view.setStore(null);
11004             this.view.el.removeAllListeners();
11005             this.view.el.remove();
11006             this.view.purgeListeners();
11007         }
11008         if(this.list){
11009             this.list.dom.innerHTML  = '';
11010         }
11011         
11012         if(this.store){
11013             this.store.un('beforeload', this.onBeforeLoad, this);
11014             this.store.un('load', this.onLoad, this);
11015             this.store.un('loadexception', this.onLoadException, this);
11016         }
11017         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11018     },
11019
11020     // private
11021     fireKey : function(e){
11022         if(e.isNavKeyPress() && !this.list.isVisible()){
11023             this.fireEvent("specialkey", this, e);
11024         }
11025     },
11026
11027     // private
11028     onResize: function(w, h){
11029 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11030 //        
11031 //        if(typeof w != 'number'){
11032 //            // we do not handle it!?!?
11033 //            return;
11034 //        }
11035 //        var tw = this.trigger.getWidth();
11036 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11037 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11038 //        var x = w - tw;
11039 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11040 //            
11041 //        //this.trigger.setStyle('left', x+'px');
11042 //        
11043 //        if(this.list && this.listWidth === undefined){
11044 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11045 //            this.list.setWidth(lw);
11046 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11047 //        }
11048         
11049     
11050         
11051     },
11052
11053     /**
11054      * Allow or prevent the user from directly editing the field text.  If false is passed,
11055      * the user will only be able to select from the items defined in the dropdown list.  This method
11056      * is the runtime equivalent of setting the 'editable' config option at config time.
11057      * @param {Boolean} value True to allow the user to directly edit the field text
11058      */
11059     setEditable : function(value){
11060         if(value == this.editable){
11061             return;
11062         }
11063         this.editable = value;
11064         if(!value){
11065             this.inputEl().dom.setAttribute('readOnly', true);
11066             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11067             this.inputEl().addClass('x-combo-noedit');
11068         }else{
11069             this.inputEl().dom.setAttribute('readOnly', false);
11070             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11071             this.inputEl().removeClass('x-combo-noedit');
11072         }
11073     },
11074
11075     // private
11076     
11077     onBeforeLoad : function(combo,opts){
11078         if(!this.hasFocus){
11079             return;
11080         }
11081          if (!opts.add) {
11082             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11083          }
11084 //        this.restrictHeight();
11085         this.selectedIndex = -1;
11086     },
11087
11088     // private
11089     onLoad : function(){
11090         
11091         this.hasQuery = false;
11092         
11093         if(!this.hasFocus){
11094             return;
11095         }
11096         
11097         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11098             this.loading.hide();
11099         }
11100         
11101         if(this.store.getCount() > 0){
11102             this.expand();
11103 //            this.restrictHeight();
11104             if(this.lastQuery == this.allQuery){
11105                 if(this.editable && !this.tickable){
11106                     this.inputEl().dom.select();
11107                 }
11108                 
11109                 if(
11110                     !this.selectByValue(this.value, true) &&
11111                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11112                     this.store.lastOptions.add != true)
11113                 ){
11114                     this.select(0, true);
11115                 }
11116             }else{
11117                 if(this.autoFocus){
11118                     this.selectNext();
11119                 }
11120                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11121                     this.taTask.delay(this.typeAheadDelay);
11122                 }
11123             }
11124         }else{
11125             this.onEmptyResults();
11126         }
11127         
11128         //this.el.focus();
11129     },
11130     // private
11131     onLoadException : function()
11132     {
11133         this.hasQuery = false;
11134         
11135         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11136             this.loading.hide();
11137         }
11138         
11139         this.collapse();
11140         Roo.log(this.store.reader.jsonData);
11141         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11142             // fixme
11143             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11144         }
11145         
11146         
11147     },
11148     // private
11149     onTypeAhead : function(){
11150         if(this.store.getCount() > 0){
11151             var r = this.store.getAt(0);
11152             var newValue = r.data[this.displayField];
11153             var len = newValue.length;
11154             var selStart = this.getRawValue().length;
11155             
11156             if(selStart != len){
11157                 this.setRawValue(newValue);
11158                 this.selectText(selStart, newValue.length);
11159             }
11160         }
11161     },
11162
11163     // private
11164     onSelect : function(record, index){
11165         
11166         if(this.fireEvent('beforeselect', this, record, index) !== false){
11167         
11168             this.setFromData(index > -1 ? record.data : false);
11169             
11170             this.collapse();
11171             this.fireEvent('select', this, record, index);
11172         }
11173     },
11174
11175     /**
11176      * Returns the currently selected field value or empty string if no value is set.
11177      * @return {String} value The selected value
11178      */
11179     getValue : function(){
11180         
11181         if(this.multiple){
11182             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11183         }
11184         
11185         if(this.valueField){
11186             return typeof this.value != 'undefined' ? this.value : '';
11187         }else{
11188             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11189         }
11190     },
11191
11192     /**
11193      * Clears any text/value currently set in the field
11194      */
11195     clearValue : function(){
11196         if(this.hiddenField){
11197             this.hiddenField.dom.value = '';
11198         }
11199         this.value = '';
11200         this.setRawValue('');
11201         this.lastSelectionText = '';
11202         
11203     },
11204
11205     /**
11206      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11207      * will be displayed in the field.  If the value does not match the data value of an existing item,
11208      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11209      * Otherwise the field will be blank (although the value will still be set).
11210      * @param {String} value The value to match
11211      */
11212     setValue : function(v){
11213         if(this.multiple){
11214             this.syncValue();
11215             return;
11216         }
11217         
11218         var text = v;
11219         if(this.valueField){
11220             var r = this.findRecord(this.valueField, v);
11221             if(r){
11222                 text = r.data[this.displayField];
11223             }else if(this.valueNotFoundText !== undefined){
11224                 text = this.valueNotFoundText;
11225             }
11226         }
11227         this.lastSelectionText = text;
11228         if(this.hiddenField){
11229             this.hiddenField.dom.value = v;
11230         }
11231         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11232         this.value = v;
11233     },
11234     /**
11235      * @property {Object} the last set data for the element
11236      */
11237     
11238     lastData : false,
11239     /**
11240      * Sets the value of the field based on a object which is related to the record format for the store.
11241      * @param {Object} value the value to set as. or false on reset?
11242      */
11243     setFromData : function(o){
11244         
11245         if(this.multiple){
11246             if(typeof o.display_name !== 'string'){
11247                 for(var i=0;i<o.display_name.length;i++){
11248                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11249                 }
11250                 return;
11251             }
11252             this.addItem(o);
11253             return;
11254         }
11255             
11256         var dv = ''; // display value
11257         var vv = ''; // value value..
11258         this.lastData = o;
11259         if (this.displayField) {
11260             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11261         } else {
11262             // this is an error condition!!!
11263             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11264         }
11265         
11266         if(this.valueField){
11267             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11268         }
11269         
11270         if(this.hiddenField){
11271             this.hiddenField.dom.value = vv;
11272             
11273             this.lastSelectionText = dv;
11274             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11275             this.value = vv;
11276             return;
11277         }
11278         // no hidden field.. - we store the value in 'value', but still display
11279         // display field!!!!
11280         this.lastSelectionText = dv;
11281         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11282         this.value = vv;
11283         
11284         
11285     },
11286     // private
11287     reset : function(){
11288         // overridden so that last data is reset..
11289         this.setValue(this.originalValue);
11290         this.clearInvalid();
11291         this.lastData = false;
11292         if (this.view) {
11293             this.view.clearSelections();
11294         }
11295     },
11296     // private
11297     findRecord : function(prop, value){
11298         var record;
11299         if(this.store.getCount() > 0){
11300             this.store.each(function(r){
11301                 if(r.data[prop] == value){
11302                     record = r;
11303                     return false;
11304                 }
11305                 return true;
11306             });
11307         }
11308         return record;
11309     },
11310     
11311     getName: function()
11312     {
11313         // returns hidden if it's set..
11314         if (!this.rendered) {return ''};
11315         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11316         
11317     },
11318     // private
11319     onViewMove : function(e, t){
11320         this.inKeyMode = false;
11321     },
11322
11323     // private
11324     onViewOver : function(e, t){
11325         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11326             return;
11327         }
11328         var item = this.view.findItemFromChild(t);
11329         
11330         if(item){
11331             var index = this.view.indexOf(item);
11332             this.select(index, false);
11333         }
11334     },
11335
11336     // private
11337     onViewClick : function(view, doFocus, el, e)
11338     {
11339         var index = this.view.getSelectedIndexes()[0];
11340         
11341         var r = this.store.getAt(index);
11342         
11343         if(this.tickable){
11344             
11345             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11346                 return;
11347             }
11348             
11349             var rm = false;
11350             var _this = this;
11351             
11352             Roo.each(this.tickItems, function(v,k){
11353                 
11354                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11355                     _this.tickItems.splice(k, 1);
11356                     rm = true;
11357                     return;
11358                 }
11359             })
11360             
11361             if(rm){
11362                 return;
11363             }
11364             
11365             this.tickItems.push(r.data);
11366             return;
11367         }
11368         
11369         if(r){
11370             this.onSelect(r, index);
11371         }
11372         if(doFocus !== false && !this.blockFocus){
11373             this.inputEl().focus();
11374         }
11375     },
11376
11377     // private
11378     restrictHeight : function(){
11379         //this.innerList.dom.style.height = '';
11380         //var inner = this.innerList.dom;
11381         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11382         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11383         //this.list.beginUpdate();
11384         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11385         this.list.alignTo(this.inputEl(), this.listAlign);
11386         this.list.alignTo(this.inputEl(), this.listAlign);
11387         //this.list.endUpdate();
11388     },
11389
11390     // private
11391     onEmptyResults : function(){
11392         this.collapse();
11393     },
11394
11395     /**
11396      * Returns true if the dropdown list is expanded, else false.
11397      */
11398     isExpanded : function(){
11399         return this.list.isVisible();
11400     },
11401
11402     /**
11403      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11404      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11405      * @param {String} value The data value of the item to select
11406      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11407      * selected item if it is not currently in view (defaults to true)
11408      * @return {Boolean} True if the value matched an item in the list, else false
11409      */
11410     selectByValue : function(v, scrollIntoView){
11411         if(v !== undefined && v !== null){
11412             var r = this.findRecord(this.valueField || this.displayField, v);
11413             if(r){
11414                 this.select(this.store.indexOf(r), scrollIntoView);
11415                 return true;
11416             }
11417         }
11418         return false;
11419     },
11420
11421     /**
11422      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11423      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11424      * @param {Number} index The zero-based index of the list item to select
11425      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11426      * selected item if it is not currently in view (defaults to true)
11427      */
11428     select : function(index, scrollIntoView){
11429         this.selectedIndex = index;
11430         this.view.select(index);
11431         if(scrollIntoView !== false){
11432             var el = this.view.getNode(index);
11433             if(el && !this.multiple && !this.tickable){
11434                 this.list.scrollChildIntoView(el, false);
11435             }
11436         }
11437     },
11438
11439     // private
11440     selectNext : function(){
11441         var ct = this.store.getCount();
11442         if(ct > 0){
11443             if(this.selectedIndex == -1){
11444                 this.select(0);
11445             }else if(this.selectedIndex < ct-1){
11446                 this.select(this.selectedIndex+1);
11447             }
11448         }
11449     },
11450
11451     // private
11452     selectPrev : function(){
11453         var ct = this.store.getCount();
11454         if(ct > 0){
11455             if(this.selectedIndex == -1){
11456                 this.select(0);
11457             }else if(this.selectedIndex != 0){
11458                 this.select(this.selectedIndex-1);
11459             }
11460         }
11461     },
11462
11463     // private
11464     onKeyUp : function(e){
11465         if(this.editable !== false && !e.isSpecialKey()){
11466             this.lastKey = e.getKey();
11467             this.dqTask.delay(this.queryDelay);
11468         }
11469     },
11470
11471     // private
11472     validateBlur : function(){
11473         return !this.list || !this.list.isVisible();   
11474     },
11475
11476     // private
11477     initQuery : function(){
11478         this.doQuery(this.getRawValue());
11479     },
11480
11481     // private
11482     doForce : function(){
11483         if(this.inputEl().dom.value.length > 0){
11484             this.inputEl().dom.value =
11485                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11486              
11487         }
11488     },
11489
11490     /**
11491      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11492      * query allowing the query action to be canceled if needed.
11493      * @param {String} query The SQL query to execute
11494      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11495      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11496      * saved in the current store (defaults to false)
11497      */
11498     doQuery : function(q, forceAll){
11499         
11500         if(q === undefined || q === null){
11501             q = '';
11502         }
11503         var qe = {
11504             query: q,
11505             forceAll: forceAll,
11506             combo: this,
11507             cancel:false
11508         };
11509         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11510             return false;
11511         }
11512         q = qe.query;
11513         
11514         forceAll = qe.forceAll;
11515         if(forceAll === true || (q.length >= this.minChars)){
11516             
11517             this.hasQuery = true;
11518             
11519             if(this.lastQuery != q || this.alwaysQuery){
11520                 this.lastQuery = q;
11521                 if(this.mode == 'local'){
11522                     this.selectedIndex = -1;
11523                     if(forceAll){
11524                         this.store.clearFilter();
11525                     }else{
11526                         this.store.filter(this.displayField, q);
11527                     }
11528                     this.onLoad();
11529                 }else{
11530                     this.store.baseParams[this.queryParam] = q;
11531                     
11532                     var options = {params : this.getParams(q)};
11533                     
11534                     if(this.loadNext){
11535                         options.add = true;
11536                         options.params.start = this.page * this.pageSize;
11537                     }
11538                     
11539                     this.store.load(options);
11540                     /*
11541                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11542                      *  we should expand the list on onLoad
11543                      *  so command out it
11544                      */
11545 //                    this.expand();
11546                 }
11547             }else{
11548                 this.selectedIndex = -1;
11549                 this.onLoad();   
11550             }
11551         }
11552         
11553         this.loadNext = false;
11554     },
11555
11556     // private
11557     getParams : function(q){
11558         var p = {};
11559         //p[this.queryParam] = q;
11560         
11561         if(this.pageSize){
11562             p.start = 0;
11563             p.limit = this.pageSize;
11564         }
11565         return p;
11566     },
11567
11568     /**
11569      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11570      */
11571     collapse : function(){
11572         if(!this.isExpanded()){
11573             return;
11574         }
11575         
11576         this.list.hide();
11577         
11578         if(this.tickable){
11579             this.okBtn.hide();
11580             this.cancelBtn.hide();
11581             this.trigger.show();
11582         }
11583         
11584         Roo.get(document).un('mousedown', this.collapseIf, this);
11585         Roo.get(document).un('mousewheel', this.collapseIf, this);
11586         if (!this.editable) {
11587             Roo.get(document).un('keydown', this.listKeyPress, this);
11588         }
11589         this.fireEvent('collapse', this);
11590     },
11591
11592     // private
11593     collapseIf : function(e){
11594         var in_combo  = e.within(this.el);
11595         var in_list =  e.within(this.list);
11596         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11597         
11598         if (in_combo || in_list || is_list) {
11599             //e.stopPropagation();
11600             return;
11601         }
11602         
11603         if(this.tickable){
11604             this.onTickableFooterButtonClick(e, false, false);
11605         }
11606
11607         this.collapse();
11608         
11609     },
11610
11611     /**
11612      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11613      */
11614     expand : function(){
11615        
11616         if(this.isExpanded() || !this.hasFocus){
11617             return;
11618         }
11619         
11620         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11621         this.list.setWidth(lw);
11622         
11623         
11624          Roo.log('expand');
11625         
11626         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11627         this.list.setWidth(lw);
11628             
11629         this.list.show();
11630         
11631         this.restrictHeight();
11632         
11633         if(this.tickable){
11634             
11635             this.tickItems = Roo.apply([], this.item);
11636             
11637             this.okBtn.show();
11638             this.cancelBtn.show();
11639             this.trigger.hide();
11640             
11641         }
11642         
11643         Roo.get(document).on('mousedown', this.collapseIf, this);
11644         Roo.get(document).on('mousewheel', this.collapseIf, this);
11645         if (!this.editable) {
11646             Roo.get(document).on('keydown', this.listKeyPress, this);
11647         }
11648         
11649         this.fireEvent('expand', this);
11650     },
11651
11652     // private
11653     // Implements the default empty TriggerField.onTriggerClick function
11654     onTriggerClick : function(e)
11655     {
11656         Roo.log('trigger click');
11657         
11658         if(this.disabled || !this.triggerList){
11659             return;
11660         }
11661         
11662         this.page = 0;
11663         this.loadNext = false;
11664         
11665         if(this.isExpanded()){
11666             this.collapse();
11667             if (!this.blockFocus) {
11668                 this.inputEl().focus();
11669             }
11670             
11671         }else {
11672             this.hasFocus = true;
11673             if(this.triggerAction == 'all') {
11674                 this.doQuery(this.allQuery, true);
11675             } else {
11676                 this.doQuery(this.getRawValue());
11677             }
11678             if (!this.blockFocus) {
11679                 this.inputEl().focus();
11680             }
11681         }
11682     },
11683     
11684     onTickableTriggerClick : function(e)
11685     {
11686         if(this.disabled){
11687             return;
11688         }
11689         
11690         this.page = 0;
11691         this.loadNext = false;
11692         this.hasFocus = true;
11693         
11694         if(this.triggerAction == 'all') {
11695             this.doQuery(this.allQuery, true);
11696         } else {
11697             this.doQuery(this.getRawValue());
11698         }
11699     },
11700     
11701     onSearchFieldClick : function(e)
11702     {
11703         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11704             return;
11705         }
11706         
11707         this.page = 0;
11708         this.loadNext = false;
11709         this.hasFocus = true;
11710         
11711         if(this.triggerAction == 'all') {
11712             this.doQuery(this.allQuery, true);
11713         } else {
11714             this.doQuery(this.getRawValue());
11715         }
11716     },
11717     
11718     listKeyPress : function(e)
11719     {
11720         //Roo.log('listkeypress');
11721         // scroll to first matching element based on key pres..
11722         if (e.isSpecialKey()) {
11723             return false;
11724         }
11725         var k = String.fromCharCode(e.getKey()).toUpperCase();
11726         //Roo.log(k);
11727         var match  = false;
11728         var csel = this.view.getSelectedNodes();
11729         var cselitem = false;
11730         if (csel.length) {
11731             var ix = this.view.indexOf(csel[0]);
11732             cselitem  = this.store.getAt(ix);
11733             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11734                 cselitem = false;
11735             }
11736             
11737         }
11738         
11739         this.store.each(function(v) { 
11740             if (cselitem) {
11741                 // start at existing selection.
11742                 if (cselitem.id == v.id) {
11743                     cselitem = false;
11744                 }
11745                 return true;
11746             }
11747                 
11748             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11749                 match = this.store.indexOf(v);
11750                 return false;
11751             }
11752             return true;
11753         }, this);
11754         
11755         if (match === false) {
11756             return true; // no more action?
11757         }
11758         // scroll to?
11759         this.view.select(match);
11760         var sn = Roo.get(this.view.getSelectedNodes()[0])
11761         //sn.scrollIntoView(sn.dom.parentNode, false);
11762     },
11763     
11764     onViewScroll : function(e, t){
11765         
11766         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){
11767             return;
11768         }
11769         
11770         this.hasQuery = true;
11771         
11772         this.loading = this.list.select('.loading', true).first();
11773         
11774         if(this.loading === null){
11775             this.list.createChild({
11776                 tag: 'div',
11777                 cls: 'loading select2-more-results select2-active',
11778                 html: 'Loading more results...'
11779             })
11780             
11781             this.loading = this.list.select('.loading', true).first();
11782             
11783             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11784             
11785             this.loading.hide();
11786         }
11787         
11788         this.loading.show();
11789         
11790         var _combo = this;
11791         
11792         this.page++;
11793         this.loadNext = true;
11794         
11795         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11796         
11797         return;
11798     },
11799     
11800     addItem : function(o)
11801     {   
11802         var dv = ''; // display value
11803         
11804         if (this.displayField) {
11805             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11806         } else {
11807             // this is an error condition!!!
11808             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11809         }
11810         
11811         if(!dv.length){
11812             return;
11813         }
11814         
11815         var choice = this.choices.createChild({
11816             tag: 'li',
11817             cls: 'select2-search-choice',
11818             cn: [
11819                 {
11820                     tag: 'div',
11821                     html: dv
11822                 },
11823                 {
11824                     tag: 'a',
11825                     href: '#',
11826                     cls: 'select2-search-choice-close',
11827                     tabindex: '-1'
11828                 }
11829             ]
11830             
11831         }, this.searchField);
11832         
11833         var close = choice.select('a.select2-search-choice-close', true).first()
11834         
11835         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11836         
11837         this.item.push(o);
11838         
11839         this.lastData = o;
11840         
11841         this.syncValue();
11842         
11843         this.inputEl().dom.value = '';
11844         
11845     },
11846     
11847     onRemoveItem : function(e, _self, o)
11848     {
11849         e.preventDefault();
11850         var index = this.item.indexOf(o.data) * 1;
11851         
11852         if( index < 0){
11853             Roo.log('not this item?!');
11854             return;
11855         }
11856         
11857         this.item.splice(index, 1);
11858         o.item.remove();
11859         
11860         this.syncValue();
11861         
11862         this.fireEvent('remove', this, e);
11863         
11864     },
11865     
11866     syncValue : function()
11867     {
11868         if(!this.item.length){
11869             this.clearValue();
11870             return;
11871         }
11872             
11873         var value = [];
11874         var _this = this;
11875         Roo.each(this.item, function(i){
11876             if(_this.valueField){
11877                 value.push(i[_this.valueField]);
11878                 return;
11879             }
11880
11881             value.push(i);
11882         });
11883
11884         this.value = value.join(',');
11885
11886         if(this.hiddenField){
11887             this.hiddenField.dom.value = this.value;
11888         }
11889     },
11890     
11891     clearItem : function()
11892     {
11893         if(!this.multiple){
11894             return;
11895         }
11896         
11897         this.item = [];
11898         
11899         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11900            c.remove();
11901         });
11902         
11903         this.syncValue();
11904     },
11905     
11906     inputEl: function ()
11907     {
11908         if(this.tickable){
11909             return this.searchField;
11910         }
11911         return this.el.select('input.form-control',true).first();
11912     },
11913     
11914     
11915     onTickableFooterButtonClick : function(e, btn, el)
11916     {
11917         e.preventDefault();
11918         
11919         if(btn && btn.name == 'cancel'){
11920             this.tickItems = Roo.apply([], this.item);
11921             this.collapse();
11922             return;
11923         }
11924         
11925         this.clearItem();
11926         
11927         var _this = this;
11928         
11929         Roo.each(this.tickItems, function(o){
11930             _this.addItem(o);
11931         });
11932         
11933         this.collapse();
11934         
11935     }
11936     
11937     
11938
11939     /** 
11940     * @cfg {Boolean} grow 
11941     * @hide 
11942     */
11943     /** 
11944     * @cfg {Number} growMin 
11945     * @hide 
11946     */
11947     /** 
11948     * @cfg {Number} growMax 
11949     * @hide 
11950     */
11951     /**
11952      * @hide
11953      * @method autoSize
11954      */
11955 });
11956 /*
11957  * Based on:
11958  * Ext JS Library 1.1.1
11959  * Copyright(c) 2006-2007, Ext JS, LLC.
11960  *
11961  * Originally Released Under LGPL - original licence link has changed is not relivant.
11962  *
11963  * Fork - LGPL
11964  * <script type="text/javascript">
11965  */
11966
11967 /**
11968  * @class Roo.View
11969  * @extends Roo.util.Observable
11970  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11971  * This class also supports single and multi selection modes. <br>
11972  * Create a data model bound view:
11973  <pre><code>
11974  var store = new Roo.data.Store(...);
11975
11976  var view = new Roo.View({
11977     el : "my-element",
11978     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11979  
11980     singleSelect: true,
11981     selectedClass: "ydataview-selected",
11982     store: store
11983  });
11984
11985  // listen for node click?
11986  view.on("click", function(vw, index, node, e){
11987  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11988  });
11989
11990  // load XML data
11991  dataModel.load("foobar.xml");
11992  </code></pre>
11993  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11994  * <br><br>
11995  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11996  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11997  * 
11998  * Note: old style constructor is still suported (container, template, config)
11999  * 
12000  * @constructor
12001  * Create a new View
12002  * @param {Object} config The config object
12003  * 
12004  */
12005 Roo.View = function(config, depreciated_tpl, depreciated_config){
12006     
12007     this.parent = false;
12008     
12009     if (typeof(depreciated_tpl) == 'undefined') {
12010         // new way.. - universal constructor.
12011         Roo.apply(this, config);
12012         this.el  = Roo.get(this.el);
12013     } else {
12014         // old format..
12015         this.el  = Roo.get(config);
12016         this.tpl = depreciated_tpl;
12017         Roo.apply(this, depreciated_config);
12018     }
12019     this.wrapEl  = this.el.wrap().wrap();
12020     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12021     
12022     
12023     if(typeof(this.tpl) == "string"){
12024         this.tpl = new Roo.Template(this.tpl);
12025     } else {
12026         // support xtype ctors..
12027         this.tpl = new Roo.factory(this.tpl, Roo);
12028     }
12029     
12030     
12031     this.tpl.compile();
12032     
12033     /** @private */
12034     this.addEvents({
12035         /**
12036          * @event beforeclick
12037          * Fires before a click is processed. Returns false to cancel the default action.
12038          * @param {Roo.View} this
12039          * @param {Number} index The index of the target node
12040          * @param {HTMLElement} node The target node
12041          * @param {Roo.EventObject} e The raw event object
12042          */
12043             "beforeclick" : true,
12044         /**
12045          * @event click
12046          * Fires when a template node is clicked.
12047          * @param {Roo.View} this
12048          * @param {Number} index The index of the target node
12049          * @param {HTMLElement} node The target node
12050          * @param {Roo.EventObject} e The raw event object
12051          */
12052             "click" : true,
12053         /**
12054          * @event dblclick
12055          * Fires when a template node is double clicked.
12056          * @param {Roo.View} this
12057          * @param {Number} index The index of the target node
12058          * @param {HTMLElement} node The target node
12059          * @param {Roo.EventObject} e The raw event object
12060          */
12061             "dblclick" : true,
12062         /**
12063          * @event contextmenu
12064          * Fires when a template node is right clicked.
12065          * @param {Roo.View} this
12066          * @param {Number} index The index of the target node
12067          * @param {HTMLElement} node The target node
12068          * @param {Roo.EventObject} e The raw event object
12069          */
12070             "contextmenu" : true,
12071         /**
12072          * @event selectionchange
12073          * Fires when the selected nodes change.
12074          * @param {Roo.View} this
12075          * @param {Array} selections Array of the selected nodes
12076          */
12077             "selectionchange" : true,
12078     
12079         /**
12080          * @event beforeselect
12081          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12082          * @param {Roo.View} this
12083          * @param {HTMLElement} node The node to be selected
12084          * @param {Array} selections Array of currently selected nodes
12085          */
12086             "beforeselect" : true,
12087         /**
12088          * @event preparedata
12089          * Fires on every row to render, to allow you to change the data.
12090          * @param {Roo.View} this
12091          * @param {Object} data to be rendered (change this)
12092          */
12093           "preparedata" : true
12094           
12095           
12096         });
12097
12098
12099
12100     this.el.on({
12101         "click": this.onClick,
12102         "dblclick": this.onDblClick,
12103         "contextmenu": this.onContextMenu,
12104         scope:this
12105     });
12106
12107     this.selections = [];
12108     this.nodes = [];
12109     this.cmp = new Roo.CompositeElementLite([]);
12110     if(this.store){
12111         this.store = Roo.factory(this.store, Roo.data);
12112         this.setStore(this.store, true);
12113     }
12114     
12115     if ( this.footer && this.footer.xtype) {
12116            
12117          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12118         
12119         this.footer.dataSource = this.store
12120         this.footer.container = fctr;
12121         this.footer = Roo.factory(this.footer, Roo);
12122         fctr.insertFirst(this.el);
12123         
12124         // this is a bit insane - as the paging toolbar seems to detach the el..
12125 //        dom.parentNode.parentNode.parentNode
12126          // they get detached?
12127     }
12128     
12129     
12130     Roo.View.superclass.constructor.call(this);
12131     
12132     
12133 };
12134
12135 Roo.extend(Roo.View, Roo.util.Observable, {
12136     
12137      /**
12138      * @cfg {Roo.data.Store} store Data store to load data from.
12139      */
12140     store : false,
12141     
12142     /**
12143      * @cfg {String|Roo.Element} el The container element.
12144      */
12145     el : '',
12146     
12147     /**
12148      * @cfg {String|Roo.Template} tpl The template used by this View 
12149      */
12150     tpl : false,
12151     /**
12152      * @cfg {String} dataName the named area of the template to use as the data area
12153      *                          Works with domtemplates roo-name="name"
12154      */
12155     dataName: false,
12156     /**
12157      * @cfg {String} selectedClass The css class to add to selected nodes
12158      */
12159     selectedClass : "x-view-selected",
12160      /**
12161      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12162      */
12163     emptyText : "",
12164     
12165     /**
12166      * @cfg {String} text to display on mask (default Loading)
12167      */
12168     mask : false,
12169     /**
12170      * @cfg {Boolean} multiSelect Allow multiple selection
12171      */
12172     multiSelect : false,
12173     /**
12174      * @cfg {Boolean} singleSelect Allow single selection
12175      */
12176     singleSelect:  false,
12177     
12178     /**
12179      * @cfg {Boolean} toggleSelect - selecting 
12180      */
12181     toggleSelect : false,
12182     
12183     /**
12184      * @cfg {Boolean} tickable - selecting 
12185      */
12186     tickable : false,
12187     
12188     /**
12189      * Returns the element this view is bound to.
12190      * @return {Roo.Element}
12191      */
12192     getEl : function(){
12193         return this.wrapEl;
12194     },
12195     
12196     
12197
12198     /**
12199      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12200      */
12201     refresh : function(){
12202         Roo.log('refresh');
12203         var t = this.tpl;
12204         
12205         // if we are using something like 'domtemplate', then
12206         // the what gets used is:
12207         // t.applySubtemplate(NAME, data, wrapping data..)
12208         // the outer template then get' applied with
12209         //     the store 'extra data'
12210         // and the body get's added to the
12211         //      roo-name="data" node?
12212         //      <span class='roo-tpl-{name}'></span> ?????
12213         
12214         
12215         
12216         this.clearSelections();
12217         this.el.update("");
12218         var html = [];
12219         var records = this.store.getRange();
12220         if(records.length < 1) {
12221             
12222             // is this valid??  = should it render a template??
12223             
12224             this.el.update(this.emptyText);
12225             return;
12226         }
12227         var el = this.el;
12228         if (this.dataName) {
12229             this.el.update(t.apply(this.store.meta)); //????
12230             el = this.el.child('.roo-tpl-' + this.dataName);
12231         }
12232         
12233         for(var i = 0, len = records.length; i < len; i++){
12234             var data = this.prepareData(records[i].data, i, records[i]);
12235             this.fireEvent("preparedata", this, data, i, records[i]);
12236             
12237             var d = Roo.apply({}, data);
12238             
12239             if(this.tickable){
12240                 Roo.apply(d, {'roo-id' : Roo.id()});
12241                 
12242                 var _this = this;
12243             
12244                 Roo.each(this.parent.item, function(item){
12245                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12246                         return;
12247                     }
12248                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12249                 });
12250             }
12251             
12252             html[html.length] = Roo.util.Format.trim(
12253                 this.dataName ?
12254                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12255                     t.apply(d)
12256             );
12257         }
12258         
12259         
12260         
12261         el.update(html.join(""));
12262         this.nodes = el.dom.childNodes;
12263         this.updateIndexes(0);
12264     },
12265     
12266
12267     /**
12268      * Function to override to reformat the data that is sent to
12269      * the template for each node.
12270      * DEPRICATED - use the preparedata event handler.
12271      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12272      * a JSON object for an UpdateManager bound view).
12273      */
12274     prepareData : function(data, index, record)
12275     {
12276         this.fireEvent("preparedata", this, data, index, record);
12277         return data;
12278     },
12279
12280     onUpdate : function(ds, record){
12281          Roo.log('on update');   
12282         this.clearSelections();
12283         var index = this.store.indexOf(record);
12284         var n = this.nodes[index];
12285         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12286         n.parentNode.removeChild(n);
12287         this.updateIndexes(index, index);
12288     },
12289
12290     
12291     
12292 // --------- FIXME     
12293     onAdd : function(ds, records, index)
12294     {
12295         Roo.log(['on Add', ds, records, index] );        
12296         this.clearSelections();
12297         if(this.nodes.length == 0){
12298             this.refresh();
12299             return;
12300         }
12301         var n = this.nodes[index];
12302         for(var i = 0, len = records.length; i < len; i++){
12303             var d = this.prepareData(records[i].data, i, records[i]);
12304             if(n){
12305                 this.tpl.insertBefore(n, d);
12306             }else{
12307                 
12308                 this.tpl.append(this.el, d);
12309             }
12310         }
12311         this.updateIndexes(index);
12312     },
12313
12314     onRemove : function(ds, record, index){
12315         Roo.log('onRemove');
12316         this.clearSelections();
12317         var el = this.dataName  ?
12318             this.el.child('.roo-tpl-' + this.dataName) :
12319             this.el; 
12320         
12321         el.dom.removeChild(this.nodes[index]);
12322         this.updateIndexes(index);
12323     },
12324
12325     /**
12326      * Refresh an individual node.
12327      * @param {Number} index
12328      */
12329     refreshNode : function(index){
12330         this.onUpdate(this.store, this.store.getAt(index));
12331     },
12332
12333     updateIndexes : function(startIndex, endIndex){
12334         var ns = this.nodes;
12335         startIndex = startIndex || 0;
12336         endIndex = endIndex || ns.length - 1;
12337         for(var i = startIndex; i <= endIndex; i++){
12338             ns[i].nodeIndex = i;
12339         }
12340     },
12341
12342     /**
12343      * Changes the data store this view uses and refresh the view.
12344      * @param {Store} store
12345      */
12346     setStore : function(store, initial){
12347         if(!initial && this.store){
12348             this.store.un("datachanged", this.refresh);
12349             this.store.un("add", this.onAdd);
12350             this.store.un("remove", this.onRemove);
12351             this.store.un("update", this.onUpdate);
12352             this.store.un("clear", this.refresh);
12353             this.store.un("beforeload", this.onBeforeLoad);
12354             this.store.un("load", this.onLoad);
12355             this.store.un("loadexception", this.onLoad);
12356         }
12357         if(store){
12358           
12359             store.on("datachanged", this.refresh, this);
12360             store.on("add", this.onAdd, this);
12361             store.on("remove", this.onRemove, this);
12362             store.on("update", this.onUpdate, this);
12363             store.on("clear", this.refresh, this);
12364             store.on("beforeload", this.onBeforeLoad, this);
12365             store.on("load", this.onLoad, this);
12366             store.on("loadexception", this.onLoad, this);
12367         }
12368         
12369         if(store){
12370             this.refresh();
12371         }
12372     },
12373     /**
12374      * onbeforeLoad - masks the loading area.
12375      *
12376      */
12377     onBeforeLoad : function(store,opts)
12378     {
12379          Roo.log('onBeforeLoad');   
12380         if (!opts.add) {
12381             this.el.update("");
12382         }
12383         this.el.mask(this.mask ? this.mask : "Loading" ); 
12384     },
12385     onLoad : function ()
12386     {
12387         this.el.unmask();
12388     },
12389     
12390
12391     /**
12392      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12393      * @param {HTMLElement} node
12394      * @return {HTMLElement} The template node
12395      */
12396     findItemFromChild : function(node){
12397         var el = this.dataName  ?
12398             this.el.child('.roo-tpl-' + this.dataName,true) :
12399             this.el.dom; 
12400         
12401         if(!node || node.parentNode == el){
12402                     return node;
12403             }
12404             var p = node.parentNode;
12405             while(p && p != el){
12406             if(p.parentNode == el){
12407                 return p;
12408             }
12409             p = p.parentNode;
12410         }
12411             return null;
12412     },
12413
12414     /** @ignore */
12415     onClick : function(e){
12416         var item = this.findItemFromChild(e.getTarget());
12417         if(item){
12418             var index = this.indexOf(item);
12419             if(this.onItemClick(item, index, e) !== false){
12420                 this.fireEvent("click", this, index, item, e);
12421             }
12422         }else{
12423             this.clearSelections();
12424         }
12425     },
12426
12427     /** @ignore */
12428     onContextMenu : function(e){
12429         var item = this.findItemFromChild(e.getTarget());
12430         if(item){
12431             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12432         }
12433     },
12434
12435     /** @ignore */
12436     onDblClick : function(e){
12437         var item = this.findItemFromChild(e.getTarget());
12438         if(item){
12439             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12440         }
12441     },
12442
12443     onItemClick : function(item, index, e)
12444     {
12445         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12446             return false;
12447         }
12448         if (this.toggleSelect) {
12449             var m = this.isSelected(item) ? 'unselect' : 'select';
12450             Roo.log(m);
12451             var _t = this;
12452             _t[m](item, true, false);
12453             return true;
12454         }
12455         if(this.multiSelect || this.singleSelect){
12456             if(this.multiSelect && e.shiftKey && this.lastSelection){
12457                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12458             }else{
12459                 this.select(item, this.multiSelect && e.ctrlKey);
12460                 this.lastSelection = item;
12461             }
12462             
12463             if(!this.tickable){
12464                 e.preventDefault();
12465             }
12466             
12467         }
12468         return true;
12469     },
12470
12471     /**
12472      * Get the number of selected nodes.
12473      * @return {Number}
12474      */
12475     getSelectionCount : function(){
12476         return this.selections.length;
12477     },
12478
12479     /**
12480      * Get the currently selected nodes.
12481      * @return {Array} An array of HTMLElements
12482      */
12483     getSelectedNodes : function(){
12484         return this.selections;
12485     },
12486
12487     /**
12488      * Get the indexes of the selected nodes.
12489      * @return {Array}
12490      */
12491     getSelectedIndexes : function(){
12492         var indexes = [], s = this.selections;
12493         for(var i = 0, len = s.length; i < len; i++){
12494             indexes.push(s[i].nodeIndex);
12495         }
12496         return indexes;
12497     },
12498
12499     /**
12500      * Clear all selections
12501      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12502      */
12503     clearSelections : function(suppressEvent){
12504         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12505             this.cmp.elements = this.selections;
12506             this.cmp.removeClass(this.selectedClass);
12507             this.selections = [];
12508             if(!suppressEvent){
12509                 this.fireEvent("selectionchange", this, this.selections);
12510             }
12511         }
12512     },
12513
12514     /**
12515      * Returns true if the passed node is selected
12516      * @param {HTMLElement/Number} node The node or node index
12517      * @return {Boolean}
12518      */
12519     isSelected : function(node){
12520         var s = this.selections;
12521         if(s.length < 1){
12522             return false;
12523         }
12524         node = this.getNode(node);
12525         return s.indexOf(node) !== -1;
12526     },
12527
12528     /**
12529      * Selects nodes.
12530      * @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
12531      * @param {Boolean} keepExisting (optional) true to keep existing selections
12532      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12533      */
12534     select : function(nodeInfo, keepExisting, suppressEvent){
12535         if(nodeInfo instanceof Array){
12536             if(!keepExisting){
12537                 this.clearSelections(true);
12538             }
12539             for(var i = 0, len = nodeInfo.length; i < len; i++){
12540                 this.select(nodeInfo[i], true, true);
12541             }
12542             return;
12543         } 
12544         var node = this.getNode(nodeInfo);
12545         if(!node || this.isSelected(node)){
12546             return; // already selected.
12547         }
12548         if(!keepExisting){
12549             this.clearSelections(true);
12550         }
12551         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12552             Roo.fly(node).addClass(this.selectedClass);
12553             this.selections.push(node);
12554             if(!suppressEvent){
12555                 this.fireEvent("selectionchange", this, this.selections);
12556             }
12557         }
12558         
12559         
12560     },
12561       /**
12562      * Unselects nodes.
12563      * @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
12564      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12565      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12566      */
12567     unselect : function(nodeInfo, keepExisting, suppressEvent)
12568     {
12569         if(nodeInfo instanceof Array){
12570             Roo.each(this.selections, function(s) {
12571                 this.unselect(s, nodeInfo);
12572             }, this);
12573             return;
12574         }
12575         var node = this.getNode(nodeInfo);
12576         if(!node || !this.isSelected(node)){
12577             Roo.log("not selected");
12578             return; // not selected.
12579         }
12580         // fireevent???
12581         var ns = [];
12582         Roo.each(this.selections, function(s) {
12583             if (s == node ) {
12584                 Roo.fly(node).removeClass(this.selectedClass);
12585
12586                 return;
12587             }
12588             ns.push(s);
12589         },this);
12590         
12591         this.selections= ns;
12592         this.fireEvent("selectionchange", this, this.selections);
12593     },
12594
12595     /**
12596      * Gets a template node.
12597      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12598      * @return {HTMLElement} The node or null if it wasn't found
12599      */
12600     getNode : function(nodeInfo){
12601         if(typeof nodeInfo == "string"){
12602             return document.getElementById(nodeInfo);
12603         }else if(typeof nodeInfo == "number"){
12604             return this.nodes[nodeInfo];
12605         }
12606         return nodeInfo;
12607     },
12608
12609     /**
12610      * Gets a range template nodes.
12611      * @param {Number} startIndex
12612      * @param {Number} endIndex
12613      * @return {Array} An array of nodes
12614      */
12615     getNodes : function(start, end){
12616         var ns = this.nodes;
12617         start = start || 0;
12618         end = typeof end == "undefined" ? ns.length - 1 : end;
12619         var nodes = [];
12620         if(start <= end){
12621             for(var i = start; i <= end; i++){
12622                 nodes.push(ns[i]);
12623             }
12624         } else{
12625             for(var i = start; i >= end; i--){
12626                 nodes.push(ns[i]);
12627             }
12628         }
12629         return nodes;
12630     },
12631
12632     /**
12633      * Finds the index of the passed node
12634      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12635      * @return {Number} The index of the node or -1
12636      */
12637     indexOf : function(node){
12638         node = this.getNode(node);
12639         if(typeof node.nodeIndex == "number"){
12640             return node.nodeIndex;
12641         }
12642         var ns = this.nodes;
12643         for(var i = 0, len = ns.length; i < len; i++){
12644             if(ns[i] == node){
12645                 return i;
12646             }
12647         }
12648         return -1;
12649     }
12650 });
12651 /*
12652  * - LGPL
12653  *
12654  * based on jquery fullcalendar
12655  * 
12656  */
12657
12658 Roo.bootstrap = Roo.bootstrap || {};
12659 /**
12660  * @class Roo.bootstrap.Calendar
12661  * @extends Roo.bootstrap.Component
12662  * Bootstrap Calendar class
12663  * @cfg {Boolean} loadMask (true|false) default false
12664  * @cfg {Object} header generate the user specific header of the calendar, default false
12665
12666  * @constructor
12667  * Create a new Container
12668  * @param {Object} config The config object
12669  */
12670
12671
12672
12673 Roo.bootstrap.Calendar = function(config){
12674     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12675      this.addEvents({
12676         /**
12677              * @event select
12678              * Fires when a date is selected
12679              * @param {DatePicker} this
12680              * @param {Date} date The selected date
12681              */
12682         'select': true,
12683         /**
12684              * @event monthchange
12685              * Fires when the displayed month changes 
12686              * @param {DatePicker} this
12687              * @param {Date} date The selected month
12688              */
12689         'monthchange': true,
12690         /**
12691              * @event evententer
12692              * Fires when mouse over an event
12693              * @param {Calendar} this
12694              * @param {event} Event
12695              */
12696         'evententer': true,
12697         /**
12698              * @event eventleave
12699              * Fires when the mouse leaves an
12700              * @param {Calendar} this
12701              * @param {event}
12702              */
12703         'eventleave': true,
12704         /**
12705              * @event eventclick
12706              * Fires when the mouse click an
12707              * @param {Calendar} this
12708              * @param {event}
12709              */
12710         'eventclick': true
12711         
12712     });
12713
12714 };
12715
12716 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12717     
12718      /**
12719      * @cfg {Number} startDay
12720      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12721      */
12722     startDay : 0,
12723     
12724     loadMask : false,
12725     
12726     header : false,
12727       
12728     getAutoCreate : function(){
12729         
12730         
12731         var fc_button = function(name, corner, style, content ) {
12732             return Roo.apply({},{
12733                 tag : 'span',
12734                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12735                          (corner.length ?
12736                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12737                             ''
12738                         ),
12739                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12740                 unselectable: 'on'
12741             });
12742         };
12743         
12744         var header = {};
12745         
12746         if(!this.header){
12747             header = {
12748                 tag : 'table',
12749                 cls : 'fc-header',
12750                 style : 'width:100%',
12751                 cn : [
12752                     {
12753                         tag: 'tr',
12754                         cn : [
12755                             {
12756                                 tag : 'td',
12757                                 cls : 'fc-header-left',
12758                                 cn : [
12759                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12760                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12761                                     { tag: 'span', cls: 'fc-header-space' },
12762                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12763
12764
12765                                 ]
12766                             },
12767
12768                             {
12769                                 tag : 'td',
12770                                 cls : 'fc-header-center',
12771                                 cn : [
12772                                     {
12773                                         tag: 'span',
12774                                         cls: 'fc-header-title',
12775                                         cn : {
12776                                             tag: 'H2',
12777                                             html : 'month / year'
12778                                         }
12779                                     }
12780
12781                                 ]
12782                             },
12783                             {
12784                                 tag : 'td',
12785                                 cls : 'fc-header-right',
12786                                 cn : [
12787                               /*      fc_button('month', 'left', '', 'month' ),
12788                                     fc_button('week', '', '', 'week' ),
12789                                     fc_button('day', 'right', '', 'day' )
12790                                 */    
12791
12792                                 ]
12793                             }
12794
12795                         ]
12796                     }
12797                 ]
12798             };
12799         }
12800         
12801         header = this.header;
12802         
12803        
12804         var cal_heads = function() {
12805             var ret = [];
12806             // fixme - handle this.
12807             
12808             for (var i =0; i < Date.dayNames.length; i++) {
12809                 var d = Date.dayNames[i];
12810                 ret.push({
12811                     tag: 'th',
12812                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12813                     html : d.substring(0,3)
12814                 });
12815                 
12816             }
12817             ret[0].cls += ' fc-first';
12818             ret[6].cls += ' fc-last';
12819             return ret;
12820         };
12821         var cal_cell = function(n) {
12822             return  {
12823                 tag: 'td',
12824                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12825                 cn : [
12826                     {
12827                         cn : [
12828                             {
12829                                 cls: 'fc-day-number',
12830                                 html: 'D'
12831                             },
12832                             {
12833                                 cls: 'fc-day-content',
12834                              
12835                                 cn : [
12836                                      {
12837                                         style: 'position: relative;' // height: 17px;
12838                                     }
12839                                 ]
12840                             }
12841                             
12842                             
12843                         ]
12844                     }
12845                 ]
12846                 
12847             }
12848         };
12849         var cal_rows = function() {
12850             
12851             var ret = []
12852             for (var r = 0; r < 6; r++) {
12853                 var row= {
12854                     tag : 'tr',
12855                     cls : 'fc-week',
12856                     cn : []
12857                 };
12858                 
12859                 for (var i =0; i < Date.dayNames.length; i++) {
12860                     var d = Date.dayNames[i];
12861                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12862
12863                 }
12864                 row.cn[0].cls+=' fc-first';
12865                 row.cn[0].cn[0].style = 'min-height:90px';
12866                 row.cn[6].cls+=' fc-last';
12867                 ret.push(row);
12868                 
12869             }
12870             ret[0].cls += ' fc-first';
12871             ret[4].cls += ' fc-prev-last';
12872             ret[5].cls += ' fc-last';
12873             return ret;
12874             
12875         };
12876         
12877         var cal_table = {
12878             tag: 'table',
12879             cls: 'fc-border-separate',
12880             style : 'width:100%',
12881             cellspacing  : 0,
12882             cn : [
12883                 { 
12884                     tag: 'thead',
12885                     cn : [
12886                         { 
12887                             tag: 'tr',
12888                             cls : 'fc-first fc-last',
12889                             cn : cal_heads()
12890                         }
12891                     ]
12892                 },
12893                 { 
12894                     tag: 'tbody',
12895                     cn : cal_rows()
12896                 }
12897                   
12898             ]
12899         };
12900          
12901          var cfg = {
12902             cls : 'fc fc-ltr',
12903             cn : [
12904                 header,
12905                 {
12906                     cls : 'fc-content',
12907                     style : "position: relative;",
12908                     cn : [
12909                         {
12910                             cls : 'fc-view fc-view-month fc-grid',
12911                             style : 'position: relative',
12912                             unselectable : 'on',
12913                             cn : [
12914                                 {
12915                                     cls : 'fc-event-container',
12916                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12917                                 },
12918                                 cal_table
12919                             ]
12920                         }
12921                     ]
12922     
12923                 }
12924            ] 
12925             
12926         };
12927         
12928          
12929         
12930         return cfg;
12931     },
12932     
12933     
12934     initEvents : function()
12935     {
12936         if(!this.store){
12937             throw "can not find store for calendar";
12938         }
12939         
12940         var mark = {
12941             tag: "div",
12942             cls:"x-dlg-mask",
12943             style: "text-align:center",
12944             cn: [
12945                 {
12946                     tag: "div",
12947                     style: "background-color:white;width:50%;margin:250 auto",
12948                     cn: [
12949                         {
12950                             tag: "img",
12951                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12952                         },
12953                         {
12954                             tag: "span",
12955                             html: "Loading"
12956                         }
12957                         
12958                     ]
12959                 }
12960             ]
12961         }
12962         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12963         
12964         var size = this.el.select('.fc-content', true).first().getSize();
12965         this.maskEl.setSize(size.width, size.height);
12966         this.maskEl.enableDisplayMode("block");
12967         if(!this.loadMask){
12968             this.maskEl.hide();
12969         }
12970         
12971         this.store = Roo.factory(this.store, Roo.data);
12972         this.store.on('load', this.onLoad, this);
12973         this.store.on('beforeload', this.onBeforeLoad, this);
12974         
12975         this.resize();
12976         
12977         this.cells = this.el.select('.fc-day',true);
12978         //Roo.log(this.cells);
12979         this.textNodes = this.el.query('.fc-day-number');
12980         this.cells.addClassOnOver('fc-state-hover');
12981         
12982         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12983         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12984         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12985         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12986         
12987         this.on('monthchange', this.onMonthChange, this);
12988         
12989         this.update(new Date().clearTime());
12990     },
12991     
12992     resize : function() {
12993         var sz  = this.el.getSize();
12994         
12995         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12996         this.el.select('.fc-day-content div',true).setHeight(34);
12997     },
12998     
12999     
13000     // private
13001     showPrevMonth : function(e){
13002         this.update(this.activeDate.add("mo", -1));
13003     },
13004     showToday : function(e){
13005         this.update(new Date().clearTime());
13006     },
13007     // private
13008     showNextMonth : function(e){
13009         this.update(this.activeDate.add("mo", 1));
13010     },
13011
13012     // private
13013     showPrevYear : function(){
13014         this.update(this.activeDate.add("y", -1));
13015     },
13016
13017     // private
13018     showNextYear : function(){
13019         this.update(this.activeDate.add("y", 1));
13020     },
13021
13022     
13023    // private
13024     update : function(date)
13025     {
13026         var vd = this.activeDate;
13027         this.activeDate = date;
13028 //        if(vd && this.el){
13029 //            var t = date.getTime();
13030 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13031 //                Roo.log('using add remove');
13032 //                
13033 //                this.fireEvent('monthchange', this, date);
13034 //                
13035 //                this.cells.removeClass("fc-state-highlight");
13036 //                this.cells.each(function(c){
13037 //                   if(c.dateValue == t){
13038 //                       c.addClass("fc-state-highlight");
13039 //                       setTimeout(function(){
13040 //                            try{c.dom.firstChild.focus();}catch(e){}
13041 //                       }, 50);
13042 //                       return false;
13043 //                   }
13044 //                   return true;
13045 //                });
13046 //                return;
13047 //            }
13048 //        }
13049         
13050         var days = date.getDaysInMonth();
13051         
13052         var firstOfMonth = date.getFirstDateOfMonth();
13053         var startingPos = firstOfMonth.getDay()-this.startDay;
13054         
13055         if(startingPos < this.startDay){
13056             startingPos += 7;
13057         }
13058         
13059         var pm = date.add(Date.MONTH, -1);
13060         var prevStart = pm.getDaysInMonth()-startingPos;
13061 //        
13062         this.cells = this.el.select('.fc-day',true);
13063         this.textNodes = this.el.query('.fc-day-number');
13064         this.cells.addClassOnOver('fc-state-hover');
13065         
13066         var cells = this.cells.elements;
13067         var textEls = this.textNodes;
13068         
13069         Roo.each(cells, function(cell){
13070             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13071         });
13072         
13073         days += startingPos;
13074
13075         // convert everything to numbers so it's fast
13076         var day = 86400000;
13077         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13078         //Roo.log(d);
13079         //Roo.log(pm);
13080         //Roo.log(prevStart);
13081         
13082         var today = new Date().clearTime().getTime();
13083         var sel = date.clearTime().getTime();
13084         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13085         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13086         var ddMatch = this.disabledDatesRE;
13087         var ddText = this.disabledDatesText;
13088         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13089         var ddaysText = this.disabledDaysText;
13090         var format = this.format;
13091         
13092         var setCellClass = function(cal, cell){
13093             cell.row = 0;
13094             cell.events = [];
13095             cell.more = [];
13096             //Roo.log('set Cell Class');
13097             cell.title = "";
13098             var t = d.getTime();
13099             
13100             //Roo.log(d);
13101             
13102             cell.dateValue = t;
13103             if(t == today){
13104                 cell.className += " fc-today";
13105                 cell.className += " fc-state-highlight";
13106                 cell.title = cal.todayText;
13107             }
13108             if(t == sel){
13109                 // disable highlight in other month..
13110                 //cell.className += " fc-state-highlight";
13111                 
13112             }
13113             // disabling
13114             if(t < min) {
13115                 cell.className = " fc-state-disabled";
13116                 cell.title = cal.minText;
13117                 return;
13118             }
13119             if(t > max) {
13120                 cell.className = " fc-state-disabled";
13121                 cell.title = cal.maxText;
13122                 return;
13123             }
13124             if(ddays){
13125                 if(ddays.indexOf(d.getDay()) != -1){
13126                     cell.title = ddaysText;
13127                     cell.className = " fc-state-disabled";
13128                 }
13129             }
13130             if(ddMatch && format){
13131                 var fvalue = d.dateFormat(format);
13132                 if(ddMatch.test(fvalue)){
13133                     cell.title = ddText.replace("%0", fvalue);
13134                     cell.className = " fc-state-disabled";
13135                 }
13136             }
13137             
13138             if (!cell.initialClassName) {
13139                 cell.initialClassName = cell.dom.className;
13140             }
13141             
13142             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13143         };
13144
13145         var i = 0;
13146         
13147         for(; i < startingPos; i++) {
13148             textEls[i].innerHTML = (++prevStart);
13149             d.setDate(d.getDate()+1);
13150             
13151             cells[i].className = "fc-past fc-other-month";
13152             setCellClass(this, cells[i]);
13153         }
13154         
13155         var intDay = 0;
13156         
13157         for(; i < days; i++){
13158             intDay = i - startingPos + 1;
13159             textEls[i].innerHTML = (intDay);
13160             d.setDate(d.getDate()+1);
13161             
13162             cells[i].className = ''; // "x-date-active";
13163             setCellClass(this, cells[i]);
13164         }
13165         var extraDays = 0;
13166         
13167         for(; i < 42; i++) {
13168             textEls[i].innerHTML = (++extraDays);
13169             d.setDate(d.getDate()+1);
13170             
13171             cells[i].className = "fc-future fc-other-month";
13172             setCellClass(this, cells[i]);
13173         }
13174         
13175         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13176         
13177         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13178         
13179         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13180         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13181         
13182         if(totalRows != 6){
13183             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13184             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13185         }
13186         
13187         this.fireEvent('monthchange', this, date);
13188         
13189         
13190         /*
13191         if(!this.internalRender){
13192             var main = this.el.dom.firstChild;
13193             var w = main.offsetWidth;
13194             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13195             Roo.fly(main).setWidth(w);
13196             this.internalRender = true;
13197             // opera does not respect the auto grow header center column
13198             // then, after it gets a width opera refuses to recalculate
13199             // without a second pass
13200             if(Roo.isOpera && !this.secondPass){
13201                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13202                 this.secondPass = true;
13203                 this.update.defer(10, this, [date]);
13204             }
13205         }
13206         */
13207         
13208     },
13209     
13210     findCell : function(dt) {
13211         dt = dt.clearTime().getTime();
13212         var ret = false;
13213         this.cells.each(function(c){
13214             //Roo.log("check " +c.dateValue + '?=' + dt);
13215             if(c.dateValue == dt){
13216                 ret = c;
13217                 return false;
13218             }
13219             return true;
13220         });
13221         
13222         return ret;
13223     },
13224     
13225     findCells : function(ev) {
13226         var s = ev.start.clone().clearTime().getTime();
13227        // Roo.log(s);
13228         var e= ev.end.clone().clearTime().getTime();
13229        // Roo.log(e);
13230         var ret = [];
13231         this.cells.each(function(c){
13232              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13233             
13234             if(c.dateValue > e){
13235                 return ;
13236             }
13237             if(c.dateValue < s){
13238                 return ;
13239             }
13240             ret.push(c);
13241         });
13242         
13243         return ret;    
13244     },
13245     
13246 //    findBestRow: function(cells)
13247 //    {
13248 //        var ret = 0;
13249 //        
13250 //        for (var i =0 ; i < cells.length;i++) {
13251 //            ret  = Math.max(cells[i].rows || 0,ret);
13252 //        }
13253 //        return ret;
13254 //        
13255 //    },
13256     
13257     
13258     addItem : function(ev)
13259     {
13260         // look for vertical location slot in
13261         var cells = this.findCells(ev);
13262         
13263 //        ev.row = this.findBestRow(cells);
13264         
13265         // work out the location.
13266         
13267         var crow = false;
13268         var rows = [];
13269         for(var i =0; i < cells.length; i++) {
13270             
13271             cells[i].row = cells[0].row;
13272             
13273             if(i == 0){
13274                 cells[i].row = cells[i].row + 1;
13275             }
13276             
13277             if (!crow) {
13278                 crow = {
13279                     start : cells[i],
13280                     end :  cells[i]
13281                 };
13282                 continue;
13283             }
13284             if (crow.start.getY() == cells[i].getY()) {
13285                 // on same row.
13286                 crow.end = cells[i];
13287                 continue;
13288             }
13289             // different row.
13290             rows.push(crow);
13291             crow = {
13292                 start: cells[i],
13293                 end : cells[i]
13294             };
13295             
13296         }
13297         
13298         rows.push(crow);
13299         ev.els = [];
13300         ev.rows = rows;
13301         ev.cells = cells;
13302         
13303         cells[0].events.push(ev);
13304         
13305         this.calevents.push(ev);
13306     },
13307     
13308     clearEvents: function() {
13309         
13310         if(!this.calevents){
13311             return;
13312         }
13313         
13314         Roo.each(this.cells.elements, function(c){
13315             c.row = 0;
13316             c.events = [];
13317             c.more = [];
13318         });
13319         
13320         Roo.each(this.calevents, function(e) {
13321             Roo.each(e.els, function(el) {
13322                 el.un('mouseenter' ,this.onEventEnter, this);
13323                 el.un('mouseleave' ,this.onEventLeave, this);
13324                 el.remove();
13325             },this);
13326         },this);
13327         
13328         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13329             e.remove();
13330         });
13331         
13332     },
13333     
13334     renderEvents: function()
13335     {   
13336         var _this = this;
13337         
13338         this.cells.each(function(c) {
13339             
13340             if(c.row < 5){
13341                 return;
13342             }
13343             
13344             var ev = c.events;
13345             
13346             var r = 4;
13347             if(c.row != c.events.length){
13348                 r = 4 - (4 - (c.row - c.events.length));
13349             }
13350             
13351             c.events = ev.slice(0, r);
13352             c.more = ev.slice(r);
13353             
13354             if(c.more.length && c.more.length == 1){
13355                 c.events.push(c.more.pop());
13356             }
13357             
13358             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13359             
13360         });
13361             
13362         this.cells.each(function(c) {
13363             
13364             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13365             
13366             
13367             for (var e = 0; e < c.events.length; e++){
13368                 var ev = c.events[e];
13369                 var rows = ev.rows;
13370                 
13371                 for(var i = 0; i < rows.length; i++) {
13372                 
13373                     // how many rows should it span..
13374
13375                     var  cfg = {
13376                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13377                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13378
13379                         unselectable : "on",
13380                         cn : [
13381                             {
13382                                 cls: 'fc-event-inner',
13383                                 cn : [
13384     //                                {
13385     //                                  tag:'span',
13386     //                                  cls: 'fc-event-time',
13387     //                                  html : cells.length > 1 ? '' : ev.time
13388     //                                },
13389                                     {
13390                                       tag:'span',
13391                                       cls: 'fc-event-title',
13392                                       html : String.format('{0}', ev.title)
13393                                     }
13394
13395
13396                                 ]
13397                             },
13398                             {
13399                                 cls: 'ui-resizable-handle ui-resizable-e',
13400                                 html : '&nbsp;&nbsp;&nbsp'
13401                             }
13402
13403                         ]
13404                     };
13405
13406                     if (i == 0) {
13407                         cfg.cls += ' fc-event-start';
13408                     }
13409                     if ((i+1) == rows.length) {
13410                         cfg.cls += ' fc-event-end';
13411                     }
13412
13413                     var ctr = _this.el.select('.fc-event-container',true).first();
13414                     var cg = ctr.createChild(cfg);
13415
13416                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13417                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13418
13419                     var r = (c.more.length) ? 1 : 0;
13420                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13421                     cg.setWidth(ebox.right - sbox.x -2);
13422
13423                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13424                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13425                     cg.on('click', _this.onEventClick, _this, ev);
13426
13427                     ev.els.push(cg);
13428                     
13429                 }
13430                 
13431             }
13432             
13433             
13434             if(c.more.length){
13435                 var  cfg = {
13436                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13437                     style : 'position: absolute',
13438                     unselectable : "on",
13439                     cn : [
13440                         {
13441                             cls: 'fc-event-inner',
13442                             cn : [
13443                                 {
13444                                   tag:'span',
13445                                   cls: 'fc-event-title',
13446                                   html : 'More'
13447                                 }
13448
13449
13450                             ]
13451                         },
13452                         {
13453                             cls: 'ui-resizable-handle ui-resizable-e',
13454                             html : '&nbsp;&nbsp;&nbsp'
13455                         }
13456
13457                     ]
13458                 };
13459
13460                 var ctr = _this.el.select('.fc-event-container',true).first();
13461                 var cg = ctr.createChild(cfg);
13462
13463                 var sbox = c.select('.fc-day-content',true).first().getBox();
13464                 var ebox = c.select('.fc-day-content',true).first().getBox();
13465                 //Roo.log(cg);
13466                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13467                 cg.setWidth(ebox.right - sbox.x -2);
13468
13469                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13470                 
13471             }
13472             
13473         });
13474         
13475         
13476         
13477     },
13478     
13479     onEventEnter: function (e, el,event,d) {
13480         this.fireEvent('evententer', this, el, event);
13481     },
13482     
13483     onEventLeave: function (e, el,event,d) {
13484         this.fireEvent('eventleave', this, el, event);
13485     },
13486     
13487     onEventClick: function (e, el,event,d) {
13488         this.fireEvent('eventclick', this, el, event);
13489     },
13490     
13491     onMonthChange: function () {
13492         this.store.load();
13493     },
13494     
13495     onMoreEventClick: function(e, el, more)
13496     {
13497         var _this = this;
13498         
13499         this.calpopover.placement = 'right';
13500         this.calpopover.setTitle('More');
13501         
13502         this.calpopover.setContent('');
13503         
13504         var ctr = this.calpopover.el.select('.popover-content', true).first();
13505         
13506         Roo.each(more, function(m){
13507             var cfg = {
13508                 cls : 'fc-event-hori fc-event-draggable',
13509                 html : m.title
13510             }
13511             var cg = ctr.createChild(cfg);
13512             
13513             cg.on('click', _this.onEventClick, _this, m);
13514         });
13515         
13516         this.calpopover.show(el);
13517         
13518         
13519     },
13520     
13521     onLoad: function () 
13522     {   
13523         this.calevents = [];
13524         var cal = this;
13525         
13526         if(this.store.getCount() > 0){
13527             this.store.data.each(function(d){
13528                cal.addItem({
13529                     id : d.data.id,
13530                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13531                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13532                     time : d.data.start_time,
13533                     title : d.data.title,
13534                     description : d.data.description,
13535                     venue : d.data.venue
13536                 });
13537             });
13538         }
13539         
13540         this.renderEvents();
13541         
13542         if(this.calevents.length && this.loadMask){
13543             this.maskEl.hide();
13544         }
13545     },
13546     
13547     onBeforeLoad: function()
13548     {
13549         this.clearEvents();
13550         if(this.loadMask){
13551             this.maskEl.show();
13552         }
13553     }
13554 });
13555
13556  
13557  /*
13558  * - LGPL
13559  *
13560  * element
13561  * 
13562  */
13563
13564 /**
13565  * @class Roo.bootstrap.Popover
13566  * @extends Roo.bootstrap.Component
13567  * Bootstrap Popover class
13568  * @cfg {String} html contents of the popover   (or false to use children..)
13569  * @cfg {String} title of popover (or false to hide)
13570  * @cfg {String} placement how it is placed
13571  * @cfg {String} trigger click || hover (or false to trigger manually)
13572  * @cfg {String} over what (parent or false to trigger manually.)
13573  * 
13574  * @constructor
13575  * Create a new Popover
13576  * @param {Object} config The config object
13577  */
13578
13579 Roo.bootstrap.Popover = function(config){
13580     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13581 };
13582
13583 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13584     
13585     title: 'Fill in a title',
13586     html: false,
13587     
13588     placement : 'right',
13589     trigger : 'hover', // hover
13590     
13591     over: 'parent',
13592     
13593     can_build_overlaid : false,
13594     
13595     getChildContainer : function()
13596     {
13597         return this.el.select('.popover-content',true).first();
13598     },
13599     
13600     getAutoCreate : function(){
13601          Roo.log('make popover?');
13602         var cfg = {
13603            cls : 'popover roo-dynamic',
13604            style: 'display:block',
13605            cn : [
13606                 {
13607                     cls : 'arrow'
13608                 },
13609                 {
13610                     cls : 'popover-inner',
13611                     cn : [
13612                         {
13613                             tag: 'h3',
13614                             cls: 'popover-title',
13615                             html : this.title
13616                         },
13617                         {
13618                             cls : 'popover-content',
13619                             html : this.html
13620                         }
13621                     ]
13622                     
13623                 }
13624            ]
13625         };
13626         
13627         return cfg;
13628     },
13629     setTitle: function(str)
13630     {
13631         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13632     },
13633     setContent: function(str)
13634     {
13635         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13636     },
13637     // as it get's added to the bottom of the page.
13638     onRender : function(ct, position)
13639     {
13640         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13641         if(!this.el){
13642             var cfg = Roo.apply({},  this.getAutoCreate());
13643             cfg.id = Roo.id();
13644             
13645             if (this.cls) {
13646                 cfg.cls += ' ' + this.cls;
13647             }
13648             if (this.style) {
13649                 cfg.style = this.style;
13650             }
13651             Roo.log("adding to ")
13652             this.el = Roo.get(document.body).createChild(cfg, position);
13653             Roo.log(this.el);
13654         }
13655         this.initEvents();
13656     },
13657     
13658     initEvents : function()
13659     {
13660         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13661         this.el.enableDisplayMode('block');
13662         this.el.hide();
13663         if (this.over === false) {
13664             return; 
13665         }
13666         if (this.triggers === false) {
13667             return;
13668         }
13669         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13670         var triggers = this.trigger ? this.trigger.split(' ') : [];
13671         Roo.each(triggers, function(trigger) {
13672         
13673             if (trigger == 'click') {
13674                 on_el.on('click', this.toggle, this);
13675             } else if (trigger != 'manual') {
13676                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13677                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13678       
13679                 on_el.on(eventIn  ,this.enter, this);
13680                 on_el.on(eventOut, this.leave, this);
13681             }
13682         }, this);
13683         
13684     },
13685     
13686     
13687     // private
13688     timeout : null,
13689     hoverState : null,
13690     
13691     toggle : function () {
13692         this.hoverState == 'in' ? this.leave() : this.enter();
13693     },
13694     
13695     enter : function () {
13696        
13697     
13698         clearTimeout(this.timeout);
13699     
13700         this.hoverState = 'in'
13701     
13702         if (!this.delay || !this.delay.show) {
13703             this.show();
13704             return 
13705         }
13706         var _t = this;
13707         this.timeout = setTimeout(function () {
13708             if (_t.hoverState == 'in') {
13709                 _t.show();
13710             }
13711         }, this.delay.show)
13712     },
13713     leave : function() {
13714         clearTimeout(this.timeout);
13715     
13716         this.hoverState = 'out'
13717     
13718         if (!this.delay || !this.delay.hide) {
13719             this.hide();
13720             return 
13721         }
13722         var _t = this;
13723         this.timeout = setTimeout(function () {
13724             if (_t.hoverState == 'out') {
13725                 _t.hide();
13726             }
13727         }, this.delay.hide)
13728     },
13729     
13730     show : function (on_el)
13731     {
13732         if (!on_el) {
13733             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13734         }
13735         // set content.
13736         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13737         if (this.html !== false) {
13738             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13739         }
13740         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13741         if (!this.title.length) {
13742             this.el.select('.popover-title',true).hide();
13743         }
13744         
13745         var placement = typeof this.placement == 'function' ?
13746             this.placement.call(this, this.el, on_el) :
13747             this.placement;
13748             
13749         var autoToken = /\s?auto?\s?/i;
13750         var autoPlace = autoToken.test(placement);
13751         if (autoPlace) {
13752             placement = placement.replace(autoToken, '') || 'top';
13753         }
13754         
13755         //this.el.detach()
13756         //this.el.setXY([0,0]);
13757         this.el.show();
13758         this.el.dom.style.display='block';
13759         this.el.addClass(placement);
13760         
13761         //this.el.appendTo(on_el);
13762         
13763         var p = this.getPosition();
13764         var box = this.el.getBox();
13765         
13766         if (autoPlace) {
13767             // fixme..
13768         }
13769         var align = Roo.bootstrap.Popover.alignment[placement]
13770         this.el.alignTo(on_el, align[0],align[1]);
13771         //var arrow = this.el.select('.arrow',true).first();
13772         //arrow.set(align[2], 
13773         
13774         this.el.addClass('in');
13775         this.hoverState = null;
13776         
13777         if (this.el.hasClass('fade')) {
13778             // fade it?
13779         }
13780         
13781     },
13782     hide : function()
13783     {
13784         this.el.setXY([0,0]);
13785         this.el.removeClass('in');
13786         this.el.hide();
13787         
13788     }
13789     
13790 });
13791
13792 Roo.bootstrap.Popover.alignment = {
13793     'left' : ['r-l', [-10,0], 'right'],
13794     'right' : ['l-r', [10,0], 'left'],
13795     'bottom' : ['t-b', [0,10], 'top'],
13796     'top' : [ 'b-t', [0,-10], 'bottom']
13797 };
13798
13799  /*
13800  * - LGPL
13801  *
13802  * Progress
13803  * 
13804  */
13805
13806 /**
13807  * @class Roo.bootstrap.Progress
13808  * @extends Roo.bootstrap.Component
13809  * Bootstrap Progress class
13810  * @cfg {Boolean} striped striped of the progress bar
13811  * @cfg {Boolean} active animated of the progress bar
13812  * 
13813  * 
13814  * @constructor
13815  * Create a new Progress
13816  * @param {Object} config The config object
13817  */
13818
13819 Roo.bootstrap.Progress = function(config){
13820     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13821 };
13822
13823 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13824     
13825     striped : false,
13826     active: false,
13827     
13828     getAutoCreate : function(){
13829         var cfg = {
13830             tag: 'div',
13831             cls: 'progress'
13832         };
13833         
13834         
13835         if(this.striped){
13836             cfg.cls += ' progress-striped';
13837         }
13838       
13839         if(this.active){
13840             cfg.cls += ' active';
13841         }
13842         
13843         
13844         return cfg;
13845     }
13846    
13847 });
13848
13849  
13850
13851  /*
13852  * - LGPL
13853  *
13854  * ProgressBar
13855  * 
13856  */
13857
13858 /**
13859  * @class Roo.bootstrap.ProgressBar
13860  * @extends Roo.bootstrap.Component
13861  * Bootstrap ProgressBar class
13862  * @cfg {Number} aria_valuenow aria-value now
13863  * @cfg {Number} aria_valuemin aria-value min
13864  * @cfg {Number} aria_valuemax aria-value max
13865  * @cfg {String} label label for the progress bar
13866  * @cfg {String} panel (success | info | warning | danger )
13867  * @cfg {String} role role of the progress bar
13868  * @cfg {String} sr_only text
13869  * 
13870  * 
13871  * @constructor
13872  * Create a new ProgressBar
13873  * @param {Object} config The config object
13874  */
13875
13876 Roo.bootstrap.ProgressBar = function(config){
13877     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13878 };
13879
13880 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13881     
13882     aria_valuenow : 0,
13883     aria_valuemin : 0,
13884     aria_valuemax : 100,
13885     label : false,
13886     panel : false,
13887     role : false,
13888     sr_only: false,
13889     
13890     getAutoCreate : function()
13891     {
13892         
13893         var cfg = {
13894             tag: 'div',
13895             cls: 'progress-bar',
13896             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13897         };
13898         
13899         if(this.sr_only){
13900             cfg.cn = {
13901                 tag: 'span',
13902                 cls: 'sr-only',
13903                 html: this.sr_only
13904             }
13905         }
13906         
13907         if(this.role){
13908             cfg.role = this.role;
13909         }
13910         
13911         if(this.aria_valuenow){
13912             cfg['aria-valuenow'] = this.aria_valuenow;
13913         }
13914         
13915         if(this.aria_valuemin){
13916             cfg['aria-valuemin'] = this.aria_valuemin;
13917         }
13918         
13919         if(this.aria_valuemax){
13920             cfg['aria-valuemax'] = this.aria_valuemax;
13921         }
13922         
13923         if(this.label && !this.sr_only){
13924             cfg.html = this.label;
13925         }
13926         
13927         if(this.panel){
13928             cfg.cls += ' progress-bar-' + this.panel;
13929         }
13930         
13931         return cfg;
13932     },
13933     
13934     update : function(aria_valuenow)
13935     {
13936         this.aria_valuenow = aria_valuenow;
13937         
13938         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13939     }
13940    
13941 });
13942
13943  
13944
13945  /*
13946  * - LGPL
13947  *
13948  * column
13949  * 
13950  */
13951
13952 /**
13953  * @class Roo.bootstrap.TabGroup
13954  * @extends Roo.bootstrap.Column
13955  * Bootstrap Column class
13956  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13957  * @cfg {Boolean} carousel true to make the group behave like a carousel
13958  * 
13959  * @constructor
13960  * Create a new TabGroup
13961  * @param {Object} config The config object
13962  */
13963
13964 Roo.bootstrap.TabGroup = function(config){
13965     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13966     if (!this.navId) {
13967         this.navId = Roo.id();
13968     }
13969     this.tabs = [];
13970     Roo.bootstrap.TabGroup.register(this);
13971     
13972 };
13973
13974 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13975     
13976     carousel : false,
13977     transition : false,
13978      
13979     getAutoCreate : function()
13980     {
13981         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13982         
13983         cfg.cls += ' tab-content';
13984         
13985         if (this.carousel) {
13986             cfg.cls += ' carousel slide';
13987             cfg.cn = [{
13988                cls : 'carousel-inner'
13989             }]
13990         }
13991         
13992         
13993         return cfg;
13994     },
13995     getChildContainer : function()
13996     {
13997         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13998     },
13999     
14000     /**
14001     * register a Navigation item
14002     * @param {Roo.bootstrap.NavItem} the navitem to add
14003     */
14004     register : function(item)
14005     {
14006         this.tabs.push( item);
14007         item.navId = this.navId; // not really needed..
14008     
14009     },
14010     
14011     getActivePanel : function()
14012     {
14013         var r = false;
14014         Roo.each(this.tabs, function(t) {
14015             if (t.active) {
14016                 r = t;
14017                 return false;
14018             }
14019             return null;
14020         });
14021         return r;
14022         
14023     },
14024     getPanelByName : function(n)
14025     {
14026         var r = false;
14027         Roo.each(this.tabs, function(t) {
14028             if (t.tabId == n) {
14029                 r = t;
14030                 return false;
14031             }
14032             return null;
14033         });
14034         return r;
14035     },
14036     indexOfPanel : function(p)
14037     {
14038         var r = false;
14039         Roo.each(this.tabs, function(t,i) {
14040             if (t.tabId == p.tabId) {
14041                 r = i;
14042                 return false;
14043             }
14044             return null;
14045         });
14046         return r;
14047     },
14048     /**
14049      * show a specific panel
14050      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14051      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14052      */
14053     showPanel : function (pan)
14054     {
14055         
14056         if (typeof(pan) == 'number') {
14057             pan = this.tabs[pan];
14058         }
14059         if (typeof(pan) == 'string') {
14060             pan = this.getPanelByName(pan);
14061         }
14062         if (pan.tabId == this.getActivePanel().tabId) {
14063             return true;
14064         }
14065         var cur = this.getActivePanel();
14066         
14067         if (false === cur.fireEvent('beforedeactivate')) {
14068             return false;
14069         }
14070         
14071         if (this.carousel) {
14072             this.transition = true;
14073             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14074             var lr = dir == 'next' ? 'left' : 'right';
14075             pan.el.addClass(dir); // or prev
14076             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14077             cur.el.addClass(lr); // or right
14078             pan.el.addClass(lr);
14079             
14080             var _this = this;
14081             cur.el.on('transitionend', function() {
14082                 Roo.log("trans end?");
14083                 
14084                 pan.el.removeClass([lr,dir]);
14085                 pan.setActive(true);
14086                 
14087                 cur.el.removeClass([lr]);
14088                 cur.setActive(false);
14089                 
14090                 _this.transition = false;
14091                 
14092             }, this, { single:  true } );
14093             return true;
14094         }
14095         
14096         cur.setActive(false);
14097         pan.setActive(true);
14098         return true;
14099         
14100     },
14101     showPanelNext : function()
14102     {
14103         var i = this.indexOfPanel(this.getActivePanel());
14104         if (i > this.tabs.length) {
14105             return;
14106         }
14107         this.showPanel(this.tabs[i+1]);
14108     },
14109     showPanelPrev : function()
14110     {
14111         var i = this.indexOfPanel(this.getActivePanel());
14112         if (i  < 1) {
14113             return;
14114         }
14115         this.showPanel(this.tabs[i-1]);
14116     }
14117     
14118     
14119   
14120 });
14121
14122  
14123
14124  
14125  
14126 Roo.apply(Roo.bootstrap.TabGroup, {
14127     
14128     groups: {},
14129      /**
14130     * register a Navigation Group
14131     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14132     */
14133     register : function(navgrp)
14134     {
14135         this.groups[navgrp.navId] = navgrp;
14136         
14137     },
14138     /**
14139     * fetch a Navigation Group based on the navigation ID
14140     * if one does not exist , it will get created.
14141     * @param {string} the navgroup to add
14142     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14143     */
14144     get: function(navId) {
14145         if (typeof(this.groups[navId]) == 'undefined') {
14146             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14147         }
14148         return this.groups[navId] ;
14149     }
14150     
14151     
14152     
14153 });
14154
14155  /*
14156  * - LGPL
14157  *
14158  * TabPanel
14159  * 
14160  */
14161
14162 /**
14163  * @class Roo.bootstrap.TabPanel
14164  * @extends Roo.bootstrap.Component
14165  * Bootstrap TabPanel class
14166  * @cfg {Boolean} active panel active
14167  * @cfg {String} html panel content
14168  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14169  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14170  * 
14171  * 
14172  * @constructor
14173  * Create a new TabPanel
14174  * @param {Object} config The config object
14175  */
14176
14177 Roo.bootstrap.TabPanel = function(config){
14178     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14179     this.addEvents({
14180         /**
14181              * @event changed
14182              * Fires when the active status changes
14183              * @param {Roo.bootstrap.TabPanel} this
14184              * @param {Boolean} state the new state
14185             
14186          */
14187         'changed': true,
14188         /**
14189              * @event beforedeactivate
14190              * Fires before a tab is de-activated - can be used to do validation on a form.
14191              * @param {Roo.bootstrap.TabPanel} this
14192              * @return {Boolean} false if there is an error
14193             
14194          */
14195         'beforedeactivate': true
14196      });
14197     
14198     this.tabId = this.tabId || Roo.id();
14199   
14200 };
14201
14202 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14203     
14204     active: false,
14205     html: false,
14206     tabId: false,
14207     navId : false,
14208     
14209     getAutoCreate : function(){
14210         var cfg = {
14211             tag: 'div',
14212             // item is needed for carousel - not sure if it has any effect otherwise
14213             cls: 'tab-pane item',
14214             html: this.html || ''
14215         };
14216         
14217         if(this.active){
14218             cfg.cls += ' active';
14219         }
14220         
14221         if(this.tabId){
14222             cfg.tabId = this.tabId;
14223         }
14224         
14225         
14226         return cfg;
14227     },
14228     
14229     initEvents:  function()
14230     {
14231         Roo.log('-------- init events on tab panel ---------');
14232         
14233         var p = this.parent();
14234         this.navId = this.navId || p.navId;
14235         
14236         if (typeof(this.navId) != 'undefined') {
14237             // not really needed.. but just in case.. parent should be a NavGroup.
14238             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14239             Roo.log(['register', tg, this]);
14240             tg.register(this);
14241         }
14242     },
14243     
14244     
14245     onRender : function(ct, position)
14246     {
14247        // Roo.log("Call onRender: " + this.xtype);
14248         
14249         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14250         
14251         
14252         
14253         
14254         
14255     },
14256     
14257     setActive: function(state)
14258     {
14259         Roo.log("panel - set active " + this.tabId + "=" + state);
14260         
14261         this.active = state;
14262         if (!state) {
14263             this.el.removeClass('active');
14264             
14265         } else  if (!this.el.hasClass('active')) {
14266             this.el.addClass('active');
14267         }
14268         this.fireEvent('changed', this, state);
14269     }
14270     
14271     
14272 });
14273  
14274
14275  
14276
14277  /*
14278  * - LGPL
14279  *
14280  * DateField
14281  * 
14282  */
14283
14284 /**
14285  * @class Roo.bootstrap.DateField
14286  * @extends Roo.bootstrap.Input
14287  * Bootstrap DateField class
14288  * @cfg {Number} weekStart default 0
14289  * @cfg {Number} weekStart default 0
14290  * @cfg {Number} viewMode default empty, (months|years)
14291  * @cfg {Number} minViewMode default empty, (months|years)
14292  * @cfg {Number} startDate default -Infinity
14293  * @cfg {Number} endDate default Infinity
14294  * @cfg {Boolean} todayHighlight default false
14295  * @cfg {Boolean} todayBtn default false
14296  * @cfg {Boolean} calendarWeeks default false
14297  * @cfg {Object} daysOfWeekDisabled default empty
14298  * 
14299  * @cfg {Boolean} keyboardNavigation default true
14300  * @cfg {String} language default en
14301  * 
14302  * @constructor
14303  * Create a new DateField
14304  * @param {Object} config The config object
14305  */
14306
14307 Roo.bootstrap.DateField = function(config){
14308     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14309      this.addEvents({
14310             /**
14311              * @event show
14312              * Fires when this field show.
14313              * @param {Roo.bootstrap.DateField} this
14314              * @param {Mixed} date The date value
14315              */
14316             show : true,
14317             /**
14318              * @event show
14319              * Fires when this field hide.
14320              * @param {Roo.bootstrap.DateField} this
14321              * @param {Mixed} date The date value
14322              */
14323             hide : true,
14324             /**
14325              * @event select
14326              * Fires when select a date.
14327              * @param {Roo.bootstrap.DateField} this
14328              * @param {Mixed} date The date value
14329              */
14330             select : true
14331         });
14332 };
14333
14334 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14335     
14336     /**
14337      * @cfg {String} format
14338      * The default date format string which can be overriden for localization support.  The format must be
14339      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14340      */
14341     format : "m/d/y",
14342     /**
14343      * @cfg {String} altFormats
14344      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14345      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14346      */
14347     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14348     
14349     weekStart : 0,
14350     
14351     viewMode : '',
14352     
14353     minViewMode : '',
14354     
14355     todayHighlight : false,
14356     
14357     todayBtn: false,
14358     
14359     language: 'en',
14360     
14361     keyboardNavigation: true,
14362     
14363     calendarWeeks: false,
14364     
14365     startDate: -Infinity,
14366     
14367     endDate: Infinity,
14368     
14369     daysOfWeekDisabled: [],
14370     
14371     _events: [],
14372     
14373     UTCDate: function()
14374     {
14375         return new Date(Date.UTC.apply(Date, arguments));
14376     },
14377     
14378     UTCToday: function()
14379     {
14380         var today = new Date();
14381         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14382     },
14383     
14384     getDate: function() {
14385             var d = this.getUTCDate();
14386             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14387     },
14388     
14389     getUTCDate: function() {
14390             return this.date;
14391     },
14392     
14393     setDate: function(d) {
14394             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14395     },
14396     
14397     setUTCDate: function(d) {
14398             this.date = d;
14399             this.setValue(this.formatDate(this.date));
14400     },
14401         
14402     onRender: function(ct, position)
14403     {
14404         
14405         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14406         
14407         this.language = this.language || 'en';
14408         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14409         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14410         
14411         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14412         this.format = this.format || 'm/d/y';
14413         this.isInline = false;
14414         this.isInput = true;
14415         this.component = this.el.select('.add-on', true).first() || false;
14416         this.component = (this.component && this.component.length === 0) ? false : this.component;
14417         this.hasInput = this.component && this.inputEL().length;
14418         
14419         if (typeof(this.minViewMode === 'string')) {
14420             switch (this.minViewMode) {
14421                 case 'months':
14422                     this.minViewMode = 1;
14423                     break;
14424                 case 'years':
14425                     this.minViewMode = 2;
14426                     break;
14427                 default:
14428                     this.minViewMode = 0;
14429                     break;
14430             }
14431         }
14432         
14433         if (typeof(this.viewMode === 'string')) {
14434             switch (this.viewMode) {
14435                 case 'months':
14436                     this.viewMode = 1;
14437                     break;
14438                 case 'years':
14439                     this.viewMode = 2;
14440                     break;
14441                 default:
14442                     this.viewMode = 0;
14443                     break;
14444             }
14445         }
14446                 
14447         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14448         
14449 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14450         
14451         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14452         
14453         this.picker().on('mousedown', this.onMousedown, this);
14454         this.picker().on('click', this.onClick, this);
14455         
14456         this.picker().addClass('datepicker-dropdown');
14457         
14458         this.startViewMode = this.viewMode;
14459         
14460         
14461         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14462             if(!this.calendarWeeks){
14463                 v.remove();
14464                 return;
14465             };
14466             
14467             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14468             v.attr('colspan', function(i, val){
14469                 return parseInt(val) + 1;
14470             });
14471         })
14472                         
14473         
14474         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14475         
14476         this.setStartDate(this.startDate);
14477         this.setEndDate(this.endDate);
14478         
14479         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14480         
14481         this.fillDow();
14482         this.fillMonths();
14483         this.update();
14484         this.showMode();
14485         
14486         if(this.isInline) {
14487             this.show();
14488         }
14489     },
14490     
14491     picker : function()
14492     {
14493         return this.pickerEl;
14494 //        return this.el.select('.datepicker', true).first();
14495     },
14496     
14497     fillDow: function()
14498     {
14499         var dowCnt = this.weekStart;
14500         
14501         var dow = {
14502             tag: 'tr',
14503             cn: [
14504                 
14505             ]
14506         };
14507         
14508         if(this.calendarWeeks){
14509             dow.cn.push({
14510                 tag: 'th',
14511                 cls: 'cw',
14512                 html: '&nbsp;'
14513             })
14514         }
14515         
14516         while (dowCnt < this.weekStart + 7) {
14517             dow.cn.push({
14518                 tag: 'th',
14519                 cls: 'dow',
14520                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14521             });
14522         }
14523         
14524         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14525     },
14526     
14527     fillMonths: function()
14528     {    
14529         var i = 0
14530         var months = this.picker().select('>.datepicker-months td', true).first();
14531         
14532         months.dom.innerHTML = '';
14533         
14534         while (i < 12) {
14535             var month = {
14536                 tag: 'span',
14537                 cls: 'month',
14538                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14539             }
14540             
14541             months.createChild(month);
14542         }
14543         
14544     },
14545     
14546     update: function()
14547     {
14548         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;
14549         
14550         if (this.date < this.startDate) {
14551             this.viewDate = new Date(this.startDate);
14552         } else if (this.date > this.endDate) {
14553             this.viewDate = new Date(this.endDate);
14554         } else {
14555             this.viewDate = new Date(this.date);
14556         }
14557         
14558         this.fill();
14559     },
14560     
14561     fill: function() 
14562     {
14563         var d = new Date(this.viewDate),
14564                 year = d.getUTCFullYear(),
14565                 month = d.getUTCMonth(),
14566                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14567                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14568                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14569                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14570                 currentDate = this.date && this.date.valueOf(),
14571                 today = this.UTCToday();
14572         
14573         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14574         
14575 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14576         
14577 //        this.picker.select('>tfoot th.today').
14578 //                                              .text(dates[this.language].today)
14579 //                                              .toggle(this.todayBtn !== false);
14580     
14581         this.updateNavArrows();
14582         this.fillMonths();
14583                                                 
14584         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14585         
14586         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14587          
14588         prevMonth.setUTCDate(day);
14589         
14590         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14591         
14592         var nextMonth = new Date(prevMonth);
14593         
14594         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14595         
14596         nextMonth = nextMonth.valueOf();
14597         
14598         var fillMonths = false;
14599         
14600         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14601         
14602         while(prevMonth.valueOf() < nextMonth) {
14603             var clsName = '';
14604             
14605             if (prevMonth.getUTCDay() === this.weekStart) {
14606                 if(fillMonths){
14607                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14608                 }
14609                     
14610                 fillMonths = {
14611                     tag: 'tr',
14612                     cn: []
14613                 };
14614                 
14615                 if(this.calendarWeeks){
14616                     // ISO 8601: First week contains first thursday.
14617                     // ISO also states week starts on Monday, but we can be more abstract here.
14618                     var
14619                     // Start of current week: based on weekstart/current date
14620                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14621                     // Thursday of this week
14622                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14623                     // First Thursday of year, year from thursday
14624                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14625                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14626                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14627                     
14628                     fillMonths.cn.push({
14629                         tag: 'td',
14630                         cls: 'cw',
14631                         html: calWeek
14632                     });
14633                 }
14634             }
14635             
14636             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14637                 clsName += ' old';
14638             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14639                 clsName += ' new';
14640             }
14641             if (this.todayHighlight &&
14642                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14643                 prevMonth.getUTCMonth() == today.getMonth() &&
14644                 prevMonth.getUTCDate() == today.getDate()) {
14645                 clsName += ' today';
14646             }
14647             
14648             if (currentDate && prevMonth.valueOf() === currentDate) {
14649                 clsName += ' active';
14650             }
14651             
14652             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14653                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14654                     clsName += ' disabled';
14655             }
14656             
14657             fillMonths.cn.push({
14658                 tag: 'td',
14659                 cls: 'day ' + clsName,
14660                 html: prevMonth.getDate()
14661             })
14662             
14663             prevMonth.setDate(prevMonth.getDate()+1);
14664         }
14665           
14666         var currentYear = this.date && this.date.getUTCFullYear();
14667         var currentMonth = this.date && this.date.getUTCMonth();
14668         
14669         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14670         
14671         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14672             v.removeClass('active');
14673             
14674             if(currentYear === year && k === currentMonth){
14675                 v.addClass('active');
14676             }
14677             
14678             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14679                 v.addClass('disabled');
14680             }
14681             
14682         });
14683         
14684         
14685         year = parseInt(year/10, 10) * 10;
14686         
14687         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14688         
14689         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14690         
14691         year -= 1;
14692         for (var i = -1; i < 11; i++) {
14693             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14694                 tag: 'span',
14695                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14696                 html: year
14697             })
14698             
14699             year += 1;
14700         }
14701     },
14702     
14703     showMode: function(dir) 
14704     {
14705         if (dir) {
14706             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14707         }
14708         Roo.each(this.picker().select('>div',true).elements, function(v){
14709             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14710             v.hide();
14711         });
14712         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14713     },
14714     
14715     place: function()
14716     {
14717         if(this.isInline) return;
14718         
14719         this.picker().removeClass(['bottom', 'top']);
14720         
14721         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14722             /*
14723              * place to the top of element!
14724              *
14725              */
14726             
14727             this.picker().addClass('top');
14728             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14729             
14730             return;
14731         }
14732         
14733         this.picker().addClass('bottom');
14734         
14735         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14736     },
14737     
14738     parseDate : function(value)
14739     {
14740         if(!value || value instanceof Date){
14741             return value;
14742         }
14743         var v = Date.parseDate(value, this.format);
14744         if (!v && this.useIso) {
14745             v = Date.parseDate(value, 'Y-m-d');
14746         }
14747         if(!v && this.altFormats){
14748             if(!this.altFormatsArray){
14749                 this.altFormatsArray = this.altFormats.split("|");
14750             }
14751             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14752                 v = Date.parseDate(value, this.altFormatsArray[i]);
14753             }
14754         }
14755         return v;
14756     },
14757     
14758     formatDate : function(date, fmt)
14759     {
14760         return (!date || !(date instanceof Date)) ?
14761         date : date.dateFormat(fmt || this.format);
14762     },
14763     
14764     onFocus : function()
14765     {
14766         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14767         this.show();
14768     },
14769     
14770     onBlur : function()
14771     {
14772         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14773         
14774         var d = this.inputEl().getValue();
14775         
14776         this.setValue(d);
14777                 
14778         this.hide();
14779     },
14780     
14781     show : function()
14782     {
14783         this.picker().show();
14784         this.update();
14785         this.place();
14786         
14787         this.fireEvent('show', this, this.date);
14788     },
14789     
14790     hide : function()
14791     {
14792         if(this.isInline) return;
14793         this.picker().hide();
14794         this.viewMode = this.startViewMode;
14795         this.showMode();
14796         
14797         this.fireEvent('hide', this, this.date);
14798         
14799     },
14800     
14801     onMousedown: function(e)
14802     {
14803         e.stopPropagation();
14804         e.preventDefault();
14805     },
14806     
14807     keyup: function(e)
14808     {
14809         Roo.bootstrap.DateField.superclass.keyup.call(this);
14810         this.update();
14811     },
14812
14813     setValue: function(v)
14814     {
14815         var d = new Date(v).clearTime();
14816         
14817         if(isNaN(d.getTime())){
14818             this.date = this.viewDate = '';
14819             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14820             return;
14821         }
14822         
14823         v = this.formatDate(d);
14824         
14825         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14826         
14827         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14828      
14829         this.update();
14830
14831         this.fireEvent('select', this, this.date);
14832         
14833     },
14834     
14835     getValue: function()
14836     {
14837         return this.formatDate(this.date);
14838     },
14839     
14840     fireKey: function(e)
14841     {
14842         if (!this.picker().isVisible()){
14843             if (e.keyCode == 27) // allow escape to hide and re-show picker
14844                 this.show();
14845             return;
14846         }
14847         
14848         var dateChanged = false,
14849         dir, day, month,
14850         newDate, newViewDate;
14851         
14852         switch(e.keyCode){
14853             case 27: // escape
14854                 this.hide();
14855                 e.preventDefault();
14856                 break;
14857             case 37: // left
14858             case 39: // right
14859                 if (!this.keyboardNavigation) break;
14860                 dir = e.keyCode == 37 ? -1 : 1;
14861                 
14862                 if (e.ctrlKey){
14863                     newDate = this.moveYear(this.date, dir);
14864                     newViewDate = this.moveYear(this.viewDate, dir);
14865                 } else if (e.shiftKey){
14866                     newDate = this.moveMonth(this.date, dir);
14867                     newViewDate = this.moveMonth(this.viewDate, dir);
14868                 } else {
14869                     newDate = new Date(this.date);
14870                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14871                     newViewDate = new Date(this.viewDate);
14872                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14873                 }
14874                 if (this.dateWithinRange(newDate)){
14875                     this.date = newDate;
14876                     this.viewDate = newViewDate;
14877                     this.setValue(this.formatDate(this.date));
14878 //                    this.update();
14879                     e.preventDefault();
14880                     dateChanged = true;
14881                 }
14882                 break;
14883             case 38: // up
14884             case 40: // down
14885                 if (!this.keyboardNavigation) break;
14886                 dir = e.keyCode == 38 ? -1 : 1;
14887                 if (e.ctrlKey){
14888                     newDate = this.moveYear(this.date, dir);
14889                     newViewDate = this.moveYear(this.viewDate, dir);
14890                 } else if (e.shiftKey){
14891                     newDate = this.moveMonth(this.date, dir);
14892                     newViewDate = this.moveMonth(this.viewDate, dir);
14893                 } else {
14894                     newDate = new Date(this.date);
14895                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14896                     newViewDate = new Date(this.viewDate);
14897                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14898                 }
14899                 if (this.dateWithinRange(newDate)){
14900                     this.date = newDate;
14901                     this.viewDate = newViewDate;
14902                     this.setValue(this.formatDate(this.date));
14903 //                    this.update();
14904                     e.preventDefault();
14905                     dateChanged = true;
14906                 }
14907                 break;
14908             case 13: // enter
14909                 this.setValue(this.formatDate(this.date));
14910                 this.hide();
14911                 e.preventDefault();
14912                 break;
14913             case 9: // tab
14914                 this.setValue(this.formatDate(this.date));
14915                 this.hide();
14916                 break;
14917             case 16: // shift
14918             case 17: // ctrl
14919             case 18: // alt
14920                 break;
14921             default :
14922                 this.hide();
14923                 
14924         }
14925     },
14926     
14927     
14928     onClick: function(e) 
14929     {
14930         e.stopPropagation();
14931         e.preventDefault();
14932         
14933         var target = e.getTarget();
14934         
14935         if(target.nodeName.toLowerCase() === 'i'){
14936             target = Roo.get(target).dom.parentNode;
14937         }
14938         
14939         var nodeName = target.nodeName;
14940         var className = target.className;
14941         var html = target.innerHTML;
14942         
14943         switch(nodeName.toLowerCase()) {
14944             case 'th':
14945                 switch(className) {
14946                     case 'switch':
14947                         this.showMode(1);
14948                         break;
14949                     case 'prev':
14950                     case 'next':
14951                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14952                         switch(this.viewMode){
14953                                 case 0:
14954                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14955                                         break;
14956                                 case 1:
14957                                 case 2:
14958                                         this.viewDate = this.moveYear(this.viewDate, dir);
14959                                         break;
14960                         }
14961                         this.fill();
14962                         break;
14963                     case 'today':
14964                         var date = new Date();
14965                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14966 //                        this.fill()
14967                         this.setValue(this.formatDate(this.date));
14968                         
14969                         this.hide();
14970                         break;
14971                 }
14972                 break;
14973             case 'span':
14974                 if (className.indexOf('disabled') === -1) {
14975                     this.viewDate.setUTCDate(1);
14976                     if (className.indexOf('month') !== -1) {
14977                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14978                     } else {
14979                         var year = parseInt(html, 10) || 0;
14980                         this.viewDate.setUTCFullYear(year);
14981                         
14982                     }
14983                     this.showMode(-1);
14984                     this.fill();
14985                 }
14986                 break;
14987                 
14988             case 'td':
14989                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14990                     var day = parseInt(html, 10) || 1;
14991                     var year = this.viewDate.getUTCFullYear(),
14992                         month = this.viewDate.getUTCMonth();
14993
14994                     if (className.indexOf('old') !== -1) {
14995                         if(month === 0 ){
14996                             month = 11;
14997                             year -= 1;
14998                         }else{
14999                             month -= 1;
15000                         }
15001                     } else if (className.indexOf('new') !== -1) {
15002                         if (month == 11) {
15003                             month = 0;
15004                             year += 1;
15005                         } else {
15006                             month += 1;
15007                         }
15008                     }
15009                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15010                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15011 //                    this.fill();
15012                     this.setValue(this.formatDate(this.date));
15013                     this.hide();
15014                 }
15015                 break;
15016         }
15017     },
15018     
15019     setStartDate: function(startDate)
15020     {
15021         this.startDate = startDate || -Infinity;
15022         if (this.startDate !== -Infinity) {
15023             this.startDate = this.parseDate(this.startDate);
15024         }
15025         this.update();
15026         this.updateNavArrows();
15027     },
15028
15029     setEndDate: function(endDate)
15030     {
15031         this.endDate = endDate || Infinity;
15032         if (this.endDate !== Infinity) {
15033             this.endDate = this.parseDate(this.endDate);
15034         }
15035         this.update();
15036         this.updateNavArrows();
15037     },
15038     
15039     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15040     {
15041         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15042         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15043             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15044         }
15045         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15046             return parseInt(d, 10);
15047         });
15048         this.update();
15049         this.updateNavArrows();
15050     },
15051     
15052     updateNavArrows: function() 
15053     {
15054         var d = new Date(this.viewDate),
15055         year = d.getUTCFullYear(),
15056         month = d.getUTCMonth();
15057         
15058         Roo.each(this.picker().select('.prev', true).elements, function(v){
15059             v.show();
15060             switch (this.viewMode) {
15061                 case 0:
15062
15063                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15064                         v.hide();
15065                     }
15066                     break;
15067                 case 1:
15068                 case 2:
15069                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15070                         v.hide();
15071                     }
15072                     break;
15073             }
15074         });
15075         
15076         Roo.each(this.picker().select('.next', true).elements, function(v){
15077             v.show();
15078             switch (this.viewMode) {
15079                 case 0:
15080
15081                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15082                         v.hide();
15083                     }
15084                     break;
15085                 case 1:
15086                 case 2:
15087                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15088                         v.hide();
15089                     }
15090                     break;
15091             }
15092         })
15093     },
15094     
15095     moveMonth: function(date, dir)
15096     {
15097         if (!dir) return date;
15098         var new_date = new Date(date.valueOf()),
15099         day = new_date.getUTCDate(),
15100         month = new_date.getUTCMonth(),
15101         mag = Math.abs(dir),
15102         new_month, test;
15103         dir = dir > 0 ? 1 : -1;
15104         if (mag == 1){
15105             test = dir == -1
15106             // If going back one month, make sure month is not current month
15107             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15108             ? function(){
15109                 return new_date.getUTCMonth() == month;
15110             }
15111             // If going forward one month, make sure month is as expected
15112             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15113             : function(){
15114                 return new_date.getUTCMonth() != new_month;
15115             };
15116             new_month = month + dir;
15117             new_date.setUTCMonth(new_month);
15118             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15119             if (new_month < 0 || new_month > 11)
15120                 new_month = (new_month + 12) % 12;
15121         } else {
15122             // For magnitudes >1, move one month at a time...
15123             for (var i=0; i<mag; i++)
15124                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15125                 new_date = this.moveMonth(new_date, dir);
15126             // ...then reset the day, keeping it in the new month
15127             new_month = new_date.getUTCMonth();
15128             new_date.setUTCDate(day);
15129             test = function(){
15130                 return new_month != new_date.getUTCMonth();
15131             };
15132         }
15133         // Common date-resetting loop -- if date is beyond end of month, make it
15134         // end of month
15135         while (test()){
15136             new_date.setUTCDate(--day);
15137             new_date.setUTCMonth(new_month);
15138         }
15139         return new_date;
15140     },
15141
15142     moveYear: function(date, dir)
15143     {
15144         return this.moveMonth(date, dir*12);
15145     },
15146
15147     dateWithinRange: function(date)
15148     {
15149         return date >= this.startDate && date <= this.endDate;
15150     },
15151
15152     
15153     remove: function() 
15154     {
15155         this.picker().remove();
15156     }
15157    
15158 });
15159
15160 Roo.apply(Roo.bootstrap.DateField,  {
15161     
15162     head : {
15163         tag: 'thead',
15164         cn: [
15165         {
15166             tag: 'tr',
15167             cn: [
15168             {
15169                 tag: 'th',
15170                 cls: 'prev',
15171                 html: '<i class="fa fa-arrow-left"/>'
15172             },
15173             {
15174                 tag: 'th',
15175                 cls: 'switch',
15176                 colspan: '5'
15177             },
15178             {
15179                 tag: 'th',
15180                 cls: 'next',
15181                 html: '<i class="fa fa-arrow-right"/>'
15182             }
15183
15184             ]
15185         }
15186         ]
15187     },
15188     
15189     content : {
15190         tag: 'tbody',
15191         cn: [
15192         {
15193             tag: 'tr',
15194             cn: [
15195             {
15196                 tag: 'td',
15197                 colspan: '7'
15198             }
15199             ]
15200         }
15201         ]
15202     },
15203     
15204     footer : {
15205         tag: 'tfoot',
15206         cn: [
15207         {
15208             tag: 'tr',
15209             cn: [
15210             {
15211                 tag: 'th',
15212                 colspan: '7',
15213                 cls: 'today'
15214             }
15215                     
15216             ]
15217         }
15218         ]
15219     },
15220     
15221     dates:{
15222         en: {
15223             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15224             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15225             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15226             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15227             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15228             today: "Today"
15229         }
15230     },
15231     
15232     modes: [
15233     {
15234         clsName: 'days',
15235         navFnc: 'Month',
15236         navStep: 1
15237     },
15238     {
15239         clsName: 'months',
15240         navFnc: 'FullYear',
15241         navStep: 1
15242     },
15243     {
15244         clsName: 'years',
15245         navFnc: 'FullYear',
15246         navStep: 10
15247     }]
15248 });
15249
15250 Roo.apply(Roo.bootstrap.DateField,  {
15251   
15252     template : {
15253         tag: 'div',
15254         cls: 'datepicker dropdown-menu',
15255         cn: [
15256         {
15257             tag: 'div',
15258             cls: 'datepicker-days',
15259             cn: [
15260             {
15261                 tag: 'table',
15262                 cls: 'table-condensed',
15263                 cn:[
15264                 Roo.bootstrap.DateField.head,
15265                 {
15266                     tag: 'tbody'
15267                 },
15268                 Roo.bootstrap.DateField.footer
15269                 ]
15270             }
15271             ]
15272         },
15273         {
15274             tag: 'div',
15275             cls: 'datepicker-months',
15276             cn: [
15277             {
15278                 tag: 'table',
15279                 cls: 'table-condensed',
15280                 cn:[
15281                 Roo.bootstrap.DateField.head,
15282                 Roo.bootstrap.DateField.content,
15283                 Roo.bootstrap.DateField.footer
15284                 ]
15285             }
15286             ]
15287         },
15288         {
15289             tag: 'div',
15290             cls: 'datepicker-years',
15291             cn: [
15292             {
15293                 tag: 'table',
15294                 cls: 'table-condensed',
15295                 cn:[
15296                 Roo.bootstrap.DateField.head,
15297                 Roo.bootstrap.DateField.content,
15298                 Roo.bootstrap.DateField.footer
15299                 ]
15300             }
15301             ]
15302         }
15303         ]
15304     }
15305 });
15306
15307  
15308
15309  /*
15310  * - LGPL
15311  *
15312  * TimeField
15313  * 
15314  */
15315
15316 /**
15317  * @class Roo.bootstrap.TimeField
15318  * @extends Roo.bootstrap.Input
15319  * Bootstrap DateField class
15320  * 
15321  * 
15322  * @constructor
15323  * Create a new TimeField
15324  * @param {Object} config The config object
15325  */
15326
15327 Roo.bootstrap.TimeField = function(config){
15328     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15329     this.addEvents({
15330             /**
15331              * @event show
15332              * Fires when this field show.
15333              * @param {Roo.bootstrap.DateField} this
15334              * @param {Mixed} date The date value
15335              */
15336             show : true,
15337             /**
15338              * @event show
15339              * Fires when this field hide.
15340              * @param {Roo.bootstrap.DateField} this
15341              * @param {Mixed} date The date value
15342              */
15343             hide : true,
15344             /**
15345              * @event select
15346              * Fires when select a date.
15347              * @param {Roo.bootstrap.DateField} this
15348              * @param {Mixed} date The date value
15349              */
15350             select : true
15351         });
15352 };
15353
15354 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15355     
15356     /**
15357      * @cfg {String} format
15358      * The default time format string which can be overriden for localization support.  The format must be
15359      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15360      */
15361     format : "H:i",
15362        
15363     onRender: function(ct, position)
15364     {
15365         
15366         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15367                 
15368         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15369         
15370         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15371         
15372         this.pop = this.picker().select('>.datepicker-time',true).first();
15373         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15374         
15375         this.picker().on('mousedown', this.onMousedown, this);
15376         this.picker().on('click', this.onClick, this);
15377         
15378         this.picker().addClass('datepicker-dropdown');
15379     
15380         this.fillTime();
15381         this.update();
15382             
15383         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15384         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15385         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15386         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15387         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15388         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15389
15390     },
15391     
15392     fireKey: function(e){
15393         if (!this.picker().isVisible()){
15394             if (e.keyCode == 27) // allow escape to hide and re-show picker
15395                 this.show();
15396             return;
15397         }
15398
15399         e.preventDefault();
15400         
15401         switch(e.keyCode){
15402             case 27: // escape
15403                 this.hide();
15404                 break;
15405             case 37: // left
15406             case 39: // right
15407                 this.onTogglePeriod();
15408                 break;
15409             case 38: // up
15410                 this.onIncrementMinutes();
15411                 break;
15412             case 40: // down
15413                 this.onDecrementMinutes();
15414                 break;
15415             case 13: // enter
15416             case 9: // tab
15417                 this.setTime();
15418                 break;
15419         }
15420     },
15421     
15422     onClick: function(e) {
15423         e.stopPropagation();
15424         e.preventDefault();
15425     },
15426     
15427     picker : function()
15428     {
15429         return this.el.select('.datepicker', true).first();
15430     },
15431     
15432     fillTime: function()
15433     {    
15434         var time = this.pop.select('tbody', true).first();
15435         
15436         time.dom.innerHTML = '';
15437         
15438         time.createChild({
15439             tag: 'tr',
15440             cn: [
15441                 {
15442                     tag: 'td',
15443                     cn: [
15444                         {
15445                             tag: 'a',
15446                             href: '#',
15447                             cls: 'btn',
15448                             cn: [
15449                                 {
15450                                     tag: 'span',
15451                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15452                                 }
15453                             ]
15454                         } 
15455                     ]
15456                 },
15457                 {
15458                     tag: 'td',
15459                     cls: 'separator'
15460                 },
15461                 {
15462                     tag: 'td',
15463                     cn: [
15464                         {
15465                             tag: 'a',
15466                             href: '#',
15467                             cls: 'btn',
15468                             cn: [
15469                                 {
15470                                     tag: 'span',
15471                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15472                                 }
15473                             ]
15474                         }
15475                     ]
15476                 },
15477                 {
15478                     tag: 'td',
15479                     cls: 'separator'
15480                 }
15481             ]
15482         });
15483         
15484         time.createChild({
15485             tag: 'tr',
15486             cn: [
15487                 {
15488                     tag: 'td',
15489                     cn: [
15490                         {
15491                             tag: 'span',
15492                             cls: 'timepicker-hour',
15493                             html: '00'
15494                         }  
15495                     ]
15496                 },
15497                 {
15498                     tag: 'td',
15499                     cls: 'separator',
15500                     html: ':'
15501                 },
15502                 {
15503                     tag: 'td',
15504                     cn: [
15505                         {
15506                             tag: 'span',
15507                             cls: 'timepicker-minute',
15508                             html: '00'
15509                         }  
15510                     ]
15511                 },
15512                 {
15513                     tag: 'td',
15514                     cls: 'separator'
15515                 },
15516                 {
15517                     tag: 'td',
15518                     cn: [
15519                         {
15520                             tag: 'button',
15521                             type: 'button',
15522                             cls: 'btn btn-primary period',
15523                             html: 'AM'
15524                             
15525                         }
15526                     ]
15527                 }
15528             ]
15529         });
15530         
15531         time.createChild({
15532             tag: 'tr',
15533             cn: [
15534                 {
15535                     tag: 'td',
15536                     cn: [
15537                         {
15538                             tag: 'a',
15539                             href: '#',
15540                             cls: 'btn',
15541                             cn: [
15542                                 {
15543                                     tag: 'span',
15544                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15545                                 }
15546                             ]
15547                         }
15548                     ]
15549                 },
15550                 {
15551                     tag: 'td',
15552                     cls: 'separator'
15553                 },
15554                 {
15555                     tag: 'td',
15556                     cn: [
15557                         {
15558                             tag: 'a',
15559                             href: '#',
15560                             cls: 'btn',
15561                             cn: [
15562                                 {
15563                                     tag: 'span',
15564                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15565                                 }
15566                             ]
15567                         }
15568                     ]
15569                 },
15570                 {
15571                     tag: 'td',
15572                     cls: 'separator'
15573                 }
15574             ]
15575         });
15576         
15577     },
15578     
15579     update: function()
15580     {
15581         
15582         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15583         
15584         this.fill();
15585     },
15586     
15587     fill: function() 
15588     {
15589         var hours = this.time.getHours();
15590         var minutes = this.time.getMinutes();
15591         var period = 'AM';
15592         
15593         if(hours > 11){
15594             period = 'PM';
15595         }
15596         
15597         if(hours == 0){
15598             hours = 12;
15599         }
15600         
15601         
15602         if(hours > 12){
15603             hours = hours - 12;
15604         }
15605         
15606         if(hours < 10){
15607             hours = '0' + hours;
15608         }
15609         
15610         if(minutes < 10){
15611             minutes = '0' + minutes;
15612         }
15613         
15614         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15615         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15616         this.pop.select('button', true).first().dom.innerHTML = period;
15617         
15618     },
15619     
15620     place: function()
15621     {   
15622         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15623         
15624         var cls = ['bottom'];
15625         
15626         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15627             cls.pop();
15628             cls.push('top');
15629         }
15630         
15631         cls.push('right');
15632         
15633         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15634             cls.pop();
15635             cls.push('left');
15636         }
15637         
15638         this.picker().addClass(cls.join('-'));
15639         
15640         var _this = this;
15641         
15642         Roo.each(cls, function(c){
15643             if(c == 'bottom'){
15644                 _this.picker().setTop(_this.inputEl().getHeight());
15645                 return;
15646             }
15647             if(c == 'top'){
15648                 _this.picker().setTop(0 - _this.picker().getHeight());
15649                 return;
15650             }
15651             
15652             if(c == 'left'){
15653                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15654                 return;
15655             }
15656             if(c == 'right'){
15657                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15658                 return;
15659             }
15660         });
15661         
15662     },
15663   
15664     onFocus : function()
15665     {
15666         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15667         this.show();
15668     },
15669     
15670     onBlur : function()
15671     {
15672         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15673         this.hide();
15674     },
15675     
15676     show : function()
15677     {
15678         this.picker().show();
15679         this.pop.show();
15680         this.update();
15681         this.place();
15682         
15683         this.fireEvent('show', this, this.date);
15684     },
15685     
15686     hide : function()
15687     {
15688         this.picker().hide();
15689         this.pop.hide();
15690         
15691         this.fireEvent('hide', this, this.date);
15692     },
15693     
15694     setTime : function()
15695     {
15696         this.hide();
15697         this.setValue(this.time.format(this.format));
15698         
15699         this.fireEvent('select', this, this.date);
15700         
15701         
15702     },
15703     
15704     onMousedown: function(e){
15705         e.stopPropagation();
15706         e.preventDefault();
15707     },
15708     
15709     onIncrementHours: function()
15710     {
15711         Roo.log('onIncrementHours');
15712         this.time = this.time.add(Date.HOUR, 1);
15713         this.update();
15714         
15715     },
15716     
15717     onDecrementHours: function()
15718     {
15719         Roo.log('onDecrementHours');
15720         this.time = this.time.add(Date.HOUR, -1);
15721         this.update();
15722     },
15723     
15724     onIncrementMinutes: function()
15725     {
15726         Roo.log('onIncrementMinutes');
15727         this.time = this.time.add(Date.MINUTE, 1);
15728         this.update();
15729     },
15730     
15731     onDecrementMinutes: function()
15732     {
15733         Roo.log('onDecrementMinutes');
15734         this.time = this.time.add(Date.MINUTE, -1);
15735         this.update();
15736     },
15737     
15738     onTogglePeriod: function()
15739     {
15740         Roo.log('onTogglePeriod');
15741         this.time = this.time.add(Date.HOUR, 12);
15742         this.update();
15743     }
15744     
15745    
15746 });
15747
15748 Roo.apply(Roo.bootstrap.TimeField,  {
15749     
15750     content : {
15751         tag: 'tbody',
15752         cn: [
15753             {
15754                 tag: 'tr',
15755                 cn: [
15756                 {
15757                     tag: 'td',
15758                     colspan: '7'
15759                 }
15760                 ]
15761             }
15762         ]
15763     },
15764     
15765     footer : {
15766         tag: 'tfoot',
15767         cn: [
15768             {
15769                 tag: 'tr',
15770                 cn: [
15771                 {
15772                     tag: 'th',
15773                     colspan: '7',
15774                     cls: '',
15775                     cn: [
15776                         {
15777                             tag: 'button',
15778                             cls: 'btn btn-info ok',
15779                             html: 'OK'
15780                         }
15781                     ]
15782                 }
15783
15784                 ]
15785             }
15786         ]
15787     }
15788 });
15789
15790 Roo.apply(Roo.bootstrap.TimeField,  {
15791   
15792     template : {
15793         tag: 'div',
15794         cls: 'datepicker dropdown-menu',
15795         cn: [
15796             {
15797                 tag: 'div',
15798                 cls: 'datepicker-time',
15799                 cn: [
15800                 {
15801                     tag: 'table',
15802                     cls: 'table-condensed',
15803                     cn:[
15804                     Roo.bootstrap.TimeField.content,
15805                     Roo.bootstrap.TimeField.footer
15806                     ]
15807                 }
15808                 ]
15809             }
15810         ]
15811     }
15812 });
15813
15814  
15815
15816  /*
15817  * - LGPL
15818  *
15819  * CheckBox
15820  * 
15821  */
15822
15823 /**
15824  * @class Roo.bootstrap.CheckBox
15825  * @extends Roo.bootstrap.Input
15826  * Bootstrap CheckBox class
15827  * 
15828  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15829  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15830  * @cfg {String} boxLabel The text that appears beside the checkbox
15831  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15832  * @cfg {Boolean} checked initnal the element
15833  * 
15834  * 
15835  * @constructor
15836  * Create a new CheckBox
15837  * @param {Object} config The config object
15838  */
15839
15840 Roo.bootstrap.CheckBox = function(config){
15841     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15842    
15843         this.addEvents({
15844             /**
15845             * @event check
15846             * Fires when the element is checked or unchecked.
15847             * @param {Roo.bootstrap.CheckBox} this This input
15848             * @param {Boolean} checked The new checked value
15849             */
15850            check : true
15851         });
15852 };
15853
15854 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15855     
15856     inputType: 'checkbox',
15857     inputValue: 1,
15858     valueOff: 0,
15859     boxLabel: false,
15860     checked: false,
15861     weight : false,
15862     
15863     getAutoCreate : function()
15864     {
15865         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15866         
15867         var id = Roo.id();
15868         
15869         var cfg = {};
15870         
15871         cfg.cls = 'form-group checkbox' //input-group
15872         
15873         
15874         
15875         
15876         var input =  {
15877             tag: 'input',
15878             id : id,
15879             type : this.inputType,
15880             value : (!this.checked) ? this.valueOff : this.inputValue,
15881             cls : 'roo-checkbox', //'form-box',
15882             placeholder : this.placeholder || ''
15883             
15884         };
15885         
15886         if (this.weight) { // Validity check?
15887             cfg.cls += " checkbox-" + this.weight;
15888         }
15889         
15890         if (this.disabled) {
15891             input.disabled=true;
15892         }
15893         
15894         if(this.checked){
15895             input.checked = this.checked;
15896         }
15897         
15898         if (this.name) {
15899             input.name = this.name;
15900         }
15901         
15902         if (this.size) {
15903             input.cls += ' input-' + this.size;
15904         }
15905         
15906         var settings=this;
15907         ['xs','sm','md','lg'].map(function(size){
15908             if (settings[size]) {
15909                 cfg.cls += ' col-' + size + '-' + settings[size];
15910             }
15911         });
15912         
15913        
15914         
15915         var inputblock = input;
15916         
15917         
15918         
15919         
15920         if (this.before || this.after) {
15921             
15922             inputblock = {
15923                 cls : 'input-group',
15924                 cn :  [] 
15925             };
15926             if (this.before) {
15927                 inputblock.cn.push({
15928                     tag :'span',
15929                     cls : 'input-group-addon',
15930                     html : this.before
15931                 });
15932             }
15933             inputblock.cn.push(input);
15934             if (this.after) {
15935                 inputblock.cn.push({
15936                     tag :'span',
15937                     cls : 'input-group-addon',
15938                     html : this.after
15939                 });
15940             }
15941             
15942         };
15943         
15944         if (align ==='left' && this.fieldLabel.length) {
15945                 Roo.log("left and has label");
15946                 cfg.cn = [
15947                     
15948                     {
15949                         tag: 'label',
15950                         'for' :  id,
15951                         cls : 'control-label col-md-' + this.labelWidth,
15952                         html : this.fieldLabel
15953                         
15954                     },
15955                     {
15956                         cls : "col-md-" + (12 - this.labelWidth), 
15957                         cn: [
15958                             inputblock
15959                         ]
15960                     }
15961                     
15962                 ];
15963         } else if ( this.fieldLabel.length) {
15964                 Roo.log(" label");
15965                 cfg.cn = [
15966                    
15967                     {
15968                         tag: this.boxLabel ? 'span' : 'label',
15969                         'for': id,
15970                         cls: 'control-label box-input-label',
15971                         //cls : 'input-group-addon',
15972                         html : this.fieldLabel
15973                         
15974                     },
15975                     
15976                     inputblock
15977                     
15978                 ];
15979
15980         } else {
15981             
15982                 Roo.log(" no label && no align");
15983                 cfg.cn = [  inputblock ] ;
15984                 
15985                 
15986         };
15987          if(this.boxLabel){
15988             cfg.cn.push( {
15989                 tag: 'label',
15990                 'for': id,
15991                 cls: 'box-label',
15992                 html: this.boxLabel
15993                 
15994             });
15995         }
15996         
15997         
15998        
15999         return cfg;
16000         
16001     },
16002     
16003     /**
16004      * return the real input element.
16005      */
16006     inputEl: function ()
16007     {
16008         return this.el.select('input.roo-checkbox',true).first();
16009     },
16010     
16011     label: function()
16012     {
16013         return this.el.select('label.control-label',true).first();
16014     },
16015     
16016     initEvents : function()
16017     {
16018 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16019         
16020         this.inputEl().on('click', this.onClick,  this);
16021         
16022     },
16023     
16024     onClick : function()
16025     {   
16026         this.setChecked(!this.checked);
16027     },
16028     
16029     setChecked : function(state,suppressEvent)
16030     {
16031         this.checked = state;
16032         
16033         this.inputEl().dom.checked = state;
16034         
16035         if(suppressEvent !== true){
16036             this.fireEvent('check', this, state);
16037         }
16038         
16039         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16040         
16041     },
16042     
16043     setValue : function(v,suppressEvent)
16044     {
16045         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16046     }
16047     
16048 });
16049
16050  
16051 /*
16052  * - LGPL
16053  *
16054  * Radio
16055  * 
16056  */
16057
16058 /**
16059  * @class Roo.bootstrap.Radio
16060  * @extends Roo.bootstrap.CheckBox
16061  * Bootstrap Radio class
16062
16063  * @constructor
16064  * Create a new Radio
16065  * @param {Object} config The config object
16066  */
16067
16068 Roo.bootstrap.Radio = function(config){
16069     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16070    
16071 };
16072
16073 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16074     
16075     inputType: 'radio',
16076     inputValue: '',
16077     valueOff: '',
16078     
16079     getAutoCreate : function()
16080     {
16081         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16082         
16083         var id = Roo.id();
16084         
16085         var cfg = {};
16086         
16087         cfg.cls = 'form-group radio' //input-group
16088         
16089         var input =  {
16090             tag: 'input',
16091             id : id,
16092             type : this.inputType,
16093             value : (!this.checked) ? this.valueOff : this.inputValue,
16094             cls : 'roo-radio',
16095             placeholder : this.placeholder || ''
16096             
16097         };
16098           if (this.weight) { // Validity check?
16099             cfg.cls += " radio-" + this.weight;
16100         }
16101         if (this.disabled) {
16102             input.disabled=true;
16103         }
16104         
16105         if(this.checked){
16106             input.checked = this.checked;
16107         }
16108         
16109         if (this.name) {
16110             input.name = this.name;
16111         }
16112         
16113         if (this.size) {
16114             input.cls += ' input-' + this.size;
16115         }
16116         
16117         var settings=this;
16118         ['xs','sm','md','lg'].map(function(size){
16119             if (settings[size]) {
16120                 cfg.cls += ' col-' + size + '-' + settings[size];
16121             }
16122         });
16123         
16124         var inputblock = input;
16125         
16126         if (this.before || this.after) {
16127             
16128             inputblock = {
16129                 cls : 'input-group',
16130                 cn :  [] 
16131             };
16132             if (this.before) {
16133                 inputblock.cn.push({
16134                     tag :'span',
16135                     cls : 'input-group-addon',
16136                     html : this.before
16137                 });
16138             }
16139             inputblock.cn.push(input);
16140             if (this.after) {
16141                 inputblock.cn.push({
16142                     tag :'span',
16143                     cls : 'input-group-addon',
16144                     html : this.after
16145                 });
16146             }
16147             
16148         };
16149         
16150         if (align ==='left' && this.fieldLabel.length) {
16151                 Roo.log("left and has label");
16152                 cfg.cn = [
16153                     
16154                     {
16155                         tag: 'label',
16156                         'for' :  id,
16157                         cls : 'control-label col-md-' + this.labelWidth,
16158                         html : this.fieldLabel
16159                         
16160                     },
16161                     {
16162                         cls : "col-md-" + (12 - this.labelWidth), 
16163                         cn: [
16164                             inputblock
16165                         ]
16166                     }
16167                     
16168                 ];
16169         } else if ( this.fieldLabel.length) {
16170                 Roo.log(" label");
16171                  cfg.cn = [
16172                    
16173                     {
16174                         tag: 'label',
16175                         'for': id,
16176                         cls: 'control-label box-input-label',
16177                         //cls : 'input-group-addon',
16178                         html : this.fieldLabel
16179                         
16180                     },
16181                     
16182                     inputblock
16183                     
16184                 ];
16185
16186         } else {
16187             
16188                    Roo.log(" no label && no align");
16189                 cfg.cn = [
16190                     
16191                         inputblock
16192                     
16193                 ];
16194                 
16195                 
16196         };
16197         
16198         if(this.boxLabel){
16199             cfg.cn.push({
16200                 tag: 'label',
16201                 'for': id,
16202                 cls: 'box-label',
16203                 html: this.boxLabel
16204             })
16205         }
16206         
16207         return cfg;
16208         
16209     },
16210     inputEl: function ()
16211     {
16212         return this.el.select('input.roo-radio',true).first();
16213     },
16214     onClick : function()
16215     {   
16216         this.setChecked(true);
16217     },
16218     
16219     setChecked : function(state,suppressEvent)
16220     {
16221         if(state){
16222             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16223                 v.dom.checked = false;
16224             });
16225         }
16226         
16227         this.checked = state;
16228         this.inputEl().dom.checked = state;
16229         
16230         if(suppressEvent !== true){
16231             this.fireEvent('check', this, state);
16232         }
16233         
16234         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16235         
16236     },
16237     
16238     getGroupValue : function()
16239     {
16240         var value = ''
16241         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16242             if(v.dom.checked == true){
16243                 value = v.dom.value;
16244             }
16245         });
16246         
16247         return value;
16248     },
16249     
16250     /**
16251      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16252      * @return {Mixed} value The field value
16253      */
16254     getValue : function(){
16255         return this.getGroupValue();
16256     }
16257     
16258 });
16259
16260  
16261 //<script type="text/javascript">
16262
16263 /*
16264  * Based  Ext JS Library 1.1.1
16265  * Copyright(c) 2006-2007, Ext JS, LLC.
16266  * LGPL
16267  *
16268  */
16269  
16270 /**
16271  * @class Roo.HtmlEditorCore
16272  * @extends Roo.Component
16273  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16274  *
16275  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16276  */
16277
16278 Roo.HtmlEditorCore = function(config){
16279     
16280     
16281     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16282     this.addEvents({
16283         /**
16284          * @event initialize
16285          * Fires when the editor is fully initialized (including the iframe)
16286          * @param {Roo.HtmlEditorCore} this
16287          */
16288         initialize: true,
16289         /**
16290          * @event activate
16291          * Fires when the editor is first receives the focus. Any insertion must wait
16292          * until after this event.
16293          * @param {Roo.HtmlEditorCore} this
16294          */
16295         activate: true,
16296          /**
16297          * @event beforesync
16298          * Fires before the textarea is updated with content from the editor iframe. Return false
16299          * to cancel the sync.
16300          * @param {Roo.HtmlEditorCore} this
16301          * @param {String} html
16302          */
16303         beforesync: true,
16304          /**
16305          * @event beforepush
16306          * Fires before the iframe editor is updated with content from the textarea. Return false
16307          * to cancel the push.
16308          * @param {Roo.HtmlEditorCore} this
16309          * @param {String} html
16310          */
16311         beforepush: true,
16312          /**
16313          * @event sync
16314          * Fires when the textarea is updated with content from the editor iframe.
16315          * @param {Roo.HtmlEditorCore} this
16316          * @param {String} html
16317          */
16318         sync: true,
16319          /**
16320          * @event push
16321          * Fires when the iframe editor is updated with content from the textarea.
16322          * @param {Roo.HtmlEditorCore} this
16323          * @param {String} html
16324          */
16325         push: true,
16326         
16327         /**
16328          * @event editorevent
16329          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16330          * @param {Roo.HtmlEditorCore} this
16331          */
16332         editorevent: true
16333     });
16334      
16335 };
16336
16337
16338 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16339
16340
16341      /**
16342      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16343      */
16344     
16345     owner : false,
16346     
16347      /**
16348      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16349      *                        Roo.resizable.
16350      */
16351     resizable : false,
16352      /**
16353      * @cfg {Number} height (in pixels)
16354      */   
16355     height: 300,
16356    /**
16357      * @cfg {Number} width (in pixels)
16358      */   
16359     width: 500,
16360     
16361     /**
16362      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16363      * 
16364      */
16365     stylesheets: false,
16366     
16367     // id of frame..
16368     frameId: false,
16369     
16370     // private properties
16371     validationEvent : false,
16372     deferHeight: true,
16373     initialized : false,
16374     activated : false,
16375     sourceEditMode : false,
16376     onFocus : Roo.emptyFn,
16377     iframePad:3,
16378     hideMode:'offsets',
16379     
16380     clearUp: true,
16381     
16382      
16383     
16384
16385     /**
16386      * Protected method that will not generally be called directly. It
16387      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16388      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16389      */
16390     getDocMarkup : function(){
16391         // body styles..
16392         var st = '';
16393         Roo.log(this.stylesheets);
16394         
16395         // inherit styels from page...?? 
16396         if (this.stylesheets === false) {
16397             
16398             Roo.get(document.head).select('style').each(function(node) {
16399                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16400             });
16401             
16402             Roo.get(document.head).select('link').each(function(node) { 
16403                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16404             });
16405             
16406         } else if (!this.stylesheets.length) {
16407                 // simple..
16408                 st = '<style type="text/css">' +
16409                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16410                    '</style>';
16411         } else {
16412             Roo.each(this.stylesheets, function(s) {
16413                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16414             });
16415             
16416         }
16417         
16418         st +=  '<style type="text/css">' +
16419             'IMG { cursor: pointer } ' +
16420         '</style>';
16421
16422         
16423         return '<html><head>' + st  +
16424             //<style type="text/css">' +
16425             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16426             //'</style>' +
16427             ' </head><body class="roo-htmleditor-body"></body></html>';
16428     },
16429
16430     // private
16431     onRender : function(ct, position)
16432     {
16433         var _t = this;
16434         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16435         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16436         
16437         
16438         this.el.dom.style.border = '0 none';
16439         this.el.dom.setAttribute('tabIndex', -1);
16440         this.el.addClass('x-hidden hide');
16441         
16442         
16443         
16444         if(Roo.isIE){ // fix IE 1px bogus margin
16445             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16446         }
16447        
16448         
16449         this.frameId = Roo.id();
16450         
16451          
16452         
16453         var iframe = this.owner.wrap.createChild({
16454             tag: 'iframe',
16455             cls: 'form-control', // bootstrap..
16456             id: this.frameId,
16457             name: this.frameId,
16458             frameBorder : 'no',
16459             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16460         }, this.el
16461         );
16462         
16463         
16464         this.iframe = iframe.dom;
16465
16466          this.assignDocWin();
16467         
16468         this.doc.designMode = 'on';
16469        
16470         this.doc.open();
16471         this.doc.write(this.getDocMarkup());
16472         this.doc.close();
16473
16474         
16475         var task = { // must defer to wait for browser to be ready
16476             run : function(){
16477                 //console.log("run task?" + this.doc.readyState);
16478                 this.assignDocWin();
16479                 if(this.doc.body || this.doc.readyState == 'complete'){
16480                     try {
16481                         this.doc.designMode="on";
16482                     } catch (e) {
16483                         return;
16484                     }
16485                     Roo.TaskMgr.stop(task);
16486                     this.initEditor.defer(10, this);
16487                 }
16488             },
16489             interval : 10,
16490             duration: 10000,
16491             scope: this
16492         };
16493         Roo.TaskMgr.start(task);
16494
16495         
16496          
16497     },
16498
16499     // private
16500     onResize : function(w, h)
16501     {
16502          Roo.log('resize: ' +w + ',' + h );
16503         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16504         if(!this.iframe){
16505             return;
16506         }
16507         if(typeof w == 'number'){
16508             
16509             this.iframe.style.width = w + 'px';
16510         }
16511         if(typeof h == 'number'){
16512             
16513             this.iframe.style.height = h + 'px';
16514             if(this.doc){
16515                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16516             }
16517         }
16518         
16519     },
16520
16521     /**
16522      * Toggles the editor between standard and source edit mode.
16523      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16524      */
16525     toggleSourceEdit : function(sourceEditMode){
16526         
16527         this.sourceEditMode = sourceEditMode === true;
16528         
16529         if(this.sourceEditMode){
16530  
16531             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16532             
16533         }else{
16534             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16535             //this.iframe.className = '';
16536             this.deferFocus();
16537         }
16538         //this.setSize(this.owner.wrap.getSize());
16539         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16540     },
16541
16542     
16543   
16544
16545     /**
16546      * Protected method that will not generally be called directly. If you need/want
16547      * custom HTML cleanup, this is the method you should override.
16548      * @param {String} html The HTML to be cleaned
16549      * return {String} The cleaned HTML
16550      */
16551     cleanHtml : function(html){
16552         html = String(html);
16553         if(html.length > 5){
16554             if(Roo.isSafari){ // strip safari nonsense
16555                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16556             }
16557         }
16558         if(html == '&nbsp;'){
16559             html = '';
16560         }
16561         return html;
16562     },
16563
16564     /**
16565      * HTML Editor -> Textarea
16566      * Protected method that will not generally be called directly. Syncs the contents
16567      * of the editor iframe with the textarea.
16568      */
16569     syncValue : function(){
16570         if(this.initialized){
16571             var bd = (this.doc.body || this.doc.documentElement);
16572             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16573             var html = bd.innerHTML;
16574             if(Roo.isSafari){
16575                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16576                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16577                 if(m && m[1]){
16578                     html = '<div style="'+m[0]+'">' + html + '</div>';
16579                 }
16580             }
16581             html = this.cleanHtml(html);
16582             // fix up the special chars.. normaly like back quotes in word...
16583             // however we do not want to do this with chinese..
16584             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16585                 var cc = b.charCodeAt();
16586                 if (
16587                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16588                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16589                     (cc >= 0xf900 && cc < 0xfb00 )
16590                 ) {
16591                         return b;
16592                 }
16593                 return "&#"+cc+";" 
16594             });
16595             if(this.owner.fireEvent('beforesync', this, html) !== false){
16596                 this.el.dom.value = html;
16597                 this.owner.fireEvent('sync', this, html);
16598             }
16599         }
16600     },
16601
16602     /**
16603      * Protected method that will not generally be called directly. Pushes the value of the textarea
16604      * into the iframe editor.
16605      */
16606     pushValue : function(){
16607         if(this.initialized){
16608             var v = this.el.dom.value.trim();
16609             
16610 //            if(v.length < 1){
16611 //                v = '&#160;';
16612 //            }
16613             
16614             if(this.owner.fireEvent('beforepush', this, v) !== false){
16615                 var d = (this.doc.body || this.doc.documentElement);
16616                 d.innerHTML = v;
16617                 this.cleanUpPaste();
16618                 this.el.dom.value = d.innerHTML;
16619                 this.owner.fireEvent('push', this, v);
16620             }
16621         }
16622     },
16623
16624     // private
16625     deferFocus : function(){
16626         this.focus.defer(10, this);
16627     },
16628
16629     // doc'ed in Field
16630     focus : function(){
16631         if(this.win && !this.sourceEditMode){
16632             this.win.focus();
16633         }else{
16634             this.el.focus();
16635         }
16636     },
16637     
16638     assignDocWin: function()
16639     {
16640         var iframe = this.iframe;
16641         
16642          if(Roo.isIE){
16643             this.doc = iframe.contentWindow.document;
16644             this.win = iframe.contentWindow;
16645         } else {
16646 //            if (!Roo.get(this.frameId)) {
16647 //                return;
16648 //            }
16649 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16650 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16651             
16652             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16653                 return;
16654             }
16655             
16656             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16657             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16658         }
16659     },
16660     
16661     // private
16662     initEditor : function(){
16663         //console.log("INIT EDITOR");
16664         this.assignDocWin();
16665         
16666         
16667         
16668         this.doc.designMode="on";
16669         this.doc.open();
16670         this.doc.write(this.getDocMarkup());
16671         this.doc.close();
16672         
16673         var dbody = (this.doc.body || this.doc.documentElement);
16674         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16675         // this copies styles from the containing element into thsi one..
16676         // not sure why we need all of this..
16677         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16678         
16679         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16680         //ss['background-attachment'] = 'fixed'; // w3c
16681         dbody.bgProperties = 'fixed'; // ie
16682         //Roo.DomHelper.applyStyles(dbody, ss);
16683         Roo.EventManager.on(this.doc, {
16684             //'mousedown': this.onEditorEvent,
16685             'mouseup': this.onEditorEvent,
16686             'dblclick': this.onEditorEvent,
16687             'click': this.onEditorEvent,
16688             'keyup': this.onEditorEvent,
16689             buffer:100,
16690             scope: this
16691         });
16692         if(Roo.isGecko){
16693             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16694         }
16695         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16696             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16697         }
16698         this.initialized = true;
16699
16700         this.owner.fireEvent('initialize', this);
16701         this.pushValue();
16702     },
16703
16704     // private
16705     onDestroy : function(){
16706         
16707         
16708         
16709         if(this.rendered){
16710             
16711             //for (var i =0; i < this.toolbars.length;i++) {
16712             //    // fixme - ask toolbars for heights?
16713             //    this.toolbars[i].onDestroy();
16714            // }
16715             
16716             //this.wrap.dom.innerHTML = '';
16717             //this.wrap.remove();
16718         }
16719     },
16720
16721     // private
16722     onFirstFocus : function(){
16723         
16724         this.assignDocWin();
16725         
16726         
16727         this.activated = true;
16728          
16729     
16730         if(Roo.isGecko){ // prevent silly gecko errors
16731             this.win.focus();
16732             var s = this.win.getSelection();
16733             if(!s.focusNode || s.focusNode.nodeType != 3){
16734                 var r = s.getRangeAt(0);
16735                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16736                 r.collapse(true);
16737                 this.deferFocus();
16738             }
16739             try{
16740                 this.execCmd('useCSS', true);
16741                 this.execCmd('styleWithCSS', false);
16742             }catch(e){}
16743         }
16744         this.owner.fireEvent('activate', this);
16745     },
16746
16747     // private
16748     adjustFont: function(btn){
16749         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16750         //if(Roo.isSafari){ // safari
16751         //    adjust *= 2;
16752        // }
16753         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16754         if(Roo.isSafari){ // safari
16755             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16756             v =  (v < 10) ? 10 : v;
16757             v =  (v > 48) ? 48 : v;
16758             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16759             
16760         }
16761         
16762         
16763         v = Math.max(1, v+adjust);
16764         
16765         this.execCmd('FontSize', v  );
16766     },
16767
16768     onEditorEvent : function(e){
16769         this.owner.fireEvent('editorevent', this, e);
16770       //  this.updateToolbar();
16771         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16772     },
16773
16774     insertTag : function(tg)
16775     {
16776         // could be a bit smarter... -> wrap the current selected tRoo..
16777         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16778             
16779             range = this.createRange(this.getSelection());
16780             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16781             wrappingNode.appendChild(range.extractContents());
16782             range.insertNode(wrappingNode);
16783
16784             return;
16785             
16786             
16787             
16788         }
16789         this.execCmd("formatblock",   tg);
16790         
16791     },
16792     
16793     insertText : function(txt)
16794     {
16795         
16796         
16797         var range = this.createRange();
16798         range.deleteContents();
16799                //alert(Sender.getAttribute('label'));
16800                
16801         range.insertNode(this.doc.createTextNode(txt));
16802     } ,
16803     
16804      
16805
16806     /**
16807      * Executes a Midas editor command on the editor document and performs necessary focus and
16808      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16809      * @param {String} cmd The Midas command
16810      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16811      */
16812     relayCmd : function(cmd, value){
16813         this.win.focus();
16814         this.execCmd(cmd, value);
16815         this.owner.fireEvent('editorevent', this);
16816         //this.updateToolbar();
16817         this.owner.deferFocus();
16818     },
16819
16820     /**
16821      * Executes a Midas editor command directly on the editor document.
16822      * For visual commands, you should use {@link #relayCmd} instead.
16823      * <b>This should only be called after the editor is initialized.</b>
16824      * @param {String} cmd The Midas command
16825      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16826      */
16827     execCmd : function(cmd, value){
16828         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16829         this.syncValue();
16830     },
16831  
16832  
16833    
16834     /**
16835      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16836      * to insert tRoo.
16837      * @param {String} text | dom node.. 
16838      */
16839     insertAtCursor : function(text)
16840     {
16841         
16842         
16843         
16844         if(!this.activated){
16845             return;
16846         }
16847         /*
16848         if(Roo.isIE){
16849             this.win.focus();
16850             var r = this.doc.selection.createRange();
16851             if(r){
16852                 r.collapse(true);
16853                 r.pasteHTML(text);
16854                 this.syncValue();
16855                 this.deferFocus();
16856             
16857             }
16858             return;
16859         }
16860         */
16861         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16862             this.win.focus();
16863             
16864             
16865             // from jquery ui (MIT licenced)
16866             var range, node;
16867             var win = this.win;
16868             
16869             if (win.getSelection && win.getSelection().getRangeAt) {
16870                 range = win.getSelection().getRangeAt(0);
16871                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16872                 range.insertNode(node);
16873             } else if (win.document.selection && win.document.selection.createRange) {
16874                 // no firefox support
16875                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16876                 win.document.selection.createRange().pasteHTML(txt);
16877             } else {
16878                 // no firefox support
16879                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16880                 this.execCmd('InsertHTML', txt);
16881             } 
16882             
16883             this.syncValue();
16884             
16885             this.deferFocus();
16886         }
16887     },
16888  // private
16889     mozKeyPress : function(e){
16890         if(e.ctrlKey){
16891             var c = e.getCharCode(), cmd;
16892           
16893             if(c > 0){
16894                 c = String.fromCharCode(c).toLowerCase();
16895                 switch(c){
16896                     case 'b':
16897                         cmd = 'bold';
16898                         break;
16899                     case 'i':
16900                         cmd = 'italic';
16901                         break;
16902                     
16903                     case 'u':
16904                         cmd = 'underline';
16905                         break;
16906                     
16907                     case 'v':
16908                         this.cleanUpPaste.defer(100, this);
16909                         return;
16910                         
16911                 }
16912                 if(cmd){
16913                     this.win.focus();
16914                     this.execCmd(cmd);
16915                     this.deferFocus();
16916                     e.preventDefault();
16917                 }
16918                 
16919             }
16920         }
16921     },
16922
16923     // private
16924     fixKeys : function(){ // load time branching for fastest keydown performance
16925         if(Roo.isIE){
16926             return function(e){
16927                 var k = e.getKey(), r;
16928                 if(k == e.TAB){
16929                     e.stopEvent();
16930                     r = this.doc.selection.createRange();
16931                     if(r){
16932                         r.collapse(true);
16933                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16934                         this.deferFocus();
16935                     }
16936                     return;
16937                 }
16938                 
16939                 if(k == e.ENTER){
16940                     r = this.doc.selection.createRange();
16941                     if(r){
16942                         var target = r.parentElement();
16943                         if(!target || target.tagName.toLowerCase() != 'li'){
16944                             e.stopEvent();
16945                             r.pasteHTML('<br />');
16946                             r.collapse(false);
16947                             r.select();
16948                         }
16949                     }
16950                 }
16951                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16952                     this.cleanUpPaste.defer(100, this);
16953                     return;
16954                 }
16955                 
16956                 
16957             };
16958         }else if(Roo.isOpera){
16959             return function(e){
16960                 var k = e.getKey();
16961                 if(k == e.TAB){
16962                     e.stopEvent();
16963                     this.win.focus();
16964                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16965                     this.deferFocus();
16966                 }
16967                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16968                     this.cleanUpPaste.defer(100, this);
16969                     return;
16970                 }
16971                 
16972             };
16973         }else if(Roo.isSafari){
16974             return function(e){
16975                 var k = e.getKey();
16976                 
16977                 if(k == e.TAB){
16978                     e.stopEvent();
16979                     this.execCmd('InsertText','\t');
16980                     this.deferFocus();
16981                     return;
16982                 }
16983                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16984                     this.cleanUpPaste.defer(100, this);
16985                     return;
16986                 }
16987                 
16988              };
16989         }
16990     }(),
16991     
16992     getAllAncestors: function()
16993     {
16994         var p = this.getSelectedNode();
16995         var a = [];
16996         if (!p) {
16997             a.push(p); // push blank onto stack..
16998             p = this.getParentElement();
16999         }
17000         
17001         
17002         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17003             a.push(p);
17004             p = p.parentNode;
17005         }
17006         a.push(this.doc.body);
17007         return a;
17008     },
17009     lastSel : false,
17010     lastSelNode : false,
17011     
17012     
17013     getSelection : function() 
17014     {
17015         this.assignDocWin();
17016         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17017     },
17018     
17019     getSelectedNode: function() 
17020     {
17021         // this may only work on Gecko!!!
17022         
17023         // should we cache this!!!!
17024         
17025         
17026         
17027          
17028         var range = this.createRange(this.getSelection()).cloneRange();
17029         
17030         if (Roo.isIE) {
17031             var parent = range.parentElement();
17032             while (true) {
17033                 var testRange = range.duplicate();
17034                 testRange.moveToElementText(parent);
17035                 if (testRange.inRange(range)) {
17036                     break;
17037                 }
17038                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17039                     break;
17040                 }
17041                 parent = parent.parentElement;
17042             }
17043             return parent;
17044         }
17045         
17046         // is ancestor a text element.
17047         var ac =  range.commonAncestorContainer;
17048         if (ac.nodeType == 3) {
17049             ac = ac.parentNode;
17050         }
17051         
17052         var ar = ac.childNodes;
17053          
17054         var nodes = [];
17055         var other_nodes = [];
17056         var has_other_nodes = false;
17057         for (var i=0;i<ar.length;i++) {
17058             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17059                 continue;
17060             }
17061             // fullly contained node.
17062             
17063             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17064                 nodes.push(ar[i]);
17065                 continue;
17066             }
17067             
17068             // probably selected..
17069             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17070                 other_nodes.push(ar[i]);
17071                 continue;
17072             }
17073             // outer..
17074             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17075                 continue;
17076             }
17077             
17078             
17079             has_other_nodes = true;
17080         }
17081         if (!nodes.length && other_nodes.length) {
17082             nodes= other_nodes;
17083         }
17084         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17085             return false;
17086         }
17087         
17088         return nodes[0];
17089     },
17090     createRange: function(sel)
17091     {
17092         // this has strange effects when using with 
17093         // top toolbar - not sure if it's a great idea.
17094         //this.editor.contentWindow.focus();
17095         if (typeof sel != "undefined") {
17096             try {
17097                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17098             } catch(e) {
17099                 return this.doc.createRange();
17100             }
17101         } else {
17102             return this.doc.createRange();
17103         }
17104     },
17105     getParentElement: function()
17106     {
17107         
17108         this.assignDocWin();
17109         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17110         
17111         var range = this.createRange(sel);
17112          
17113         try {
17114             var p = range.commonAncestorContainer;
17115             while (p.nodeType == 3) { // text node
17116                 p = p.parentNode;
17117             }
17118             return p;
17119         } catch (e) {
17120             return null;
17121         }
17122     
17123     },
17124     /***
17125      *
17126      * Range intersection.. the hard stuff...
17127      *  '-1' = before
17128      *  '0' = hits..
17129      *  '1' = after.
17130      *         [ -- selected range --- ]
17131      *   [fail]                        [fail]
17132      *
17133      *    basically..
17134      *      if end is before start or  hits it. fail.
17135      *      if start is after end or hits it fail.
17136      *
17137      *   if either hits (but other is outside. - then it's not 
17138      *   
17139      *    
17140      **/
17141     
17142     
17143     // @see http://www.thismuchiknow.co.uk/?p=64.
17144     rangeIntersectsNode : function(range, node)
17145     {
17146         var nodeRange = node.ownerDocument.createRange();
17147         try {
17148             nodeRange.selectNode(node);
17149         } catch (e) {
17150             nodeRange.selectNodeContents(node);
17151         }
17152     
17153         var rangeStartRange = range.cloneRange();
17154         rangeStartRange.collapse(true);
17155     
17156         var rangeEndRange = range.cloneRange();
17157         rangeEndRange.collapse(false);
17158     
17159         var nodeStartRange = nodeRange.cloneRange();
17160         nodeStartRange.collapse(true);
17161     
17162         var nodeEndRange = nodeRange.cloneRange();
17163         nodeEndRange.collapse(false);
17164     
17165         return rangeStartRange.compareBoundaryPoints(
17166                  Range.START_TO_START, nodeEndRange) == -1 &&
17167                rangeEndRange.compareBoundaryPoints(
17168                  Range.START_TO_START, nodeStartRange) == 1;
17169         
17170          
17171     },
17172     rangeCompareNode : function(range, node)
17173     {
17174         var nodeRange = node.ownerDocument.createRange();
17175         try {
17176             nodeRange.selectNode(node);
17177         } catch (e) {
17178             nodeRange.selectNodeContents(node);
17179         }
17180         
17181         
17182         range.collapse(true);
17183     
17184         nodeRange.collapse(true);
17185      
17186         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17187         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17188          
17189         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17190         
17191         var nodeIsBefore   =  ss == 1;
17192         var nodeIsAfter    = ee == -1;
17193         
17194         if (nodeIsBefore && nodeIsAfter)
17195             return 0; // outer
17196         if (!nodeIsBefore && nodeIsAfter)
17197             return 1; //right trailed.
17198         
17199         if (nodeIsBefore && !nodeIsAfter)
17200             return 2;  // left trailed.
17201         // fully contined.
17202         return 3;
17203     },
17204
17205     // private? - in a new class?
17206     cleanUpPaste :  function()
17207     {
17208         // cleans up the whole document..
17209         Roo.log('cleanuppaste');
17210         
17211         this.cleanUpChildren(this.doc.body);
17212         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17213         if (clean != this.doc.body.innerHTML) {
17214             this.doc.body.innerHTML = clean;
17215         }
17216         
17217     },
17218     
17219     cleanWordChars : function(input) {// change the chars to hex code
17220         var he = Roo.HtmlEditorCore;
17221         
17222         var output = input;
17223         Roo.each(he.swapCodes, function(sw) { 
17224             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17225             
17226             output = output.replace(swapper, sw[1]);
17227         });
17228         
17229         return output;
17230     },
17231     
17232     
17233     cleanUpChildren : function (n)
17234     {
17235         if (!n.childNodes.length) {
17236             return;
17237         }
17238         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17239            this.cleanUpChild(n.childNodes[i]);
17240         }
17241     },
17242     
17243     
17244         
17245     
17246     cleanUpChild : function (node)
17247     {
17248         var ed = this;
17249         //console.log(node);
17250         if (node.nodeName == "#text") {
17251             // clean up silly Windows -- stuff?
17252             return; 
17253         }
17254         if (node.nodeName == "#comment") {
17255             node.parentNode.removeChild(node);
17256             // clean up silly Windows -- stuff?
17257             return; 
17258         }
17259         
17260         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17261             // remove node.
17262             node.parentNode.removeChild(node);
17263             return;
17264             
17265         }
17266         
17267         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17268         
17269         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17270         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17271         
17272         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17273         //    remove_keep_children = true;
17274         //}
17275         
17276         if (remove_keep_children) {
17277             this.cleanUpChildren(node);
17278             // inserts everything just before this node...
17279             while (node.childNodes.length) {
17280                 var cn = node.childNodes[0];
17281                 node.removeChild(cn);
17282                 node.parentNode.insertBefore(cn, node);
17283             }
17284             node.parentNode.removeChild(node);
17285             return;
17286         }
17287         
17288         if (!node.attributes || !node.attributes.length) {
17289             this.cleanUpChildren(node);
17290             return;
17291         }
17292         
17293         function cleanAttr(n,v)
17294         {
17295             
17296             if (v.match(/^\./) || v.match(/^\//)) {
17297                 return;
17298             }
17299             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17300                 return;
17301             }
17302             if (v.match(/^#/)) {
17303                 return;
17304             }
17305 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17306             node.removeAttribute(n);
17307             
17308         }
17309         
17310         function cleanStyle(n,v)
17311         {
17312             if (v.match(/expression/)) { //XSS?? should we even bother..
17313                 node.removeAttribute(n);
17314                 return;
17315             }
17316             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17317             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17318             
17319             
17320             var parts = v.split(/;/);
17321             var clean = [];
17322             
17323             Roo.each(parts, function(p) {
17324                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17325                 if (!p.length) {
17326                     return true;
17327                 }
17328                 var l = p.split(':').shift().replace(/\s+/g,'');
17329                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17330                 
17331                 if ( cblack.indexOf(l) > -1) {
17332 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17333                     //node.removeAttribute(n);
17334                     return true;
17335                 }
17336                 //Roo.log()
17337                 // only allow 'c whitelisted system attributes'
17338                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17339 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17340                     //node.removeAttribute(n);
17341                     return true;
17342                 }
17343                 
17344                 
17345                  
17346                 
17347                 clean.push(p);
17348                 return true;
17349             });
17350             if (clean.length) { 
17351                 node.setAttribute(n, clean.join(';'));
17352             } else {
17353                 node.removeAttribute(n);
17354             }
17355             
17356         }
17357         
17358         
17359         for (var i = node.attributes.length-1; i > -1 ; i--) {
17360             var a = node.attributes[i];
17361             //console.log(a);
17362             
17363             if (a.name.toLowerCase().substr(0,2)=='on')  {
17364                 node.removeAttribute(a.name);
17365                 continue;
17366             }
17367             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17368                 node.removeAttribute(a.name);
17369                 continue;
17370             }
17371             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17372                 cleanAttr(a.name,a.value); // fixme..
17373                 continue;
17374             }
17375             if (a.name == 'style') {
17376                 cleanStyle(a.name,a.value);
17377                 continue;
17378             }
17379             /// clean up MS crap..
17380             // tecnically this should be a list of valid class'es..
17381             
17382             
17383             if (a.name == 'class') {
17384                 if (a.value.match(/^Mso/)) {
17385                     node.className = '';
17386                 }
17387                 
17388                 if (a.value.match(/body/)) {
17389                     node.className = '';
17390                 }
17391                 continue;
17392             }
17393             
17394             // style cleanup!?
17395             // class cleanup?
17396             
17397         }
17398         
17399         
17400         this.cleanUpChildren(node);
17401         
17402         
17403     },
17404     /**
17405      * Clean up MS wordisms...
17406      */
17407     cleanWord : function(node)
17408     {
17409         var _t = this;
17410         var cleanWordChildren = function()
17411         {
17412             if (!node.childNodes.length) {
17413                 return;
17414             }
17415             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17416                _t.cleanWord(node.childNodes[i]);
17417             }
17418         }
17419         
17420         
17421         if (!node) {
17422             this.cleanWord(this.doc.body);
17423             return;
17424         }
17425         if (node.nodeName == "#text") {
17426             // clean up silly Windows -- stuff?
17427             return; 
17428         }
17429         if (node.nodeName == "#comment") {
17430             node.parentNode.removeChild(node);
17431             // clean up silly Windows -- stuff?
17432             return; 
17433         }
17434         
17435         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17436             node.parentNode.removeChild(node);
17437             return;
17438         }
17439         
17440         // remove - but keep children..
17441         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17442             while (node.childNodes.length) {
17443                 var cn = node.childNodes[0];
17444                 node.removeChild(cn);
17445                 node.parentNode.insertBefore(cn, node);
17446             }
17447             node.parentNode.removeChild(node);
17448             cleanWordChildren();
17449             return;
17450         }
17451         // clean styles
17452         if (node.className.length) {
17453             
17454             var cn = node.className.split(/\W+/);
17455             var cna = [];
17456             Roo.each(cn, function(cls) {
17457                 if (cls.match(/Mso[a-zA-Z]+/)) {
17458                     return;
17459                 }
17460                 cna.push(cls);
17461             });
17462             node.className = cna.length ? cna.join(' ') : '';
17463             if (!cna.length) {
17464                 node.removeAttribute("class");
17465             }
17466         }
17467         
17468         if (node.hasAttribute("lang")) {
17469             node.removeAttribute("lang");
17470         }
17471         
17472         if (node.hasAttribute("style")) {
17473             
17474             var styles = node.getAttribute("style").split(";");
17475             var nstyle = [];
17476             Roo.each(styles, function(s) {
17477                 if (!s.match(/:/)) {
17478                     return;
17479                 }
17480                 var kv = s.split(":");
17481                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17482                     return;
17483                 }
17484                 // what ever is left... we allow.
17485                 nstyle.push(s);
17486             });
17487             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17488             if (!nstyle.length) {
17489                 node.removeAttribute('style');
17490             }
17491         }
17492         
17493         cleanWordChildren();
17494         
17495         
17496     },
17497     domToHTML : function(currentElement, depth, nopadtext) {
17498         
17499             depth = depth || 0;
17500             nopadtext = nopadtext || false;
17501         
17502             if (!currentElement) {
17503                 return this.domToHTML(this.doc.body);
17504             }
17505             
17506             //Roo.log(currentElement);
17507             var j;
17508             var allText = false;
17509             var nodeName = currentElement.nodeName;
17510             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17511             
17512             if  (nodeName == '#text') {
17513                 return currentElement.nodeValue;
17514             }
17515             
17516             
17517             var ret = '';
17518             if (nodeName != 'BODY') {
17519                  
17520                 var i = 0;
17521                 // Prints the node tagName, such as <A>, <IMG>, etc
17522                 if (tagName) {
17523                     var attr = [];
17524                     for(i = 0; i < currentElement.attributes.length;i++) {
17525                         // quoting?
17526                         var aname = currentElement.attributes.item(i).name;
17527                         if (!currentElement.attributes.item(i).value.length) {
17528                             continue;
17529                         }
17530                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17531                     }
17532                     
17533                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17534                 } 
17535                 else {
17536                     
17537                     // eack
17538                 }
17539             } else {
17540                 tagName = false;
17541             }
17542             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17543                 return ret;
17544             }
17545             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17546                 nopadtext = true;
17547             }
17548             
17549             
17550             // Traverse the tree
17551             i = 0;
17552             var currentElementChild = currentElement.childNodes.item(i);
17553             var allText = true;
17554             var innerHTML  = '';
17555             lastnode = '';
17556             while (currentElementChild) {
17557                 // Formatting code (indent the tree so it looks nice on the screen)
17558                 var nopad = nopadtext;
17559                 if (lastnode == 'SPAN') {
17560                     nopad  = true;
17561                 }
17562                 // text
17563                 if  (currentElementChild.nodeName == '#text') {
17564                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17565                     if (!nopad && toadd.length > 80) {
17566                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17567                     }
17568                     innerHTML  += toadd;
17569                     
17570                     i++;
17571                     currentElementChild = currentElement.childNodes.item(i);
17572                     lastNode = '';
17573                     continue;
17574                 }
17575                 allText = false;
17576                 
17577                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17578                     
17579                 // Recursively traverse the tree structure of the child node
17580                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17581                 lastnode = currentElementChild.nodeName;
17582                 i++;
17583                 currentElementChild=currentElement.childNodes.item(i);
17584             }
17585             
17586             ret += innerHTML;
17587             
17588             if (!allText) {
17589                     // The remaining code is mostly for formatting the tree
17590                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17591             }
17592             
17593             
17594             if (tagName) {
17595                 ret+= "</"+tagName+">";
17596             }
17597             return ret;
17598             
17599         }
17600     
17601     // hide stuff that is not compatible
17602     /**
17603      * @event blur
17604      * @hide
17605      */
17606     /**
17607      * @event change
17608      * @hide
17609      */
17610     /**
17611      * @event focus
17612      * @hide
17613      */
17614     /**
17615      * @event specialkey
17616      * @hide
17617      */
17618     /**
17619      * @cfg {String} fieldClass @hide
17620      */
17621     /**
17622      * @cfg {String} focusClass @hide
17623      */
17624     /**
17625      * @cfg {String} autoCreate @hide
17626      */
17627     /**
17628      * @cfg {String} inputType @hide
17629      */
17630     /**
17631      * @cfg {String} invalidClass @hide
17632      */
17633     /**
17634      * @cfg {String} invalidText @hide
17635      */
17636     /**
17637      * @cfg {String} msgFx @hide
17638      */
17639     /**
17640      * @cfg {String} validateOnBlur @hide
17641      */
17642 });
17643
17644 Roo.HtmlEditorCore.white = [
17645         'area', 'br', 'img', 'input', 'hr', 'wbr',
17646         
17647        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17648        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17649        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17650        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17651        'table',   'ul',         'xmp', 
17652        
17653        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17654       'thead',   'tr', 
17655      
17656       'dir', 'menu', 'ol', 'ul', 'dl',
17657        
17658       'embed',  'object'
17659 ];
17660
17661
17662 Roo.HtmlEditorCore.black = [
17663     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17664         'applet', // 
17665         'base',   'basefont', 'bgsound', 'blink',  'body', 
17666         'frame',  'frameset', 'head',    'html',   'ilayer', 
17667         'iframe', 'layer',  'link',     'meta',    'object',   
17668         'script', 'style' ,'title',  'xml' // clean later..
17669 ];
17670 Roo.HtmlEditorCore.clean = [
17671     'script', 'style', 'title', 'xml'
17672 ];
17673 Roo.HtmlEditorCore.remove = [
17674     'font'
17675 ];
17676 // attributes..
17677
17678 Roo.HtmlEditorCore.ablack = [
17679     'on'
17680 ];
17681     
17682 Roo.HtmlEditorCore.aclean = [ 
17683     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17684 ];
17685
17686 // protocols..
17687 Roo.HtmlEditorCore.pwhite= [
17688         'http',  'https',  'mailto'
17689 ];
17690
17691 // white listed style attributes.
17692 Roo.HtmlEditorCore.cwhite= [
17693       //  'text-align', /// default is to allow most things..
17694       
17695          
17696 //        'font-size'//??
17697 ];
17698
17699 // black listed style attributes.
17700 Roo.HtmlEditorCore.cblack= [
17701       //  'font-size' -- this can be set by the project 
17702 ];
17703
17704
17705 Roo.HtmlEditorCore.swapCodes   =[ 
17706     [    8211, "--" ], 
17707     [    8212, "--" ], 
17708     [    8216,  "'" ],  
17709     [    8217, "'" ],  
17710     [    8220, '"' ],  
17711     [    8221, '"' ],  
17712     [    8226, "*" ],  
17713     [    8230, "..." ]
17714 ]; 
17715
17716     /*
17717  * - LGPL
17718  *
17719  * HtmlEditor
17720  * 
17721  */
17722
17723 /**
17724  * @class Roo.bootstrap.HtmlEditor
17725  * @extends Roo.bootstrap.TextArea
17726  * Bootstrap HtmlEditor class
17727
17728  * @constructor
17729  * Create a new HtmlEditor
17730  * @param {Object} config The config object
17731  */
17732
17733 Roo.bootstrap.HtmlEditor = function(config){
17734     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17735     if (!this.toolbars) {
17736         this.toolbars = [];
17737     }
17738     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17739     this.addEvents({
17740             /**
17741              * @event initialize
17742              * Fires when the editor is fully initialized (including the iframe)
17743              * @param {HtmlEditor} this
17744              */
17745             initialize: true,
17746             /**
17747              * @event activate
17748              * Fires when the editor is first receives the focus. Any insertion must wait
17749              * until after this event.
17750              * @param {HtmlEditor} this
17751              */
17752             activate: true,
17753              /**
17754              * @event beforesync
17755              * Fires before the textarea is updated with content from the editor iframe. Return false
17756              * to cancel the sync.
17757              * @param {HtmlEditor} this
17758              * @param {String} html
17759              */
17760             beforesync: true,
17761              /**
17762              * @event beforepush
17763              * Fires before the iframe editor is updated with content from the textarea. Return false
17764              * to cancel the push.
17765              * @param {HtmlEditor} this
17766              * @param {String} html
17767              */
17768             beforepush: true,
17769              /**
17770              * @event sync
17771              * Fires when the textarea is updated with content from the editor iframe.
17772              * @param {HtmlEditor} this
17773              * @param {String} html
17774              */
17775             sync: true,
17776              /**
17777              * @event push
17778              * Fires when the iframe editor is updated with content from the textarea.
17779              * @param {HtmlEditor} this
17780              * @param {String} html
17781              */
17782             push: true,
17783              /**
17784              * @event editmodechange
17785              * Fires when the editor switches edit modes
17786              * @param {HtmlEditor} this
17787              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17788              */
17789             editmodechange: true,
17790             /**
17791              * @event editorevent
17792              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17793              * @param {HtmlEditor} this
17794              */
17795             editorevent: true,
17796             /**
17797              * @event firstfocus
17798              * Fires when on first focus - needed by toolbars..
17799              * @param {HtmlEditor} this
17800              */
17801             firstfocus: true,
17802             /**
17803              * @event autosave
17804              * Auto save the htmlEditor value as a file into Events
17805              * @param {HtmlEditor} this
17806              */
17807             autosave: true,
17808             /**
17809              * @event savedpreview
17810              * preview the saved version of htmlEditor
17811              * @param {HtmlEditor} this
17812              */
17813             savedpreview: true
17814         });
17815 };
17816
17817
17818 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17819     
17820     
17821       /**
17822      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17823      */
17824     toolbars : false,
17825    
17826      /**
17827      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17828      *                        Roo.resizable.
17829      */
17830     resizable : false,
17831      /**
17832      * @cfg {Number} height (in pixels)
17833      */   
17834     height: 300,
17835    /**
17836      * @cfg {Number} width (in pixels)
17837      */   
17838     width: false,
17839     
17840     /**
17841      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17842      * 
17843      */
17844     stylesheets: false,
17845     
17846     // id of frame..
17847     frameId: false,
17848     
17849     // private properties
17850     validationEvent : false,
17851     deferHeight: true,
17852     initialized : false,
17853     activated : false,
17854     
17855     onFocus : Roo.emptyFn,
17856     iframePad:3,
17857     hideMode:'offsets',
17858     
17859     
17860     tbContainer : false,
17861     
17862     toolbarContainer :function() {
17863         return this.wrap.select('.x-html-editor-tb',true).first();
17864     },
17865
17866     /**
17867      * Protected method that will not generally be called directly. It
17868      * is called when the editor creates its toolbar. Override this method if you need to
17869      * add custom toolbar buttons.
17870      * @param {HtmlEditor} editor
17871      */
17872     createToolbar : function(){
17873         
17874         Roo.log("create toolbars");
17875         
17876         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17877         this.toolbars[0].render(this.toolbarContainer());
17878         
17879         return;
17880         
17881 //        if (!editor.toolbars || !editor.toolbars.length) {
17882 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17883 //        }
17884 //        
17885 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17886 //            editor.toolbars[i] = Roo.factory(
17887 //                    typeof(editor.toolbars[i]) == 'string' ?
17888 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17889 //                Roo.bootstrap.HtmlEditor);
17890 //            editor.toolbars[i].init(editor);
17891 //        }
17892     },
17893
17894      
17895     // private
17896     onRender : function(ct, position)
17897     {
17898        // Roo.log("Call onRender: " + this.xtype);
17899         var _t = this;
17900         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17901       
17902         this.wrap = this.inputEl().wrap({
17903             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17904         });
17905         
17906         this.editorcore.onRender(ct, position);
17907          
17908         if (this.resizable) {
17909             this.resizeEl = new Roo.Resizable(this.wrap, {
17910                 pinned : true,
17911                 wrap: true,
17912                 dynamic : true,
17913                 minHeight : this.height,
17914                 height: this.height,
17915                 handles : this.resizable,
17916                 width: this.width,
17917                 listeners : {
17918                     resize : function(r, w, h) {
17919                         _t.onResize(w,h); // -something
17920                     }
17921                 }
17922             });
17923             
17924         }
17925         this.createToolbar(this);
17926        
17927         
17928         if(!this.width && this.resizable){
17929             this.setSize(this.wrap.getSize());
17930         }
17931         if (this.resizeEl) {
17932             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17933             // should trigger onReize..
17934         }
17935         
17936     },
17937
17938     // private
17939     onResize : function(w, h)
17940     {
17941         Roo.log('resize: ' +w + ',' + h );
17942         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17943         var ew = false;
17944         var eh = false;
17945         
17946         if(this.inputEl() ){
17947             if(typeof w == 'number'){
17948                 var aw = w - this.wrap.getFrameWidth('lr');
17949                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17950                 ew = aw;
17951             }
17952             if(typeof h == 'number'){
17953                  var tbh = -11;  // fixme it needs to tool bar size!
17954                 for (var i =0; i < this.toolbars.length;i++) {
17955                     // fixme - ask toolbars for heights?
17956                     tbh += this.toolbars[i].el.getHeight();
17957                     //if (this.toolbars[i].footer) {
17958                     //    tbh += this.toolbars[i].footer.el.getHeight();
17959                     //}
17960                 }
17961               
17962                 
17963                 
17964                 
17965                 
17966                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17967                 ah -= 5; // knock a few pixes off for look..
17968                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17969                 var eh = ah;
17970             }
17971         }
17972         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17973         this.editorcore.onResize(ew,eh);
17974         
17975     },
17976
17977     /**
17978      * Toggles the editor between standard and source edit mode.
17979      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17980      */
17981     toggleSourceEdit : function(sourceEditMode)
17982     {
17983         this.editorcore.toggleSourceEdit(sourceEditMode);
17984         
17985         if(this.editorcore.sourceEditMode){
17986             Roo.log('editor - showing textarea');
17987             
17988 //            Roo.log('in');
17989 //            Roo.log(this.syncValue());
17990             this.syncValue();
17991             this.inputEl().removeClass(['hide', 'x-hidden']);
17992             this.inputEl().dom.removeAttribute('tabIndex');
17993             this.inputEl().focus();
17994         }else{
17995             Roo.log('editor - hiding textarea');
17996 //            Roo.log('out')
17997 //            Roo.log(this.pushValue()); 
17998             this.pushValue();
17999             
18000             this.inputEl().addClass(['hide', 'x-hidden']);
18001             this.inputEl().dom.setAttribute('tabIndex', -1);
18002             //this.deferFocus();
18003         }
18004          
18005         if(this.resizable){
18006             this.setSize(this.wrap.getSize());
18007         }
18008         
18009         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18010     },
18011  
18012     // private (for BoxComponent)
18013     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18014
18015     // private (for BoxComponent)
18016     getResizeEl : function(){
18017         return this.wrap;
18018     },
18019
18020     // private (for BoxComponent)
18021     getPositionEl : function(){
18022         return this.wrap;
18023     },
18024
18025     // private
18026     initEvents : function(){
18027         this.originalValue = this.getValue();
18028     },
18029
18030 //    /**
18031 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18032 //     * @method
18033 //     */
18034 //    markInvalid : Roo.emptyFn,
18035 //    /**
18036 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18037 //     * @method
18038 //     */
18039 //    clearInvalid : Roo.emptyFn,
18040
18041     setValue : function(v){
18042         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18043         this.editorcore.pushValue();
18044     },
18045
18046      
18047     // private
18048     deferFocus : function(){
18049         this.focus.defer(10, this);
18050     },
18051
18052     // doc'ed in Field
18053     focus : function(){
18054         this.editorcore.focus();
18055         
18056     },
18057       
18058
18059     // private
18060     onDestroy : function(){
18061         
18062         
18063         
18064         if(this.rendered){
18065             
18066             for (var i =0; i < this.toolbars.length;i++) {
18067                 // fixme - ask toolbars for heights?
18068                 this.toolbars[i].onDestroy();
18069             }
18070             
18071             this.wrap.dom.innerHTML = '';
18072             this.wrap.remove();
18073         }
18074     },
18075
18076     // private
18077     onFirstFocus : function(){
18078         //Roo.log("onFirstFocus");
18079         this.editorcore.onFirstFocus();
18080          for (var i =0; i < this.toolbars.length;i++) {
18081             this.toolbars[i].onFirstFocus();
18082         }
18083         
18084     },
18085     
18086     // private
18087     syncValue : function()
18088     {   
18089         this.editorcore.syncValue();
18090     },
18091     
18092     pushValue : function()
18093     {   
18094         this.editorcore.pushValue();
18095     }
18096      
18097     
18098     // hide stuff that is not compatible
18099     /**
18100      * @event blur
18101      * @hide
18102      */
18103     /**
18104      * @event change
18105      * @hide
18106      */
18107     /**
18108      * @event focus
18109      * @hide
18110      */
18111     /**
18112      * @event specialkey
18113      * @hide
18114      */
18115     /**
18116      * @cfg {String} fieldClass @hide
18117      */
18118     /**
18119      * @cfg {String} focusClass @hide
18120      */
18121     /**
18122      * @cfg {String} autoCreate @hide
18123      */
18124     /**
18125      * @cfg {String} inputType @hide
18126      */
18127     /**
18128      * @cfg {String} invalidClass @hide
18129      */
18130     /**
18131      * @cfg {String} invalidText @hide
18132      */
18133     /**
18134      * @cfg {String} msgFx @hide
18135      */
18136     /**
18137      * @cfg {String} validateOnBlur @hide
18138      */
18139 });
18140  
18141     
18142    
18143    
18144    
18145       
18146 Roo.namespace('Roo.bootstrap.htmleditor');
18147 /**
18148  * @class Roo.bootstrap.HtmlEditorToolbar1
18149  * Basic Toolbar
18150  * 
18151  * Usage:
18152  *
18153  new Roo.bootstrap.HtmlEditor({
18154     ....
18155     toolbars : [
18156         new Roo.bootstrap.HtmlEditorToolbar1({
18157             disable : { fonts: 1 , format: 1, ..., ... , ...],
18158             btns : [ .... ]
18159         })
18160     }
18161      
18162  * 
18163  * @cfg {Object} disable List of elements to disable..
18164  * @cfg {Array} btns List of additional buttons.
18165  * 
18166  * 
18167  * NEEDS Extra CSS? 
18168  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18169  */
18170  
18171 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18172 {
18173     
18174     Roo.apply(this, config);
18175     
18176     // default disabled, based on 'good practice'..
18177     this.disable = this.disable || {};
18178     Roo.applyIf(this.disable, {
18179         fontSize : true,
18180         colors : true,
18181         specialElements : true
18182     });
18183     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18184     
18185     this.editor = config.editor;
18186     this.editorcore = config.editor.editorcore;
18187     
18188     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18189     
18190     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18191     // dont call parent... till later.
18192 }
18193 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18194      
18195     bar : true,
18196     
18197     editor : false,
18198     editorcore : false,
18199     
18200     
18201     formats : [
18202         "p" ,  
18203         "h1","h2","h3","h4","h5","h6", 
18204         "pre", "code", 
18205         "abbr", "acronym", "address", "cite", "samp", "var",
18206         'div','span'
18207     ],
18208     
18209     onRender : function(ct, position)
18210     {
18211        // Roo.log("Call onRender: " + this.xtype);
18212         
18213        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18214        Roo.log(this.el);
18215        this.el.dom.style.marginBottom = '0';
18216        var _this = this;
18217        var editorcore = this.editorcore;
18218        var editor= this.editor;
18219        
18220        var children = [];
18221        var btn = function(id,cmd , toggle, handler){
18222        
18223             var  event = toggle ? 'toggle' : 'click';
18224        
18225             var a = {
18226                 size : 'sm',
18227                 xtype: 'Button',
18228                 xns: Roo.bootstrap,
18229                 glyphicon : id,
18230                 cmd : id || cmd,
18231                 enableToggle:toggle !== false,
18232                 //html : 'submit'
18233                 pressed : toggle ? false : null,
18234                 listeners : {}
18235             }
18236             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18237                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18238             }
18239             children.push(a);
18240             return a;
18241        }
18242         
18243         var style = {
18244                 xtype: 'Button',
18245                 size : 'sm',
18246                 xns: Roo.bootstrap,
18247                 glyphicon : 'font',
18248                 //html : 'submit'
18249                 menu : {
18250                     xtype: 'Menu',
18251                     xns: Roo.bootstrap,
18252                     items:  []
18253                 }
18254         };
18255         Roo.each(this.formats, function(f) {
18256             style.menu.items.push({
18257                 xtype :'MenuItem',
18258                 xns: Roo.bootstrap,
18259                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18260                 tagname : f,
18261                 listeners : {
18262                     click : function()
18263                     {
18264                         editorcore.insertTag(this.tagname);
18265                         editor.focus();
18266                     }
18267                 }
18268                 
18269             });
18270         });
18271          children.push(style);   
18272             
18273             
18274         btn('bold',false,true);
18275         btn('italic',false,true);
18276         btn('align-left', 'justifyleft',true);
18277         btn('align-center', 'justifycenter',true);
18278         btn('align-right' , 'justifyright',true);
18279         btn('link', false, false, function(btn) {
18280             //Roo.log("create link?");
18281             var url = prompt(this.createLinkText, this.defaultLinkValue);
18282             if(url && url != 'http:/'+'/'){
18283                 this.editorcore.relayCmd('createlink', url);
18284             }
18285         }),
18286         btn('list','insertunorderedlist',true);
18287         btn('pencil', false,true, function(btn){
18288                 Roo.log(this);
18289                 
18290                 this.toggleSourceEdit(btn.pressed);
18291         });
18292         /*
18293         var cog = {
18294                 xtype: 'Button',
18295                 size : 'sm',
18296                 xns: Roo.bootstrap,
18297                 glyphicon : 'cog',
18298                 //html : 'submit'
18299                 menu : {
18300                     xtype: 'Menu',
18301                     xns: Roo.bootstrap,
18302                     items:  []
18303                 }
18304         };
18305         
18306         cog.menu.items.push({
18307             xtype :'MenuItem',
18308             xns: Roo.bootstrap,
18309             html : Clean styles,
18310             tagname : f,
18311             listeners : {
18312                 click : function()
18313                 {
18314                     editorcore.insertTag(this.tagname);
18315                     editor.focus();
18316                 }
18317             }
18318             
18319         });
18320        */
18321         
18322          
18323        this.xtype = 'NavSimplebar';
18324         
18325         for(var i=0;i< children.length;i++) {
18326             
18327             this.buttons.add(this.addxtypeChild(children[i]));
18328             
18329         }
18330         
18331         editor.on('editorevent', this.updateToolbar, this);
18332     },
18333     onBtnClick : function(id)
18334     {
18335        this.editorcore.relayCmd(id);
18336        this.editorcore.focus();
18337     },
18338     
18339     /**
18340      * Protected method that will not generally be called directly. It triggers
18341      * a toolbar update by reading the markup state of the current selection in the editor.
18342      */
18343     updateToolbar: function(){
18344
18345         if(!this.editorcore.activated){
18346             this.editor.onFirstFocus(); // is this neeed?
18347             return;
18348         }
18349
18350         var btns = this.buttons; 
18351         var doc = this.editorcore.doc;
18352         btns.get('bold').setActive(doc.queryCommandState('bold'));
18353         btns.get('italic').setActive(doc.queryCommandState('italic'));
18354         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18355         
18356         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18357         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18358         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18359         
18360         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18361         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18362          /*
18363         
18364         var ans = this.editorcore.getAllAncestors();
18365         if (this.formatCombo) {
18366             
18367             
18368             var store = this.formatCombo.store;
18369             this.formatCombo.setValue("");
18370             for (var i =0; i < ans.length;i++) {
18371                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18372                     // select it..
18373                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18374                     break;
18375                 }
18376             }
18377         }
18378         
18379         
18380         
18381         // hides menus... - so this cant be on a menu...
18382         Roo.bootstrap.MenuMgr.hideAll();
18383         */
18384         Roo.bootstrap.MenuMgr.hideAll();
18385         //this.editorsyncValue();
18386     },
18387     onFirstFocus: function() {
18388         this.buttons.each(function(item){
18389            item.enable();
18390         });
18391     },
18392     toggleSourceEdit : function(sourceEditMode){
18393         
18394           
18395         if(sourceEditMode){
18396             Roo.log("disabling buttons");
18397            this.buttons.each( function(item){
18398                 if(item.cmd != 'pencil'){
18399                     item.disable();
18400                 }
18401             });
18402           
18403         }else{
18404             Roo.log("enabling buttons");
18405             if(this.editorcore.initialized){
18406                 this.buttons.each( function(item){
18407                     item.enable();
18408                 });
18409             }
18410             
18411         }
18412         Roo.log("calling toggole on editor");
18413         // tell the editor that it's been pressed..
18414         this.editor.toggleSourceEdit(sourceEditMode);
18415        
18416     }
18417 });
18418
18419
18420
18421
18422
18423 /**
18424  * @class Roo.bootstrap.Table.AbstractSelectionModel
18425  * @extends Roo.util.Observable
18426  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18427  * implemented by descendant classes.  This class should not be directly instantiated.
18428  * @constructor
18429  */
18430 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18431     this.locked = false;
18432     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18433 };
18434
18435
18436 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18437     /** @ignore Called by the grid automatically. Do not call directly. */
18438     init : function(grid){
18439         this.grid = grid;
18440         this.initEvents();
18441     },
18442
18443     /**
18444      * Locks the selections.
18445      */
18446     lock : function(){
18447         this.locked = true;
18448     },
18449
18450     /**
18451      * Unlocks the selections.
18452      */
18453     unlock : function(){
18454         this.locked = false;
18455     },
18456
18457     /**
18458      * Returns true if the selections are locked.
18459      * @return {Boolean}
18460      */
18461     isLocked : function(){
18462         return this.locked;
18463     }
18464 });
18465 /**
18466  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18467  * @class Roo.bootstrap.Table.RowSelectionModel
18468  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18469  * It supports multiple selections and keyboard selection/navigation. 
18470  * @constructor
18471  * @param {Object} config
18472  */
18473
18474 Roo.bootstrap.Table.RowSelectionModel = function(config){
18475     Roo.apply(this, config);
18476     this.selections = new Roo.util.MixedCollection(false, function(o){
18477         return o.id;
18478     });
18479
18480     this.last = false;
18481     this.lastActive = false;
18482
18483     this.addEvents({
18484         /**
18485              * @event selectionchange
18486              * Fires when the selection changes
18487              * @param {SelectionModel} this
18488              */
18489             "selectionchange" : true,
18490         /**
18491              * @event afterselectionchange
18492              * Fires after the selection changes (eg. by key press or clicking)
18493              * @param {SelectionModel} this
18494              */
18495             "afterselectionchange" : true,
18496         /**
18497              * @event beforerowselect
18498              * Fires when a row is selected being selected, return false to cancel.
18499              * @param {SelectionModel} this
18500              * @param {Number} rowIndex The selected index
18501              * @param {Boolean} keepExisting False if other selections will be cleared
18502              */
18503             "beforerowselect" : true,
18504         /**
18505              * @event rowselect
18506              * Fires when a row is selected.
18507              * @param {SelectionModel} this
18508              * @param {Number} rowIndex The selected index
18509              * @param {Roo.data.Record} r The record
18510              */
18511             "rowselect" : true,
18512         /**
18513              * @event rowdeselect
18514              * Fires when a row is deselected.
18515              * @param {SelectionModel} this
18516              * @param {Number} rowIndex The selected index
18517              */
18518         "rowdeselect" : true
18519     });
18520     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18521     this.locked = false;
18522 };
18523
18524 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18525     /**
18526      * @cfg {Boolean} singleSelect
18527      * True to allow selection of only one row at a time (defaults to false)
18528      */
18529     singleSelect : false,
18530
18531     // private
18532     initEvents : function(){
18533
18534         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18535             this.grid.on("mousedown", this.handleMouseDown, this);
18536         }else{ // allow click to work like normal
18537             this.grid.on("rowclick", this.handleDragableRowClick, this);
18538         }
18539
18540         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18541             "up" : function(e){
18542                 if(!e.shiftKey){
18543                     this.selectPrevious(e.shiftKey);
18544                 }else if(this.last !== false && this.lastActive !== false){
18545                     var last = this.last;
18546                     this.selectRange(this.last,  this.lastActive-1);
18547                     this.grid.getView().focusRow(this.lastActive);
18548                     if(last !== false){
18549                         this.last = last;
18550                     }
18551                 }else{
18552                     this.selectFirstRow();
18553                 }
18554                 this.fireEvent("afterselectionchange", this);
18555             },
18556             "down" : function(e){
18557                 if(!e.shiftKey){
18558                     this.selectNext(e.shiftKey);
18559                 }else if(this.last !== false && this.lastActive !== false){
18560                     var last = this.last;
18561                     this.selectRange(this.last,  this.lastActive+1);
18562                     this.grid.getView().focusRow(this.lastActive);
18563                     if(last !== false){
18564                         this.last = last;
18565                     }
18566                 }else{
18567                     this.selectFirstRow();
18568                 }
18569                 this.fireEvent("afterselectionchange", this);
18570             },
18571             scope: this
18572         });
18573
18574         var view = this.grid.view;
18575         view.on("refresh", this.onRefresh, this);
18576         view.on("rowupdated", this.onRowUpdated, this);
18577         view.on("rowremoved", this.onRemove, this);
18578     },
18579
18580     // private
18581     onRefresh : function(){
18582         var ds = this.grid.dataSource, i, v = this.grid.view;
18583         var s = this.selections;
18584         s.each(function(r){
18585             if((i = ds.indexOfId(r.id)) != -1){
18586                 v.onRowSelect(i);
18587             }else{
18588                 s.remove(r);
18589             }
18590         });
18591     },
18592
18593     // private
18594     onRemove : function(v, index, r){
18595         this.selections.remove(r);
18596     },
18597
18598     // private
18599     onRowUpdated : function(v, index, r){
18600         if(this.isSelected(r)){
18601             v.onRowSelect(index);
18602         }
18603     },
18604
18605     /**
18606      * Select records.
18607      * @param {Array} records The records to select
18608      * @param {Boolean} keepExisting (optional) True to keep existing selections
18609      */
18610     selectRecords : function(records, keepExisting){
18611         if(!keepExisting){
18612             this.clearSelections();
18613         }
18614         var ds = this.grid.dataSource;
18615         for(var i = 0, len = records.length; i < len; i++){
18616             this.selectRow(ds.indexOf(records[i]), true);
18617         }
18618     },
18619
18620     /**
18621      * Gets the number of selected rows.
18622      * @return {Number}
18623      */
18624     getCount : function(){
18625         return this.selections.length;
18626     },
18627
18628     /**
18629      * Selects the first row in the grid.
18630      */
18631     selectFirstRow : function(){
18632         this.selectRow(0);
18633     },
18634
18635     /**
18636      * Select the last row.
18637      * @param {Boolean} keepExisting (optional) True to keep existing selections
18638      */
18639     selectLastRow : function(keepExisting){
18640         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18641     },
18642
18643     /**
18644      * Selects the row immediately following the last selected row.
18645      * @param {Boolean} keepExisting (optional) True to keep existing selections
18646      */
18647     selectNext : function(keepExisting){
18648         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18649             this.selectRow(this.last+1, keepExisting);
18650             this.grid.getView().focusRow(this.last);
18651         }
18652     },
18653
18654     /**
18655      * Selects the row that precedes the last selected row.
18656      * @param {Boolean} keepExisting (optional) True to keep existing selections
18657      */
18658     selectPrevious : function(keepExisting){
18659         if(this.last){
18660             this.selectRow(this.last-1, keepExisting);
18661             this.grid.getView().focusRow(this.last);
18662         }
18663     },
18664
18665     /**
18666      * Returns the selected records
18667      * @return {Array} Array of selected records
18668      */
18669     getSelections : function(){
18670         return [].concat(this.selections.items);
18671     },
18672
18673     /**
18674      * Returns the first selected record.
18675      * @return {Record}
18676      */
18677     getSelected : function(){
18678         return this.selections.itemAt(0);
18679     },
18680
18681
18682     /**
18683      * Clears all selections.
18684      */
18685     clearSelections : function(fast){
18686         if(this.locked) return;
18687         if(fast !== true){
18688             var ds = this.grid.dataSource;
18689             var s = this.selections;
18690             s.each(function(r){
18691                 this.deselectRow(ds.indexOfId(r.id));
18692             }, this);
18693             s.clear();
18694         }else{
18695             this.selections.clear();
18696         }
18697         this.last = false;
18698     },
18699
18700
18701     /**
18702      * Selects all rows.
18703      */
18704     selectAll : function(){
18705         if(this.locked) return;
18706         this.selections.clear();
18707         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18708             this.selectRow(i, true);
18709         }
18710     },
18711
18712     /**
18713      * Returns True if there is a selection.
18714      * @return {Boolean}
18715      */
18716     hasSelection : function(){
18717         return this.selections.length > 0;
18718     },
18719
18720     /**
18721      * Returns True if the specified row is selected.
18722      * @param {Number/Record} record The record or index of the record to check
18723      * @return {Boolean}
18724      */
18725     isSelected : function(index){
18726         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18727         return (r && this.selections.key(r.id) ? true : false);
18728     },
18729
18730     /**
18731      * Returns True if the specified record id is selected.
18732      * @param {String} id The id of record to check
18733      * @return {Boolean}
18734      */
18735     isIdSelected : function(id){
18736         return (this.selections.key(id) ? true : false);
18737     },
18738
18739     // private
18740     handleMouseDown : function(e, t){
18741         var view = this.grid.getView(), rowIndex;
18742         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18743             return;
18744         };
18745         if(e.shiftKey && this.last !== false){
18746             var last = this.last;
18747             this.selectRange(last, rowIndex, e.ctrlKey);
18748             this.last = last; // reset the last
18749             view.focusRow(rowIndex);
18750         }else{
18751             var isSelected = this.isSelected(rowIndex);
18752             if(e.button !== 0 && isSelected){
18753                 view.focusRow(rowIndex);
18754             }else if(e.ctrlKey && isSelected){
18755                 this.deselectRow(rowIndex);
18756             }else if(!isSelected){
18757                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18758                 view.focusRow(rowIndex);
18759             }
18760         }
18761         this.fireEvent("afterselectionchange", this);
18762     },
18763     // private
18764     handleDragableRowClick :  function(grid, rowIndex, e) 
18765     {
18766         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18767             this.selectRow(rowIndex, false);
18768             grid.view.focusRow(rowIndex);
18769              this.fireEvent("afterselectionchange", this);
18770         }
18771     },
18772     
18773     /**
18774      * Selects multiple rows.
18775      * @param {Array} rows Array of the indexes of the row to select
18776      * @param {Boolean} keepExisting (optional) True to keep existing selections
18777      */
18778     selectRows : function(rows, keepExisting){
18779         if(!keepExisting){
18780             this.clearSelections();
18781         }
18782         for(var i = 0, len = rows.length; i < len; i++){
18783             this.selectRow(rows[i], true);
18784         }
18785     },
18786
18787     /**
18788      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18789      * @param {Number} startRow The index of the first row in the range
18790      * @param {Number} endRow The index of the last row in the range
18791      * @param {Boolean} keepExisting (optional) True to retain existing selections
18792      */
18793     selectRange : function(startRow, endRow, keepExisting){
18794         if(this.locked) return;
18795         if(!keepExisting){
18796             this.clearSelections();
18797         }
18798         if(startRow <= endRow){
18799             for(var i = startRow; i <= endRow; i++){
18800                 this.selectRow(i, true);
18801             }
18802         }else{
18803             for(var i = startRow; i >= endRow; i--){
18804                 this.selectRow(i, true);
18805             }
18806         }
18807     },
18808
18809     /**
18810      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18811      * @param {Number} startRow The index of the first row in the range
18812      * @param {Number} endRow The index of the last row in the range
18813      */
18814     deselectRange : function(startRow, endRow, preventViewNotify){
18815         if(this.locked) return;
18816         for(var i = startRow; i <= endRow; i++){
18817             this.deselectRow(i, preventViewNotify);
18818         }
18819     },
18820
18821     /**
18822      * Selects a row.
18823      * @param {Number} row The index of the row to select
18824      * @param {Boolean} keepExisting (optional) True to keep existing selections
18825      */
18826     selectRow : function(index, keepExisting, preventViewNotify){
18827         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18828         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18829             if(!keepExisting || this.singleSelect){
18830                 this.clearSelections();
18831             }
18832             var r = this.grid.dataSource.getAt(index);
18833             this.selections.add(r);
18834             this.last = this.lastActive = index;
18835             if(!preventViewNotify){
18836                 this.grid.getView().onRowSelect(index);
18837             }
18838             this.fireEvent("rowselect", this, index, r);
18839             this.fireEvent("selectionchange", this);
18840         }
18841     },
18842
18843     /**
18844      * Deselects a row.
18845      * @param {Number} row The index of the row to deselect
18846      */
18847     deselectRow : function(index, preventViewNotify){
18848         if(this.locked) return;
18849         if(this.last == index){
18850             this.last = false;
18851         }
18852         if(this.lastActive == index){
18853             this.lastActive = false;
18854         }
18855         var r = this.grid.dataSource.getAt(index);
18856         this.selections.remove(r);
18857         if(!preventViewNotify){
18858             this.grid.getView().onRowDeselect(index);
18859         }
18860         this.fireEvent("rowdeselect", this, index);
18861         this.fireEvent("selectionchange", this);
18862     },
18863
18864     // private
18865     restoreLast : function(){
18866         if(this._last){
18867             this.last = this._last;
18868         }
18869     },
18870
18871     // private
18872     acceptsNav : function(row, col, cm){
18873         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18874     },
18875
18876     // private
18877     onEditorKey : function(field, e){
18878         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18879         if(k == e.TAB){
18880             e.stopEvent();
18881             ed.completeEdit();
18882             if(e.shiftKey){
18883                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18884             }else{
18885                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18886             }
18887         }else if(k == e.ENTER && !e.ctrlKey){
18888             e.stopEvent();
18889             ed.completeEdit();
18890             if(e.shiftKey){
18891                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18892             }else{
18893                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18894             }
18895         }else if(k == e.ESC){
18896             ed.cancelEdit();
18897         }
18898         if(newCell){
18899             g.startEditing(newCell[0], newCell[1]);
18900         }
18901     }
18902 });/*
18903  * Based on:
18904  * Ext JS Library 1.1.1
18905  * Copyright(c) 2006-2007, Ext JS, LLC.
18906  *
18907  * Originally Released Under LGPL - original licence link has changed is not relivant.
18908  *
18909  * Fork - LGPL
18910  * <script type="text/javascript">
18911  */
18912  
18913 /**
18914  * @class Roo.bootstrap.PagingToolbar
18915  * @extends Roo.Row
18916  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18917  * @constructor
18918  * Create a new PagingToolbar
18919  * @param {Object} config The config object
18920  */
18921 Roo.bootstrap.PagingToolbar = function(config)
18922 {
18923     // old args format still supported... - xtype is prefered..
18924         // created from xtype...
18925     var ds = config.dataSource;
18926     this.toolbarItems = [];
18927     if (config.items) {
18928         this.toolbarItems = config.items;
18929 //        config.items = [];
18930     }
18931     
18932     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18933     this.ds = ds;
18934     this.cursor = 0;
18935     if (ds) { 
18936         this.bind(ds);
18937     }
18938     
18939     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18940     
18941 };
18942
18943 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18944     /**
18945      * @cfg {Roo.data.Store} dataSource
18946      * The underlying data store providing the paged data
18947      */
18948     /**
18949      * @cfg {String/HTMLElement/Element} container
18950      * container The id or element that will contain the toolbar
18951      */
18952     /**
18953      * @cfg {Boolean} displayInfo
18954      * True to display the displayMsg (defaults to false)
18955      */
18956     /**
18957      * @cfg {Number} pageSize
18958      * The number of records to display per page (defaults to 20)
18959      */
18960     pageSize: 20,
18961     /**
18962      * @cfg {String} displayMsg
18963      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18964      */
18965     displayMsg : 'Displaying {0} - {1} of {2}',
18966     /**
18967      * @cfg {String} emptyMsg
18968      * The message to display when no records are found (defaults to "No data to display")
18969      */
18970     emptyMsg : 'No data to display',
18971     /**
18972      * Customizable piece of the default paging text (defaults to "Page")
18973      * @type String
18974      */
18975     beforePageText : "Page",
18976     /**
18977      * Customizable piece of the default paging text (defaults to "of %0")
18978      * @type String
18979      */
18980     afterPageText : "of {0}",
18981     /**
18982      * Customizable piece of the default paging text (defaults to "First Page")
18983      * @type String
18984      */
18985     firstText : "First Page",
18986     /**
18987      * Customizable piece of the default paging text (defaults to "Previous Page")
18988      * @type String
18989      */
18990     prevText : "Previous Page",
18991     /**
18992      * Customizable piece of the default paging text (defaults to "Next Page")
18993      * @type String
18994      */
18995     nextText : "Next Page",
18996     /**
18997      * Customizable piece of the default paging text (defaults to "Last Page")
18998      * @type String
18999      */
19000     lastText : "Last Page",
19001     /**
19002      * Customizable piece of the default paging text (defaults to "Refresh")
19003      * @type String
19004      */
19005     refreshText : "Refresh",
19006
19007     buttons : false,
19008     // private
19009     onRender : function(ct, position) 
19010     {
19011         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19012         this.navgroup.parentId = this.id;
19013         this.navgroup.onRender(this.el, null);
19014         // add the buttons to the navgroup
19015         
19016         if(this.displayInfo){
19017             Roo.log(this.el.select('ul.navbar-nav',true).first());
19018             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19019             this.displayEl = this.el.select('.x-paging-info', true).first();
19020 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19021 //            this.displayEl = navel.el.select('span',true).first();
19022         }
19023         
19024         var _this = this;
19025         
19026         if(this.buttons){
19027             Roo.each(_this.buttons, function(e){
19028                Roo.factory(e).onRender(_this.el, null);
19029             });
19030         }
19031             
19032         Roo.each(_this.toolbarItems, function(e) {
19033             _this.navgroup.addItem(e);
19034         });
19035         
19036         this.first = this.navgroup.addItem({
19037             tooltip: this.firstText,
19038             cls: "prev",
19039             icon : 'fa fa-backward',
19040             disabled: true,
19041             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19042         });
19043         
19044         this.prev =  this.navgroup.addItem({
19045             tooltip: this.prevText,
19046             cls: "prev",
19047             icon : 'fa fa-step-backward',
19048             disabled: true,
19049             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19050         });
19051     //this.addSeparator();
19052         
19053         
19054         var field = this.navgroup.addItem( {
19055             tagtype : 'span',
19056             cls : 'x-paging-position',
19057             
19058             html : this.beforePageText  +
19059                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19060                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19061          } ); //?? escaped?
19062         
19063         this.field = field.el.select('input', true).first();
19064         this.field.on("keydown", this.onPagingKeydown, this);
19065         this.field.on("focus", function(){this.dom.select();});
19066     
19067     
19068         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19069         //this.field.setHeight(18);
19070         //this.addSeparator();
19071         this.next = this.navgroup.addItem({
19072             tooltip: this.nextText,
19073             cls: "next",
19074             html : ' <i class="fa fa-step-forward">',
19075             disabled: true,
19076             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19077         });
19078         this.last = this.navgroup.addItem({
19079             tooltip: this.lastText,
19080             icon : 'fa fa-forward',
19081             cls: "next",
19082             disabled: true,
19083             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19084         });
19085     //this.addSeparator();
19086         this.loading = this.navgroup.addItem({
19087             tooltip: this.refreshText,
19088             icon: 'fa fa-refresh',
19089             
19090             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19091         });
19092
19093     },
19094
19095     // private
19096     updateInfo : function(){
19097         if(this.displayEl){
19098             var count = this.ds.getCount();
19099             var msg = count == 0 ?
19100                 this.emptyMsg :
19101                 String.format(
19102                     this.displayMsg,
19103                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19104                 );
19105             this.displayEl.update(msg);
19106         }
19107     },
19108
19109     // private
19110     onLoad : function(ds, r, o){
19111        this.cursor = o.params ? o.params.start : 0;
19112        var d = this.getPageData(),
19113             ap = d.activePage,
19114             ps = d.pages;
19115         
19116        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19117        this.field.dom.value = ap;
19118        this.first.setDisabled(ap == 1);
19119        this.prev.setDisabled(ap == 1);
19120        this.next.setDisabled(ap == ps);
19121        this.last.setDisabled(ap == ps);
19122        this.loading.enable();
19123        this.updateInfo();
19124     },
19125
19126     // private
19127     getPageData : function(){
19128         var total = this.ds.getTotalCount();
19129         return {
19130             total : total,
19131             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19132             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19133         };
19134     },
19135
19136     // private
19137     onLoadError : function(){
19138         this.loading.enable();
19139     },
19140
19141     // private
19142     onPagingKeydown : function(e){
19143         var k = e.getKey();
19144         var d = this.getPageData();
19145         if(k == e.RETURN){
19146             var v = this.field.dom.value, pageNum;
19147             if(!v || isNaN(pageNum = parseInt(v, 10))){
19148                 this.field.dom.value = d.activePage;
19149                 return;
19150             }
19151             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19152             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19153             e.stopEvent();
19154         }
19155         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))
19156         {
19157           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19158           this.field.dom.value = pageNum;
19159           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19160           e.stopEvent();
19161         }
19162         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19163         {
19164           var v = this.field.dom.value, pageNum; 
19165           var increment = (e.shiftKey) ? 10 : 1;
19166           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19167             increment *= -1;
19168           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19169             this.field.dom.value = d.activePage;
19170             return;
19171           }
19172           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19173           {
19174             this.field.dom.value = parseInt(v, 10) + increment;
19175             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19176             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19177           }
19178           e.stopEvent();
19179         }
19180     },
19181
19182     // private
19183     beforeLoad : function(){
19184         if(this.loading){
19185             this.loading.disable();
19186         }
19187     },
19188
19189     // private
19190     onClick : function(which){
19191         var ds = this.ds;
19192         if (!ds) {
19193             return;
19194         }
19195         switch(which){
19196             case "first":
19197                 ds.load({params:{start: 0, limit: this.pageSize}});
19198             break;
19199             case "prev":
19200                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19201             break;
19202             case "next":
19203                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19204             break;
19205             case "last":
19206                 var total = ds.getTotalCount();
19207                 var extra = total % this.pageSize;
19208                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19209                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19210             break;
19211             case "refresh":
19212                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19213             break;
19214         }
19215     },
19216
19217     /**
19218      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19219      * @param {Roo.data.Store} store The data store to unbind
19220      */
19221     unbind : function(ds){
19222         ds.un("beforeload", this.beforeLoad, this);
19223         ds.un("load", this.onLoad, this);
19224         ds.un("loadexception", this.onLoadError, this);
19225         ds.un("remove", this.updateInfo, this);
19226         ds.un("add", this.updateInfo, this);
19227         this.ds = undefined;
19228     },
19229
19230     /**
19231      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19232      * @param {Roo.data.Store} store The data store to bind
19233      */
19234     bind : function(ds){
19235         ds.on("beforeload", this.beforeLoad, this);
19236         ds.on("load", this.onLoad, this);
19237         ds.on("loadexception", this.onLoadError, this);
19238         ds.on("remove", this.updateInfo, this);
19239         ds.on("add", this.updateInfo, this);
19240         this.ds = ds;
19241     }
19242 });/*
19243  * - LGPL
19244  *
19245  * element
19246  * 
19247  */
19248
19249 /**
19250  * @class Roo.bootstrap.MessageBar
19251  * @extends Roo.bootstrap.Component
19252  * Bootstrap MessageBar class
19253  * @cfg {String} html contents of the MessageBar
19254  * @cfg {String} weight (info | success | warning | danger) default info
19255  * @cfg {String} beforeClass insert the bar before the given class
19256  * @cfg {Boolean} closable (true | false) default false
19257  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19258  * 
19259  * @constructor
19260  * Create a new Element
19261  * @param {Object} config The config object
19262  */
19263
19264 Roo.bootstrap.MessageBar = function(config){
19265     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19266 };
19267
19268 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19269     
19270     html: '',
19271     weight: 'info',
19272     closable: false,
19273     fixed: false,
19274     beforeClass: 'bootstrap-sticky-wrap',
19275     
19276     getAutoCreate : function(){
19277         
19278         var cfg = {
19279             tag: 'div',
19280             cls: 'alert alert-dismissable alert-' + this.weight,
19281             cn: [
19282                 {
19283                     tag: 'span',
19284                     cls: 'message',
19285                     html: this.html || ''
19286                 }
19287             ]
19288         }
19289         
19290         if(this.fixed){
19291             cfg.cls += ' alert-messages-fixed';
19292         }
19293         
19294         if(this.closable){
19295             cfg.cn.push({
19296                 tag: 'button',
19297                 cls: 'close',
19298                 html: 'x'
19299             });
19300         }
19301         
19302         return cfg;
19303     },
19304     
19305     onRender : function(ct, position)
19306     {
19307         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19308         
19309         if(!this.el){
19310             var cfg = Roo.apply({},  this.getAutoCreate());
19311             cfg.id = Roo.id();
19312             
19313             if (this.cls) {
19314                 cfg.cls += ' ' + this.cls;
19315             }
19316             if (this.style) {
19317                 cfg.style = this.style;
19318             }
19319             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19320             
19321             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19322         }
19323         
19324         this.el.select('>button.close').on('click', this.hide, this);
19325         
19326     },
19327     
19328     show : function()
19329     {
19330         if (!this.rendered) {
19331             this.render();
19332         }
19333         
19334         this.el.show();
19335         
19336         this.fireEvent('show', this);
19337         
19338     },
19339     
19340     hide : function()
19341     {
19342         if (!this.rendered) {
19343             this.render();
19344         }
19345         
19346         this.el.hide();
19347         
19348         this.fireEvent('hide', this);
19349     },
19350     
19351     update : function()
19352     {
19353 //        var e = this.el.dom.firstChild;
19354 //        
19355 //        if(this.closable){
19356 //            e = e.nextSibling;
19357 //        }
19358 //        
19359 //        e.data = this.html || '';
19360
19361         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19362     }
19363    
19364 });
19365
19366  
19367
19368      /*
19369  * - LGPL
19370  *
19371  * Graph
19372  * 
19373  */
19374
19375
19376 /**
19377  * @class Roo.bootstrap.Graph
19378  * @extends Roo.bootstrap.Component
19379  * Bootstrap Graph class
19380 > Prameters
19381  -sm {number} sm 4
19382  -md {number} md 5
19383  @cfg {String} graphtype  bar | vbar | pie
19384  @cfg {number} g_x coodinator | centre x (pie)
19385  @cfg {number} g_y coodinator | centre y (pie)
19386  @cfg {number} g_r radius (pie)
19387  @cfg {number} g_height height of the chart (respected by all elements in the set)
19388  @cfg {number} g_width width of the chart (respected by all elements in the set)
19389  @cfg {Object} title The title of the chart
19390     
19391  -{Array}  values
19392  -opts (object) options for the chart 
19393      o {
19394      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19395      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19396      o vgutter (number)
19397      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.
19398      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19399      o to
19400      o stretch (boolean)
19401      o }
19402  -opts (object) options for the pie
19403      o{
19404      o cut
19405      o startAngle (number)
19406      o endAngle (number)
19407      } 
19408  *
19409  * @constructor
19410  * Create a new Input
19411  * @param {Object} config The config object
19412  */
19413
19414 Roo.bootstrap.Graph = function(config){
19415     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19416     
19417     this.addEvents({
19418         // img events
19419         /**
19420          * @event click
19421          * The img click event for the img.
19422          * @param {Roo.EventObject} e
19423          */
19424         "click" : true
19425     });
19426 };
19427
19428 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19429     
19430     sm: 4,
19431     md: 5,
19432     graphtype: 'bar',
19433     g_height: 250,
19434     g_width: 400,
19435     g_x: 50,
19436     g_y: 50,
19437     g_r: 30,
19438     opts:{
19439         //g_colors: this.colors,
19440         g_type: 'soft',
19441         g_gutter: '20%'
19442
19443     },
19444     title : false,
19445
19446     getAutoCreate : function(){
19447         
19448         var cfg = {
19449             tag: 'div',
19450             html : null
19451         }
19452         
19453         
19454         return  cfg;
19455     },
19456
19457     onRender : function(ct,position){
19458         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19459         this.raphael = Raphael(this.el.dom);
19460         
19461                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19462                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19463                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19464                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19465                 /*
19466                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19467                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19468                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19469                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19470                 
19471                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19472                 r.barchart(330, 10, 300, 220, data1);
19473                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19474                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19475                 */
19476                 
19477                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19478                 // r.barchart(30, 30, 560, 250,  xdata, {
19479                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19480                 //     axis : "0 0 1 1",
19481                 //     axisxlabels :  xdata
19482                 //     //yvalues : cols,
19483                    
19484                 // });
19485 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19486 //        
19487 //        this.load(null,xdata,{
19488 //                axis : "0 0 1 1",
19489 //                axisxlabels :  xdata
19490 //                });
19491
19492     },
19493
19494     load : function(graphtype,xdata,opts){
19495         this.raphael.clear();
19496         if(!graphtype) {
19497             graphtype = this.graphtype;
19498         }
19499         if(!opts){
19500             opts = this.opts;
19501         }
19502         var r = this.raphael,
19503             fin = function () {
19504                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19505             },
19506             fout = function () {
19507                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19508             },
19509             pfin = function() {
19510                 this.sector.stop();
19511                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19512
19513                 if (this.label) {
19514                     this.label[0].stop();
19515                     this.label[0].attr({ r: 7.5 });
19516                     this.label[1].attr({ "font-weight": 800 });
19517                 }
19518             },
19519             pfout = function() {
19520                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19521
19522                 if (this.label) {
19523                     this.label[0].animate({ r: 5 }, 500, "bounce");
19524                     this.label[1].attr({ "font-weight": 400 });
19525                 }
19526             };
19527
19528         switch(graphtype){
19529             case 'bar':
19530                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19531                 break;
19532             case 'hbar':
19533                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19534                 break;
19535             case 'pie':
19536 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19537 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19538 //            
19539                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19540                 
19541                 break;
19542
19543         }
19544         
19545         if(this.title){
19546             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19547         }
19548         
19549     },
19550     
19551     setTitle: function(o)
19552     {
19553         this.title = o;
19554     },
19555     
19556     initEvents: function() {
19557         
19558         if(!this.href){
19559             this.el.on('click', this.onClick, this);
19560         }
19561     },
19562     
19563     onClick : function(e)
19564     {
19565         Roo.log('img onclick');
19566         this.fireEvent('click', this, e);
19567     }
19568    
19569 });
19570
19571  
19572 /*
19573  * - LGPL
19574  *
19575  * numberBox
19576  * 
19577  */
19578 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19579
19580 /**
19581  * @class Roo.bootstrap.dash.NumberBox
19582  * @extends Roo.bootstrap.Component
19583  * Bootstrap NumberBox class
19584  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19585  * @cfg {String} headline Box headline
19586  * @cfg {String} content Box content
19587  * @cfg {String} icon Box icon
19588  * @cfg {String} footer Footer text
19589  * @cfg {String} fhref Footer href
19590  * 
19591  * @constructor
19592  * Create a new NumberBox
19593  * @param {Object} config The config object
19594  */
19595
19596
19597 Roo.bootstrap.dash.NumberBox = function(config){
19598     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19599     
19600 };
19601
19602 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19603     
19604     bgcolor : 'aqua',
19605     headline : '',
19606     content : '',
19607     icon : '',
19608     footer : '',
19609     fhref : '',
19610     ficon : '',
19611     
19612     getAutoCreate : function(){
19613         
19614         var cfg = {
19615             tag : 'div',
19616             cls : 'small-box bg-' + this.bgcolor,
19617             cn : [
19618                 {
19619                     tag : 'div',
19620                     cls : 'inner',
19621                     cn :[
19622                         {
19623                             tag : 'h3',
19624                             cls : 'roo-headline',
19625                             html : this.headline
19626                         },
19627                         {
19628                             tag : 'p',
19629                             cls : 'roo-content',
19630                             html : this.content
19631                         }
19632                     ]
19633                 }
19634             ]
19635         }
19636         
19637         if(this.icon){
19638             cfg.cn.push({
19639                 tag : 'div',
19640                 cls : 'icon',
19641                 cn :[
19642                     {
19643                         tag : 'i',
19644                         cls : 'ion ' + this.icon
19645                     }
19646                 ]
19647             });
19648         }
19649         
19650         if(this.footer){
19651             var footer = {
19652                 tag : 'a',
19653                 cls : 'small-box-footer',
19654                 href : this.fhref || '#',
19655                 html : this.footer
19656             };
19657             
19658             cfg.cn.push(footer);
19659             
19660         }
19661         
19662         return  cfg;
19663     },
19664
19665     onRender : function(ct,position){
19666         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19667
19668
19669        
19670                 
19671     },
19672
19673     setHeadline: function (value)
19674     {
19675         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19676     },
19677     
19678     setFooter: function (value, href)
19679     {
19680         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19681         
19682         if(href){
19683             this.el.select('a.small-box-footer',true).first().attr('href', href);
19684         }
19685         
19686     },
19687
19688     setContent: function (value)
19689     {
19690         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19691     },
19692
19693     initEvents: function() 
19694     {   
19695         
19696     }
19697     
19698 });
19699
19700  
19701 /*
19702  * - LGPL
19703  *
19704  * TabBox
19705  * 
19706  */
19707 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19708
19709 /**
19710  * @class Roo.bootstrap.dash.TabBox
19711  * @extends Roo.bootstrap.Component
19712  * Bootstrap TabBox class
19713  * @cfg {String} title Title of the TabBox
19714  * @cfg {String} icon Icon of the TabBox
19715  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19716  * 
19717  * @constructor
19718  * Create a new TabBox
19719  * @param {Object} config The config object
19720  */
19721
19722
19723 Roo.bootstrap.dash.TabBox = function(config){
19724     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19725     this.addEvents({
19726         // raw events
19727         /**
19728          * @event addpane
19729          * When a pane is added
19730          * @param {Roo.bootstrap.dash.TabPane} pane
19731          */
19732         "addpane" : true
19733          
19734     });
19735 };
19736
19737 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19738
19739     title : '',
19740     icon : false,
19741     showtabs : true,
19742     
19743     getChildContainer : function()
19744     {
19745         return this.el.select('.tab-content', true).first();
19746     },
19747     
19748     getAutoCreate : function(){
19749         
19750         var header = {
19751             tag: 'li',
19752             cls: 'pull-left header',
19753             html: this.title,
19754             cn : []
19755         };
19756         
19757         if(this.icon){
19758             header.cn.push({
19759                 tag: 'i',
19760                 cls: 'fa ' + this.icon
19761             });
19762         }
19763         
19764         
19765         var cfg = {
19766             tag: 'div',
19767             cls: 'nav-tabs-custom',
19768             cn: [
19769                 {
19770                     tag: 'ul',
19771                     cls: 'nav nav-tabs pull-right',
19772                     cn: [
19773                         header
19774                     ]
19775                 },
19776                 {
19777                     tag: 'div',
19778                     cls: 'tab-content no-padding',
19779                     cn: []
19780                 }
19781             ]
19782         }
19783
19784         return  cfg;
19785     },
19786     initEvents : function()
19787     {
19788         //Roo.log('add add pane handler');
19789         this.on('addpane', this.onAddPane, this);
19790     },
19791      /**
19792      * Updates the box title
19793      * @param {String} html to set the title to.
19794      */
19795     setTitle : function(value)
19796     {
19797         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19798     },
19799     onAddPane : function(pane)
19800     {
19801         //Roo.log('addpane');
19802         //Roo.log(pane);
19803         // tabs are rendere left to right..
19804         if(!this.showtabs){
19805             return;
19806         }
19807         
19808         var ctr = this.el.select('.nav-tabs', true).first();
19809          
19810          
19811         var existing = ctr.select('.nav-tab',true);
19812         var qty = existing.getCount();;
19813         
19814         
19815         var tab = ctr.createChild({
19816             tag : 'li',
19817             cls : 'nav-tab' + (qty ? '' : ' active'),
19818             cn : [
19819                 {
19820                     tag : 'a',
19821                     href:'#',
19822                     html : pane.title
19823                 }
19824             ]
19825         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19826         pane.tab = tab;
19827         
19828         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19829         if (!qty) {
19830             pane.el.addClass('active');
19831         }
19832         
19833                 
19834     },
19835     onTabClick : function(ev,un,ob,pane)
19836     {
19837         //Roo.log('tab - prev default');
19838         ev.preventDefault();
19839         
19840         
19841         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19842         pane.tab.addClass('active');
19843         //Roo.log(pane.title);
19844         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19845         // technically we should have a deactivate event.. but maybe add later.
19846         // and it should not de-activate the selected tab...
19847         
19848         pane.el.addClass('active');
19849         pane.fireEvent('activate');
19850         
19851         
19852     }
19853     
19854     
19855 });
19856
19857  
19858 /*
19859  * - LGPL
19860  *
19861  * Tab pane
19862  * 
19863  */
19864 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19865 /**
19866  * @class Roo.bootstrap.TabPane
19867  * @extends Roo.bootstrap.Component
19868  * Bootstrap TabPane class
19869  * @cfg {Boolean} active (false | true) Default false
19870  * @cfg {String} title title of panel
19871
19872  * 
19873  * @constructor
19874  * Create a new TabPane
19875  * @param {Object} config The config object
19876  */
19877
19878 Roo.bootstrap.dash.TabPane = function(config){
19879     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19880     
19881     this.addEvents({
19882         // raw events
19883         /**
19884          * @event activate
19885          * When a pane is activated
19886          * @param {Roo.bootstrap.dash.TabPane} pane
19887          */
19888         "activate" : true
19889          
19890     });
19891 };
19892
19893 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19894     
19895     active : false,
19896     title : '',
19897     
19898     // the tabBox that this is attached to.
19899     tab : false,
19900      
19901     getAutoCreate : function() 
19902     {
19903         var cfg = {
19904             tag: 'div',
19905             cls: 'tab-pane'
19906         }
19907         
19908         if(this.active){
19909             cfg.cls += ' active';
19910         }
19911         
19912         return cfg;
19913     },
19914     initEvents  : function()
19915     {
19916         //Roo.log('trigger add pane handler');
19917         this.parent().fireEvent('addpane', this)
19918     },
19919     
19920      /**
19921      * Updates the tab title 
19922      * @param {String} html to set the title to.
19923      */
19924     setTitle: function(str)
19925     {
19926         if (!this.tab) {
19927             return;
19928         }
19929         this.title = str;
19930         this.tab.select('a', true).first().dom.innerHTML = str;
19931         
19932     }
19933     
19934     
19935     
19936 });
19937
19938  
19939
19940
19941  /*
19942  * - LGPL
19943  *
19944  * menu
19945  * 
19946  */
19947 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19948
19949 /**
19950  * @class Roo.bootstrap.menu.Menu
19951  * @extends Roo.bootstrap.Component
19952  * Bootstrap Menu class - container for Menu
19953  * @cfg {String} html Text of the menu
19954  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19955  * @cfg {String} icon Font awesome icon
19956  * @cfg {String} pos Menu align to (top | bottom) default bottom
19957  * 
19958  * 
19959  * @constructor
19960  * Create a new Menu
19961  * @param {Object} config The config object
19962  */
19963
19964
19965 Roo.bootstrap.menu.Menu = function(config){
19966     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19967     
19968     this.addEvents({
19969         /**
19970          * @event beforeshow
19971          * Fires before this menu is displayed
19972          * @param {Roo.bootstrap.menu.Menu} this
19973          */
19974         beforeshow : true,
19975         /**
19976          * @event beforehide
19977          * Fires before this menu is hidden
19978          * @param {Roo.bootstrap.menu.Menu} this
19979          */
19980         beforehide : true,
19981         /**
19982          * @event show
19983          * Fires after this menu is displayed
19984          * @param {Roo.bootstrap.menu.Menu} this
19985          */
19986         show : true,
19987         /**
19988          * @event hide
19989          * Fires after this menu is hidden
19990          * @param {Roo.bootstrap.menu.Menu} this
19991          */
19992         hide : true,
19993         /**
19994          * @event click
19995          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19996          * @param {Roo.bootstrap.menu.Menu} this
19997          * @param {Roo.EventObject} e
19998          */
19999         click : true
20000     });
20001     
20002 };
20003
20004 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20005     
20006     submenu : false,
20007     html : '',
20008     weight : 'default',
20009     icon : false,
20010     pos : 'bottom',
20011     
20012     
20013     getChildContainer : function() {
20014         if(this.isSubMenu){
20015             return this.el;
20016         }
20017         
20018         return this.el.select('ul.dropdown-menu', true).first();  
20019     },
20020     
20021     getAutoCreate : function()
20022     {
20023         var text = [
20024             {
20025                 tag : 'span',
20026                 cls : 'roo-menu-text',
20027                 html : this.html
20028             }
20029         ];
20030         
20031         if(this.icon){
20032             text.unshift({
20033                 tag : 'i',
20034                 cls : 'fa ' + this.icon
20035             })
20036         }
20037         
20038         
20039         var cfg = {
20040             tag : 'div',
20041             cls : 'btn-group',
20042             cn : [
20043                 {
20044                     tag : 'button',
20045                     cls : 'dropdown-button btn btn-' + this.weight,
20046                     cn : text
20047                 },
20048                 {
20049                     tag : 'button',
20050                     cls : 'dropdown-toggle btn btn-' + this.weight,
20051                     cn : [
20052                         {
20053                             tag : 'span',
20054                             cls : 'caret'
20055                         }
20056                     ]
20057                 },
20058                 {
20059                     tag : 'ul',
20060                     cls : 'dropdown-menu'
20061                 }
20062             ]
20063             
20064         };
20065         
20066         if(this.pos == 'top'){
20067             cfg.cls += ' dropup';
20068         }
20069         
20070         if(this.isSubMenu){
20071             cfg = {
20072                 tag : 'ul',
20073                 cls : 'dropdown-menu'
20074             }
20075         }
20076         
20077         return cfg;
20078     },
20079     
20080     onRender : function(ct, position)
20081     {
20082         this.isSubMenu = ct.hasClass('dropdown-submenu');
20083         
20084         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20085     },
20086     
20087     initEvents : function() 
20088     {
20089         if(this.isSubMenu){
20090             return;
20091         }
20092         
20093         this.hidden = true;
20094         
20095         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20096         this.triggerEl.on('click', this.onTriggerPress, this);
20097         
20098         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20099         this.buttonEl.on('click', this.onClick, this);
20100         
20101     },
20102     
20103     list : function()
20104     {
20105         if(this.isSubMenu){
20106             return this.el;
20107         }
20108         
20109         return this.el.select('ul.dropdown-menu', true).first();
20110     },
20111     
20112     onClick : function(e)
20113     {
20114         this.fireEvent("click", this, e);
20115     },
20116     
20117     onTriggerPress  : function(e)
20118     {   
20119         if (this.isVisible()) {
20120             this.hide();
20121         } else {
20122             this.show();
20123         }
20124     },
20125     
20126     isVisible : function(){
20127         return !this.hidden;
20128     },
20129     
20130     show : function()
20131     {
20132         this.fireEvent("beforeshow", this);
20133         
20134         this.hidden = false;
20135         this.el.addClass('open');
20136         
20137         Roo.get(document).on("mouseup", this.onMouseUp, this);
20138         
20139         this.fireEvent("show", this);
20140         
20141         
20142     },
20143     
20144     hide : function()
20145     {
20146         this.fireEvent("beforehide", this);
20147         
20148         this.hidden = true;
20149         this.el.removeClass('open');
20150         
20151         Roo.get(document).un("mouseup", this.onMouseUp);
20152         
20153         this.fireEvent("hide", this);
20154     },
20155     
20156     onMouseUp : function()
20157     {
20158         this.hide();
20159     }
20160     
20161 });
20162
20163  
20164  /*
20165  * - LGPL
20166  *
20167  * menu item
20168  * 
20169  */
20170 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20171
20172 /**
20173  * @class Roo.bootstrap.menu.Item
20174  * @extends Roo.bootstrap.Component
20175  * Bootstrap MenuItem class
20176  * @cfg {Boolean} submenu (true | false) default false
20177  * @cfg {String} html text of the item
20178  * @cfg {String} href the link
20179  * @cfg {Boolean} disable (true | false) default false
20180  * @cfg {Boolean} preventDefault (true | false) default true
20181  * @cfg {String} icon Font awesome icon
20182  * @cfg {String} pos Submenu align to (left | right) default right 
20183  * 
20184  * 
20185  * @constructor
20186  * Create a new Item
20187  * @param {Object} config The config object
20188  */
20189
20190
20191 Roo.bootstrap.menu.Item = function(config){
20192     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20193     this.addEvents({
20194         /**
20195          * @event mouseover
20196          * Fires when the mouse is hovering over this menu
20197          * @param {Roo.bootstrap.menu.Item} this
20198          * @param {Roo.EventObject} e
20199          */
20200         mouseover : true,
20201         /**
20202          * @event mouseout
20203          * Fires when the mouse exits this menu
20204          * @param {Roo.bootstrap.menu.Item} this
20205          * @param {Roo.EventObject} e
20206          */
20207         mouseout : true,
20208         // raw events
20209         /**
20210          * @event click
20211          * The raw click event for the entire grid.
20212          * @param {Roo.EventObject} e
20213          */
20214         click : true
20215     });
20216 };
20217
20218 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20219     
20220     submenu : false,
20221     href : '',
20222     html : '',
20223     preventDefault: true,
20224     disable : false,
20225     icon : false,
20226     pos : 'right',
20227     
20228     getAutoCreate : function()
20229     {
20230         var text = [
20231             {
20232                 tag : 'span',
20233                 cls : 'roo-menu-item-text',
20234                 html : this.html
20235             }
20236         ];
20237         
20238         if(this.icon){
20239             text.unshift({
20240                 tag : 'i',
20241                 cls : 'fa ' + this.icon
20242             })
20243         }
20244         
20245         var cfg = {
20246             tag : 'li',
20247             cn : [
20248                 {
20249                     tag : 'a',
20250                     href : this.href || '#',
20251                     cn : text
20252                 }
20253             ]
20254         };
20255         
20256         if(this.disable){
20257             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20258         }
20259         
20260         if(this.submenu){
20261             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20262             
20263             if(this.pos == 'left'){
20264                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20265             }
20266         }
20267         
20268         return cfg;
20269     },
20270     
20271     initEvents : function() 
20272     {
20273         this.el.on('mouseover', this.onMouseOver, this);
20274         this.el.on('mouseout', this.onMouseOut, this);
20275         
20276         this.el.select('a', true).first().on('click', this.onClick, this);
20277         
20278     },
20279     
20280     onClick : function(e)
20281     {
20282         if(this.preventDefault){
20283             e.preventDefault();
20284         }
20285         
20286         this.fireEvent("click", this, e);
20287     },
20288     
20289     onMouseOver : function(e)
20290     {
20291         if(this.submenu && this.pos == 'left'){
20292             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20293         }
20294         
20295         this.fireEvent("mouseover", this, e);
20296     },
20297     
20298     onMouseOut : function(e)
20299     {
20300         this.fireEvent("mouseout", this, e);
20301     }
20302 });
20303
20304  
20305
20306  /*
20307  * - LGPL
20308  *
20309  * menu separator
20310  * 
20311  */
20312 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20313
20314 /**
20315  * @class Roo.bootstrap.menu.Separator
20316  * @extends Roo.bootstrap.Component
20317  * Bootstrap Separator class
20318  * 
20319  * @constructor
20320  * Create a new Separator
20321  * @param {Object} config The config object
20322  */
20323
20324
20325 Roo.bootstrap.menu.Separator = function(config){
20326     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20327 };
20328
20329 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20330     
20331     getAutoCreate : function(){
20332         var cfg = {
20333             tag : 'li',
20334             cls: 'divider'
20335         };
20336         
20337         return cfg;
20338     }
20339    
20340 });
20341
20342  
20343
20344