Roo/bootstrap/TabPanel.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
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
775  * @cfg {Number} md colspan out of 12 for computer-sized screens
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
777  * @cfg {String} html content of column.
778  * 
779  * @constructor
780  * Create a new Column
781  * @param {Object} config The config object
782  */
783
784 Roo.bootstrap.Column = function(config){
785     Roo.bootstrap.Column.superclass.constructor.call(this, config);
786 };
787
788 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
789     
790     xs: null,
791     sm: null,
792     md: null,
793     lg: null,
794     html: '',
795     offset: 0,
796     
797     getAutoCreate : function(){
798         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
799         
800         cfg = {
801             tag: 'div',
802             cls: 'column'
803         };
804         
805         var settings=this;
806         ['xs','sm','md','lg'].map(function(size){
807             if (settings[size]) {
808                 cfg.cls += ' col-' + size + '-' + settings[size];
809             }
810         });
811         if (this.html.length) {
812             cfg.html = this.html;
813         }
814         
815         return cfg;
816     }
817    
818 });
819
820  
821
822  /*
823  * - LGPL
824  *
825  * page container.
826  * 
827  */
828
829
830 /**
831  * @class Roo.bootstrap.Container
832  * @extends Roo.bootstrap.Component
833  * Bootstrap Container class
834  * @cfg {Boolean} jumbotron is it a jumbotron element
835  * @cfg {String} html content of element
836  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
837  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
838  * @cfg {String} header content of header (for panel)
839  * @cfg {String} footer content of footer (for panel)
840  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
841  * @cfg {String} tag (header|aside|section) type of HTML tag.
842
843  *     
844  * @constructor
845  * Create a new Container
846  * @param {Object} config The config object
847  */
848
849 Roo.bootstrap.Container = function(config){
850     Roo.bootstrap.Container.superclass.constructor.call(this, config);
851 };
852
853 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
854     
855     jumbotron : false,
856     well: '',
857     panel : '',
858     header: '',
859     footer : '',
860     sticky: '',
861     tag : false,
862   
863      
864     getChildContainer : function() {
865         
866         if(!this.el){
867             return false;
868         }
869         
870         if (this.panel.length) {
871             return this.el.select('.panel-body',true).first();
872         }
873         
874         return this.el;
875     },
876     
877     
878     getAutoCreate : function(){
879         
880         var cfg = {
881             tag : this.tag || 'div',
882             html : '',
883             cls : ''
884         };
885         if (this.jumbotron) {
886             cfg.cls = 'jumbotron';
887         }
888         // - this is applied by the parent..
889         //if (this.cls) {
890         //    cfg.cls = this.cls + '';
891         //}
892         
893         if (this.sticky.length) {
894             
895             var bd = Roo.get(document.body);
896             if (!bd.hasClass('bootstrap-sticky')) {
897                 bd.addClass('bootstrap-sticky');
898                 Roo.select('html',true).setStyle('height', '100%');
899             }
900              
901             cfg.cls += 'bootstrap-sticky-' + this.sticky;
902         }
903         
904         
905         if (this.well.length) {
906             switch (this.well) {
907                 case 'lg':
908                 case 'sm':
909                     cfg.cls +=' well well-' +this.well;
910                     break;
911                 default:
912                     cfg.cls +=' well';
913                     break;
914             }
915         }
916         
917         var body = cfg;
918         
919         if (this.panel.length) {
920             cfg.cls += ' panel panel-' + this.panel;
921             cfg.cn = [];
922             if (this.header.length) {
923                 cfg.cn.push({
924                     
925                     cls : 'panel-heading',
926                     cn : [{
927                         tag: 'h3',
928                         cls : 'panel-title',
929                         html : this.header
930                     }]
931                     
932                 });
933             }
934             body = false;
935             cfg.cn.push({
936                 cls : 'panel-body',
937                 html : this.html
938             });
939             
940             
941             if (this.footer.length) {
942                 cfg.cn.push({
943                     cls : 'panel-footer',
944                     html : this.footer
945                     
946                 });
947             }
948             
949         }
950         
951         if (body) {
952             body.html = this.html || cfg.html;
953         }
954         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
955             cfg.cls =  'container';
956         }
957         
958         return cfg;
959     },
960     
961     titleEl : function()
962     {
963         if(!this.el || !this.panel.length || !this.header.length){
964             return;
965         }
966         
967         return this.el.select('.panel-title',true).first();
968     },
969     
970     setTitle : function(v)
971     {
972         var titleEl = this.titleEl();
973         
974         if(!titleEl){
975             return;
976         }
977         
978         titleEl.dom.innerHTML = v;
979     },
980     
981     getTitle : function()
982     {
983         
984         var titleEl = this.titleEl();
985         
986         if(!titleEl){
987             return '';
988         }
989         
990         return titleEl.dom.innerHTML;
991     }
992    
993 });
994
995  /*
996  * - LGPL
997  *
998  * image
999  * 
1000  */
1001
1002
1003 /**
1004  * @class Roo.bootstrap.Img
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Img class
1007  * @cfg {Boolean} imgResponsive false | true
1008  * @cfg {String} border rounded | circle | thumbnail
1009  * @cfg {String} src image source
1010  * @cfg {String} alt image alternative text
1011  * @cfg {String} href a tag href
1012  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1013  * 
1014  * @constructor
1015  * Create a new Input
1016  * @param {Object} config The config object
1017  */
1018
1019 Roo.bootstrap.Img = function(config){
1020     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1021     
1022     this.addEvents({
1023         // img events
1024         /**
1025          * @event click
1026          * The img click event for the img.
1027          * @param {Roo.EventObject} e
1028          */
1029         "click" : true
1030     });
1031 };
1032
1033 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1034     
1035     imgResponsive: true,
1036     border: '',
1037     src: '',
1038     href: false,
1039     target: false,
1040
1041     getAutoCreate : function(){
1042         
1043         var cfg = {
1044             tag: 'img',
1045             cls: (this.imgResponsive) ? 'img-responsive' : '',
1046             html : null
1047         }
1048         
1049         cfg.html = this.html || cfg.html;
1050         
1051         cfg.src = this.src || cfg.src;
1052         
1053         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1054             cfg.cls += ' img-' + this.border;
1055         }
1056         
1057         if(this.alt){
1058             cfg.alt = this.alt;
1059         }
1060         
1061         if(this.href){
1062             var a = {
1063                 tag: 'a',
1064                 href: this.href,
1065                 cn: [
1066                     cfg
1067                 ]
1068             }
1069             
1070             if(this.target){
1071                 a.target = this.target;
1072             }
1073             
1074         }
1075         
1076         
1077         return (this.href) ? a : cfg;
1078     },
1079     
1080     initEvents: function() {
1081         
1082         if(!this.href){
1083             this.el.on('click', this.onClick, this);
1084         }
1085     },
1086     
1087     onClick : function(e)
1088     {
1089         Roo.log('img onclick');
1090         this.fireEvent('click', this, e);
1091     }
1092    
1093 });
1094
1095  /*
1096  * - LGPL
1097  *
1098  * image
1099  * 
1100  */
1101
1102
1103 /**
1104  * @class Roo.bootstrap.Link
1105  * @extends Roo.bootstrap.Component
1106  * Bootstrap Link Class
1107  * @cfg {String} alt image alternative text
1108  * @cfg {String} href a tag href
1109  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1110  * @cfg {String} html the content of the link.
1111  * @cfg {Boolean} preventDefault (true | false) default false
1112
1113  * 
1114  * @constructor
1115  * Create a new Input
1116  * @param {Object} config The config object
1117  */
1118
1119 Roo.bootstrap.Link = function(config){
1120     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1121     
1122     this.addEvents({
1123         // img events
1124         /**
1125          * @event click
1126          * The img click event for the img.
1127          * @param {Roo.EventObject} e
1128          */
1129         "click" : true
1130     });
1131 };
1132
1133 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1134     
1135     href: false,
1136     target: false,
1137     preventDefault: false,
1138
1139     getAutoCreate : function(){
1140         
1141         var cfg = {
1142             tag: 'a',
1143             html : this.html || 'html-missing'
1144         }
1145         
1146         
1147         if(this.alt){
1148             cfg.alt = this.alt;
1149         }
1150         cfg.href = this.href || '#';
1151         if(this.target){
1152             cfg.target = this.target;
1153         }
1154         
1155         return cfg;
1156     },
1157     
1158     initEvents: function() {
1159         
1160         if(!this.href || this.preventDefault){
1161             this.el.on('click', this.onClick, this);
1162         }
1163     },
1164     
1165     onClick : function(e)
1166     {
1167         if(this.preventDefault){
1168             e.preventDefault();
1169         }
1170         //Roo.log('img onclick');
1171         this.fireEvent('click', this, e);
1172     }
1173    
1174 });
1175
1176  /*
1177  * - LGPL
1178  *
1179  * header
1180  * 
1181  */
1182
1183 /**
1184  * @class Roo.bootstrap.Header
1185  * @extends Roo.bootstrap.Component
1186  * Bootstrap Header class
1187  * @cfg {String} html content of header
1188  * @cfg {Number} level (1|2|3|4|5|6) default 1
1189  * 
1190  * @constructor
1191  * Create a new Header
1192  * @param {Object} config The config object
1193  */
1194
1195
1196 Roo.bootstrap.Header  = function(config){
1197     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1198 };
1199
1200 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1201     
1202     //href : false,
1203     html : false,
1204     level : 1,
1205     
1206     
1207     
1208     getAutoCreate : function(){
1209         
1210         var cfg = {
1211             tag: 'h' + (1 *this.level),
1212             html: this.html || 'fill in html'
1213         } ;
1214         
1215         return cfg;
1216     }
1217    
1218 });
1219
1220  
1221
1222  /*
1223  * Based on:
1224  * Ext JS Library 1.1.1
1225  * Copyright(c) 2006-2007, Ext JS, LLC.
1226  *
1227  * Originally Released Under LGPL - original licence link has changed is not relivant.
1228  *
1229  * Fork - LGPL
1230  * <script type="text/javascript">
1231  */
1232  
1233 /**
1234  * @class Roo.bootstrap.MenuMgr
1235  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1236  * @singleton
1237  */
1238 Roo.bootstrap.MenuMgr = function(){
1239    var menus, active, groups = {}, attached = false, lastShow = new Date();
1240
1241    // private - called when first menu is created
1242    function init(){
1243        menus = {};
1244        active = new Roo.util.MixedCollection();
1245        Roo.get(document).addKeyListener(27, function(){
1246            if(active.length > 0){
1247                hideAll();
1248            }
1249        });
1250    }
1251
1252    // private
1253    function hideAll(){
1254        if(active && active.length > 0){
1255            var c = active.clone();
1256            c.each(function(m){
1257                m.hide();
1258            });
1259        }
1260    }
1261
1262    // private
1263    function onHide(m){
1264        active.remove(m);
1265        if(active.length < 1){
1266            Roo.get(document).un("mouseup", onMouseDown);
1267             
1268            attached = false;
1269        }
1270    }
1271
1272    // private
1273    function onShow(m){
1274        var last = active.last();
1275        lastShow = new Date();
1276        active.add(m);
1277        if(!attached){
1278           Roo.get(document).on("mouseup", onMouseDown);
1279            
1280            attached = true;
1281        }
1282        if(m.parentMenu){
1283           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1284           m.parentMenu.activeChild = m;
1285        }else if(last && last.isVisible()){
1286           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1287        }
1288    }
1289
1290    // private
1291    function onBeforeHide(m){
1292        if(m.activeChild){
1293            m.activeChild.hide();
1294        }
1295        if(m.autoHideTimer){
1296            clearTimeout(m.autoHideTimer);
1297            delete m.autoHideTimer;
1298        }
1299    }
1300
1301    // private
1302    function onBeforeShow(m){
1303        var pm = m.parentMenu;
1304        if(!pm && !m.allowOtherMenus){
1305            hideAll();
1306        }else if(pm && pm.activeChild && active != m){
1307            pm.activeChild.hide();
1308        }
1309    }
1310
1311    // private
1312    function onMouseDown(e){
1313         Roo.log("on MouseDown");
1314         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1315            hideAll();
1316         }
1317         
1318         
1319    }
1320
1321    // private
1322    function onBeforeCheck(mi, state){
1323        if(state){
1324            var g = groups[mi.group];
1325            for(var i = 0, l = g.length; i < l; i++){
1326                if(g[i] != mi){
1327                    g[i].setChecked(false);
1328                }
1329            }
1330        }
1331    }
1332
1333    return {
1334
1335        /**
1336         * Hides all menus that are currently visible
1337         */
1338        hideAll : function(){
1339             hideAll();  
1340        },
1341
1342        // private
1343        register : function(menu){
1344            if(!menus){
1345                init();
1346            }
1347            menus[menu.id] = menu;
1348            menu.on("beforehide", onBeforeHide);
1349            menu.on("hide", onHide);
1350            menu.on("beforeshow", onBeforeShow);
1351            menu.on("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                if(!groups[g]){
1355                    groups[g] = [];
1356                }
1357                groups[g].push(menu);
1358                menu.on("checkchange", onCheck);
1359            }
1360        },
1361
1362         /**
1363          * Returns a {@link Roo.menu.Menu} object
1364          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1365          * be used to generate and return a new Menu instance.
1366          */
1367        get : function(menu){
1368            if(typeof menu == "string"){ // menu id
1369                return menus[menu];
1370            }else if(menu.events){  // menu instance
1371                return menu;
1372            }
1373            /*else if(typeof menu.length == 'number'){ // array of menu items?
1374                return new Roo.bootstrap.Menu({items:menu});
1375            }else{ // otherwise, must be a config
1376                return new Roo.bootstrap.Menu(menu);
1377            }
1378            */
1379            return false;
1380        },
1381
1382        // private
1383        unregister : function(menu){
1384            delete menus[menu.id];
1385            menu.un("beforehide", onBeforeHide);
1386            menu.un("hide", onHide);
1387            menu.un("beforeshow", onBeforeShow);
1388            menu.un("show", onShow);
1389            var g = menu.group;
1390            if(g && menu.events["checkchange"]){
1391                groups[g].remove(menu);
1392                menu.un("checkchange", onCheck);
1393            }
1394        },
1395
1396        // private
1397        registerCheckable : function(menuItem){
1398            var g = menuItem.group;
1399            if(g){
1400                if(!groups[g]){
1401                    groups[g] = [];
1402                }
1403                groups[g].push(menuItem);
1404                menuItem.on("beforecheckchange", onBeforeCheck);
1405            }
1406        },
1407
1408        // private
1409        unregisterCheckable : function(menuItem){
1410            var g = menuItem.group;
1411            if(g){
1412                groups[g].remove(menuItem);
1413                menuItem.un("beforecheckchange", onBeforeCheck);
1414            }
1415        }
1416    };
1417 }();/*
1418  * - LGPL
1419  *
1420  * menu
1421  * 
1422  */
1423
1424 /**
1425  * @class Roo.bootstrap.Menu
1426  * @extends Roo.bootstrap.Component
1427  * Bootstrap Menu class - container for MenuItems
1428  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1429  * 
1430  * @constructor
1431  * Create a new Menu
1432  * @param {Object} config The config object
1433  */
1434
1435
1436 Roo.bootstrap.Menu = function(config){
1437     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1438     if (this.registerMenu) {
1439         Roo.bootstrap.MenuMgr.register(this);
1440     }
1441     this.addEvents({
1442         /**
1443          * @event beforeshow
1444          * Fires before this menu is displayed
1445          * @param {Roo.menu.Menu} this
1446          */
1447         beforeshow : true,
1448         /**
1449          * @event beforehide
1450          * Fires before this menu is hidden
1451          * @param {Roo.menu.Menu} this
1452          */
1453         beforehide : true,
1454         /**
1455          * @event show
1456          * Fires after this menu is displayed
1457          * @param {Roo.menu.Menu} this
1458          */
1459         show : true,
1460         /**
1461          * @event hide
1462          * Fires after this menu is hidden
1463          * @param {Roo.menu.Menu} this
1464          */
1465         hide : true,
1466         /**
1467          * @event click
1468          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1469          * @param {Roo.menu.Menu} this
1470          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1471          * @param {Roo.EventObject} e
1472          */
1473         click : true,
1474         /**
1475          * @event mouseover
1476          * Fires when the mouse is hovering over this menu
1477          * @param {Roo.menu.Menu} this
1478          * @param {Roo.EventObject} e
1479          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1480          */
1481         mouseover : true,
1482         /**
1483          * @event mouseout
1484          * Fires when the mouse exits this menu
1485          * @param {Roo.menu.Menu} this
1486          * @param {Roo.EventObject} e
1487          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1488          */
1489         mouseout : true,
1490         /**
1491          * @event itemclick
1492          * Fires when a menu item contained in this menu is clicked
1493          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1494          * @param {Roo.EventObject} e
1495          */
1496         itemclick: true
1497     });
1498     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1499 };
1500
1501 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1502     
1503    /// html : false,
1504     //align : '',
1505     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1506     type: false,
1507     /**
1508      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1509      */
1510     registerMenu : true,
1511     
1512     menuItems :false, // stores the menu items..
1513     
1514     hidden:true,
1515     
1516     parentMenu : false,
1517     
1518     getChildContainer : function() {
1519         return this.el;  
1520     },
1521     
1522     getAutoCreate : function(){
1523          
1524         //if (['right'].indexOf(this.align)!==-1) {
1525         //    cfg.cn[1].cls += ' pull-right'
1526         //}
1527         
1528         
1529         var cfg = {
1530             tag : 'ul',
1531             cls : 'dropdown-menu' ,
1532             style : 'z-index:1000'
1533             
1534         }
1535         
1536         if (this.type === 'submenu') {
1537             cfg.cls = 'submenu active';
1538         }
1539         if (this.type === 'treeview') {
1540             cfg.cls = 'treeview-menu';
1541         }
1542         
1543         return cfg;
1544     },
1545     initEvents : function() {
1546         
1547        // Roo.log("ADD event");
1548        // Roo.log(this.triggerEl.dom);
1549         this.triggerEl.on('click', this.onTriggerPress, this);
1550         this.triggerEl.addClass('dropdown-toggle');
1551         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1552
1553         this.el.on("mouseover", this.onMouseOver, this);
1554         this.el.on("mouseout", this.onMouseOut, this);
1555         
1556         
1557     },
1558     findTargetItem : function(e){
1559         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1560         if(!t){
1561             return false;
1562         }
1563         //Roo.log(t);         Roo.log(t.id);
1564         if(t && t.id){
1565             //Roo.log(this.menuitems);
1566             return this.menuitems.get(t.id);
1567             
1568             //return this.items.get(t.menuItemId);
1569         }
1570         
1571         return false;
1572     },
1573     onClick : function(e){
1574         Roo.log("menu.onClick");
1575         var t = this.findTargetItem(e);
1576         if(!t){
1577             return;
1578         }
1579         Roo.log(e);
1580         /*
1581         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1582             if(t == this.activeItem && t.shouldDeactivate(e)){
1583                 this.activeItem.deactivate();
1584                 delete this.activeItem;
1585                 return;
1586             }
1587             if(t.canActivate){
1588                 this.setActiveItem(t, true);
1589             }
1590             return;
1591             
1592             
1593         }
1594         */
1595         Roo.log('pass click event');
1596         
1597         t.onClick(e);
1598         
1599         this.fireEvent("click", this, t, e);
1600         
1601         this.hide();
1602     },
1603      onMouseOver : function(e){
1604         var t  = this.findTargetItem(e);
1605         //Roo.log(t);
1606         //if(t){
1607         //    if(t.canActivate && !t.disabled){
1608         //        this.setActiveItem(t, true);
1609         //    }
1610         //}
1611         
1612         this.fireEvent("mouseover", this, e, t);
1613     },
1614     isVisible : function(){
1615         return !this.hidden;
1616     },
1617      onMouseOut : function(e){
1618         var t  = this.findTargetItem(e);
1619         
1620         //if(t ){
1621         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1622         //        this.activeItem.deactivate();
1623         //        delete this.activeItem;
1624         //    }
1625         //}
1626         this.fireEvent("mouseout", this, e, t);
1627     },
1628     
1629     
1630     /**
1631      * Displays this menu relative to another element
1632      * @param {String/HTMLElement/Roo.Element} element The element to align to
1633      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1634      * the element (defaults to this.defaultAlign)
1635      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1636      */
1637     show : function(el, pos, parentMenu){
1638         this.parentMenu = parentMenu;
1639         if(!this.el){
1640             this.render();
1641         }
1642         this.fireEvent("beforeshow", this);
1643         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1644     },
1645      /**
1646      * Displays this menu at a specific xy position
1647      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1648      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1649      */
1650     showAt : function(xy, parentMenu, /* private: */_e){
1651         this.parentMenu = parentMenu;
1652         if(!this.el){
1653             this.render();
1654         }
1655         if(_e !== false){
1656             this.fireEvent("beforeshow", this);
1657             
1658             //xy = this.el.adjustForConstraints(xy);
1659         }
1660         //this.el.setXY(xy);
1661         //this.el.show();
1662         this.hideMenuItems();
1663         this.hidden = false;
1664         this.triggerEl.addClass('open');
1665         this.focus();
1666         this.fireEvent("show", this);
1667     },
1668     
1669     focus : function(){
1670         return;
1671         if(!this.hidden){
1672             this.doFocus.defer(50, this);
1673         }
1674     },
1675
1676     doFocus : function(){
1677         if(!this.hidden){
1678             this.focusEl.focus();
1679         }
1680     },
1681
1682     /**
1683      * Hides this menu and optionally all parent menus
1684      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1685      */
1686     hide : function(deep){
1687         
1688         this.hideMenuItems();
1689         if(this.el && this.isVisible()){
1690             this.fireEvent("beforehide", this);
1691             if(this.activeItem){
1692                 this.activeItem.deactivate();
1693                 this.activeItem = null;
1694             }
1695             this.triggerEl.removeClass('open');;
1696             this.hidden = true;
1697             this.fireEvent("hide", this);
1698         }
1699         if(deep === true && this.parentMenu){
1700             this.parentMenu.hide(true);
1701         }
1702     },
1703     
1704     onTriggerPress  : function(e)
1705     {
1706         
1707         Roo.log('trigger press');
1708         //Roo.log(e.getTarget());
1709        // Roo.log(this.triggerEl.dom);
1710         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1711             return;
1712         }
1713         if (this.isVisible()) {
1714             Roo.log('hide');
1715             this.hide();
1716         } else {
1717             this.show(this.triggerEl, false, false);
1718         }
1719         
1720         
1721     },
1722     
1723          
1724        
1725     
1726     hideMenuItems : function()
1727     {
1728         //$(backdrop).remove()
1729         Roo.select('.open',true).each(function(aa) {
1730             
1731             aa.removeClass('open');
1732           //var parent = getParent($(this))
1733           //var relatedTarget = { relatedTarget: this }
1734           
1735            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1736           //if (e.isDefaultPrevented()) return
1737            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1738         })
1739     },
1740     addxtypeChild : function (tree, cntr) {
1741         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1742           
1743         this.menuitems.add(comp);
1744         return comp;
1745
1746     },
1747     getEl : function()
1748     {
1749         Roo.log(this.el);
1750         return this.el;
1751     }
1752 });
1753
1754  
1755  /*
1756  * - LGPL
1757  *
1758  * menu item
1759  * 
1760  */
1761
1762
1763 /**
1764  * @class Roo.bootstrap.MenuItem
1765  * @extends Roo.bootstrap.Component
1766  * Bootstrap MenuItem class
1767  * @cfg {String} html the menu label
1768  * @cfg {String} href the link
1769  * @cfg {Boolean} preventDefault (true | false) default true
1770  * 
1771  * 
1772  * @constructor
1773  * Create a new MenuItem
1774  * @param {Object} config The config object
1775  */
1776
1777
1778 Roo.bootstrap.MenuItem = function(config){
1779     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1780     this.addEvents({
1781         // raw events
1782         /**
1783          * @event click
1784          * The raw click event for the entire grid.
1785          * @param {Roo.EventObject} e
1786          */
1787         "click" : true
1788     });
1789 };
1790
1791 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1792     
1793     href : false,
1794     html : false,
1795     preventDefault: true,
1796     
1797     getAutoCreate : function(){
1798         var cfg= {
1799             tag: 'li',
1800             cls: 'dropdown-menu-item',
1801             cn: [
1802                     {
1803                         tag : 'a',
1804                         href : '#',
1805                         html : 'Link'
1806                     }
1807                 ]
1808         };
1809         if (this.parent().type == 'treeview') {
1810             cfg.cls = 'treeview-menu';
1811         }
1812         
1813         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1814         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1815         return cfg;
1816     },
1817     
1818     initEvents: function() {
1819         
1820         //this.el.select('a').on('click', this.onClick, this);
1821         
1822     },
1823     onClick : function(e)
1824     {
1825         Roo.log('item on click ');
1826         //if(this.preventDefault){
1827         //    e.preventDefault();
1828         //}
1829         //this.parent().hideMenuItems();
1830         
1831         this.fireEvent('click', this, e);
1832     },
1833     getEl : function()
1834     {
1835         return this.el;
1836     }
1837 });
1838
1839  
1840
1841  /*
1842  * - LGPL
1843  *
1844  * menu separator
1845  * 
1846  */
1847
1848
1849 /**
1850  * @class Roo.bootstrap.MenuSeparator
1851  * @extends Roo.bootstrap.Component
1852  * Bootstrap MenuSeparator class
1853  * 
1854  * @constructor
1855  * Create a new MenuItem
1856  * @param {Object} config The config object
1857  */
1858
1859
1860 Roo.bootstrap.MenuSeparator = function(config){
1861     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1862 };
1863
1864 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1865     
1866     getAutoCreate : function(){
1867         var cfg = {
1868             cls: 'divider',
1869             tag : 'li'
1870         };
1871         
1872         return cfg;
1873     }
1874    
1875 });
1876
1877  
1878
1879  
1880 /*
1881 <div class="modal fade">
1882   <div class="modal-dialog">
1883     <div class="modal-content">
1884       <div class="modal-header">
1885         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1886         <h4 class="modal-title">Modal title</h4>
1887       </div>
1888       <div class="modal-body">
1889         <p>One fine body&hellip;</p>
1890       </div>
1891       <div class="modal-footer">
1892         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1893         <button type="button" class="btn btn-primary">Save changes</button>
1894       </div>
1895     </div><!-- /.modal-content -->
1896   </div><!-- /.modal-dialog -->
1897 </div><!-- /.modal -->
1898 */
1899 /*
1900  * - LGPL
1901  *
1902  * page contgainer.
1903  * 
1904  */
1905
1906 /**
1907  * @class Roo.bootstrap.Modal
1908  * @extends Roo.bootstrap.Component
1909  * Bootstrap Modal class
1910  * @cfg {String} title Title of dialog
1911  * @cfg {Boolean} specificTitle (true|false) default false
1912  * @cfg {Array} buttons Array of buttons or standard button set..
1913  * @cfg {String} buttonPosition (left|right|center) default right
1914  * 
1915  * @constructor
1916  * Create a new Modal Dialog
1917  * @param {Object} config The config object
1918  */
1919
1920 Roo.bootstrap.Modal = function(config){
1921     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1922     this.addEvents({
1923         // raw events
1924         /**
1925          * @event btnclick
1926          * The raw btnclick event for the button
1927          * @param {Roo.EventObject} e
1928          */
1929         "btnclick" : true
1930     });
1931     this.buttons = this.buttons || [];
1932 };
1933
1934 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1935     
1936     title : 'test dialog',
1937    
1938     buttons : false,
1939     
1940     // set on load...
1941     body:  false,
1942     
1943     specificTitle: false,
1944     
1945     buttonPosition: 'right',
1946     
1947     onRender : function(ct, position)
1948     {
1949         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1950      
1951         if(!this.el){
1952             var cfg = Roo.apply({},  this.getAutoCreate());
1953             cfg.id = Roo.id();
1954             //if(!cfg.name){
1955             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1956             //}
1957             //if (!cfg.name.length) {
1958             //    delete cfg.name;
1959            // }
1960             if (this.cls) {
1961                 cfg.cls += ' ' + this.cls;
1962             }
1963             if (this.style) {
1964                 cfg.style = this.style;
1965             }
1966             this.el = Roo.get(document.body).createChild(cfg, position);
1967         }
1968         //var type = this.el.dom.type;
1969         
1970         if(this.tabIndex !== undefined){
1971             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1972         }
1973         
1974         
1975         
1976         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1977         this.maskEl.enableDisplayMode("block");
1978         this.maskEl.hide();
1979         //this.el.addClass("x-dlg-modal");
1980     
1981         if (this.buttons.length) {
1982             Roo.each(this.buttons, function(bb) {
1983                 b = Roo.apply({}, bb);
1984                 b.xns = b.xns || Roo.bootstrap;
1985                 b.xtype = b.xtype || 'Button';
1986                 if (typeof(b.listeners) == 'undefined') {
1987                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1988                 }
1989                 
1990                 var btn = Roo.factory(b);
1991                 
1992                 btn.onRender(this.el.select('.modal-footer div').first());
1993                 
1994             },this);
1995         }
1996         // render the children.
1997         var nitems = [];
1998         
1999         if(typeof(this.items) != 'undefined'){
2000             var items = this.items;
2001             delete this.items;
2002
2003             for(var i =0;i < items.length;i++) {
2004                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2005             }
2006         }
2007         
2008         this.items = nitems;
2009         
2010         this.body = this.el.select('.modal-body',true).first();
2011         this.close = this.el.select('.modal-header .close', true).first();
2012         this.footer = this.el.select('.modal-footer',true).first();
2013         this.initEvents();
2014         //this.el.addClass([this.fieldClass, this.cls]);
2015         
2016     },
2017     getAutoCreate : function(){
2018         
2019         
2020         var bdy = {
2021                 cls : 'modal-body',
2022                 html : this.html || ''
2023         };
2024         
2025         var title = {
2026             tag: 'h4',
2027             cls : 'modal-title',
2028             html : this.title
2029         };
2030         
2031         if(this.specificTitle){
2032             title = this.title;
2033         };
2034         
2035         return modal = {
2036             cls: "modal fade",
2037             style : 'display: none',
2038             cn : [
2039                 {
2040                     cls: "modal-dialog",
2041                     cn : [
2042                         {
2043                             cls : "modal-content",
2044                             cn : [
2045                                 {
2046                                     cls : 'modal-header',
2047                                     cn : [
2048                                         {
2049                                             tag: 'button',
2050                                             cls : 'close',
2051                                             html : '&times'
2052                                         },
2053                                         title
2054                                     ]
2055                                 },
2056                                 bdy,
2057                                 {
2058                                     cls : 'modal-footer',
2059                                     cn : [
2060                                         {
2061                                             tag: 'div',
2062                                             cls: 'btn-' + this.buttonPosition
2063                                         }
2064                                     ]
2065                                     
2066                                 }
2067                                 
2068                                 
2069                             ]
2070                             
2071                         }
2072                     ]
2073                         
2074                 }
2075             ]
2076             
2077             
2078         };
2079           
2080     },
2081     getChildContainer : function() {
2082          
2083          return this.el.select('.modal-body',true).first();
2084         
2085     },
2086     getButtonContainer : function() {
2087          return this.el.select('.modal-footer div',true).first();
2088         
2089     },
2090     initEvents : function()
2091     {
2092         this.el.select('.modal-header .close').on('click', this.hide, this);
2093 //        
2094 //        this.addxtype(this);
2095     },
2096     show : function() {
2097         
2098         if (!this.rendered) {
2099             this.render();
2100         }
2101        
2102         this.el.addClass('on');
2103         this.el.removeClass('fade');
2104         this.el.setStyle('display', 'block');
2105         Roo.get(document.body).addClass("x-body-masked");
2106         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2107         this.maskEl.show();
2108         this.el.setStyle('zIndex', '10001');
2109         this.fireEvent('show', this);
2110         
2111         
2112     },
2113     hide : function()
2114     {
2115         Roo.log('Modal hide?!');
2116         this.maskEl.hide();
2117         Roo.get(document.body).removeClass("x-body-masked");
2118         this.el.removeClass('on');
2119         this.el.addClass('fade');
2120         this.el.setStyle('display', 'none');
2121         this.fireEvent('hide', this);
2122     },
2123     
2124     addButton : function(str, cb)
2125     {
2126          
2127         
2128         var b = Roo.apply({}, { html : str } );
2129         b.xns = b.xns || Roo.bootstrap;
2130         b.xtype = b.xtype || 'Button';
2131         if (typeof(b.listeners) == 'undefined') {
2132             b.listeners = { click : cb.createDelegate(this)  };
2133         }
2134         
2135         var btn = Roo.factory(b);
2136            
2137         btn.onRender(this.el.select('.modal-footer div').first());
2138         
2139         return btn;   
2140        
2141     },
2142     
2143     setDefaultButton : function(btn)
2144     {
2145         //this.el.select('.modal-footer').()
2146     },
2147     resizeTo: function(w,h)
2148     {
2149         // skip..
2150     },
2151     setContentSize  : function(w, h)
2152     {
2153         
2154     },
2155     onButtonClick: function(btn,e)
2156     {
2157         //Roo.log([a,b,c]);
2158         this.fireEvent('btnclick', btn.name, e);
2159     },
2160     setTitle: function(str) {
2161         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2162         
2163     }
2164 });
2165
2166
2167 Roo.apply(Roo.bootstrap.Modal,  {
2168     /**
2169          * Button config that displays a single OK button
2170          * @type Object
2171          */
2172         OK :  [{
2173             name : 'ok',
2174             weight : 'primary',
2175             html : 'OK'
2176         }], 
2177         /**
2178          * Button config that displays Yes and No buttons
2179          * @type Object
2180          */
2181         YESNO : [
2182             {
2183                 name  : 'no',
2184                 html : 'No'
2185             },
2186             {
2187                 name  :'yes',
2188                 weight : 'primary',
2189                 html : 'Yes'
2190             }
2191         ],
2192         
2193         /**
2194          * Button config that displays OK and Cancel buttons
2195          * @type Object
2196          */
2197         OKCANCEL : [
2198             {
2199                name : 'cancel',
2200                 html : 'Cancel'
2201             },
2202             {
2203                 name : 'ok',
2204                 weight : 'primary',
2205                 html : 'OK'
2206             }
2207         ],
2208         /**
2209          * Button config that displays Yes, No and Cancel buttons
2210          * @type Object
2211          */
2212         YESNOCANCEL : [
2213             {
2214                 name : 'yes',
2215                 weight : 'primary',
2216                 html : 'Yes'
2217             },
2218             {
2219                 name : 'no',
2220                 html : 'No'
2221             },
2222             {
2223                 name : 'cancel',
2224                 html : 'Cancel'
2225             }
2226         ]
2227 });
2228  /*
2229  * - LGPL
2230  *
2231  * messagebox - can be used as a replace
2232  * 
2233  */
2234 /**
2235  * @class Roo.MessageBox
2236  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2237  * Example usage:
2238  *<pre><code>
2239 // Basic alert:
2240 Roo.Msg.alert('Status', 'Changes saved successfully.');
2241
2242 // Prompt for user data:
2243 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2244     if (btn == 'ok'){
2245         // process text value...
2246     }
2247 });
2248
2249 // Show a dialog using config options:
2250 Roo.Msg.show({
2251    title:'Save Changes?',
2252    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2253    buttons: Roo.Msg.YESNOCANCEL,
2254    fn: processResult,
2255    animEl: 'elId'
2256 });
2257 </code></pre>
2258  * @singleton
2259  */
2260 Roo.bootstrap.MessageBox = function(){
2261     var dlg, opt, mask, waitTimer;
2262     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2263     var buttons, activeTextEl, bwidth;
2264
2265     
2266     // private
2267     var handleButton = function(button){
2268         dlg.hide();
2269         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2270     };
2271
2272     // private
2273     var handleHide = function(){
2274         if(opt && opt.cls){
2275             dlg.el.removeClass(opt.cls);
2276         }
2277         //if(waitTimer){
2278         //    Roo.TaskMgr.stop(waitTimer);
2279         //    waitTimer = null;
2280         //}
2281     };
2282
2283     // private
2284     var updateButtons = function(b){
2285         var width = 0;
2286         if(!b){
2287             buttons["ok"].hide();
2288             buttons["cancel"].hide();
2289             buttons["yes"].hide();
2290             buttons["no"].hide();
2291             //dlg.footer.dom.style.display = 'none';
2292             return width;
2293         }
2294         dlg.footer.dom.style.display = '';
2295         for(var k in buttons){
2296             if(typeof buttons[k] != "function"){
2297                 if(b[k]){
2298                     buttons[k].show();
2299                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2300                     width += buttons[k].el.getWidth()+15;
2301                 }else{
2302                     buttons[k].hide();
2303                 }
2304             }
2305         }
2306         return width;
2307     };
2308
2309     // private
2310     var handleEsc = function(d, k, e){
2311         if(opt && opt.closable !== false){
2312             dlg.hide();
2313         }
2314         if(e){
2315             e.stopEvent();
2316         }
2317     };
2318
2319     return {
2320         /**
2321          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2322          * @return {Roo.BasicDialog} The BasicDialog element
2323          */
2324         getDialog : function(){
2325            if(!dlg){
2326                 dlg = new Roo.bootstrap.Modal( {
2327                     //draggable: true,
2328                     //resizable:false,
2329                     //constraintoviewport:false,
2330                     //fixedcenter:true,
2331                     //collapsible : false,
2332                     //shim:true,
2333                     //modal: true,
2334                   //  width:400,
2335                   //  height:100,
2336                     //buttonAlign:"center",
2337                     closeClick : function(){
2338                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2339                             handleButton("no");
2340                         }else{
2341                             handleButton("cancel");
2342                         }
2343                     }
2344                 });
2345                 dlg.render();
2346                 dlg.on("hide", handleHide);
2347                 mask = dlg.mask;
2348                 //dlg.addKeyListener(27, handleEsc);
2349                 buttons = {};
2350                 this.buttons = buttons;
2351                 var bt = this.buttonText;
2352                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2353                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2354                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2355                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2356                 Roo.log(buttons)
2357                 bodyEl = dlg.body.createChild({
2358
2359                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2360                         '<textarea class="roo-mb-textarea"></textarea>' +
2361                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2362                 });
2363                 msgEl = bodyEl.dom.firstChild;
2364                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2365                 textboxEl.enableDisplayMode();
2366                 textboxEl.addKeyListener([10,13], function(){
2367                     if(dlg.isVisible() && opt && opt.buttons){
2368                         if(opt.buttons.ok){
2369                             handleButton("ok");
2370                         }else if(opt.buttons.yes){
2371                             handleButton("yes");
2372                         }
2373                     }
2374                 });
2375                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2376                 textareaEl.enableDisplayMode();
2377                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2378                 progressEl.enableDisplayMode();
2379                 var pf = progressEl.dom.firstChild;
2380                 if (pf) {
2381                     pp = Roo.get(pf.firstChild);
2382                     pp.setHeight(pf.offsetHeight);
2383                 }
2384                 
2385             }
2386             return dlg;
2387         },
2388
2389         /**
2390          * Updates the message box body text
2391          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2392          * the XHTML-compliant non-breaking space character '&amp;#160;')
2393          * @return {Roo.MessageBox} This message box
2394          */
2395         updateText : function(text){
2396             if(!dlg.isVisible() && !opt.width){
2397                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2398             }
2399             msgEl.innerHTML = text || '&#160;';
2400       
2401             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2402             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2403             var w = Math.max(
2404                     Math.min(opt.width || cw , this.maxWidth), 
2405                     Math.max(opt.minWidth || this.minWidth, bwidth)
2406             );
2407             if(opt.prompt){
2408                 activeTextEl.setWidth(w);
2409             }
2410             if(dlg.isVisible()){
2411                 dlg.fixedcenter = false;
2412             }
2413             // to big, make it scroll. = But as usual stupid IE does not support
2414             // !important..
2415             
2416             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2417                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2418                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2419             } else {
2420                 bodyEl.dom.style.height = '';
2421                 bodyEl.dom.style.overflowY = '';
2422             }
2423             if (cw > w) {
2424                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2425             } else {
2426                 bodyEl.dom.style.overflowX = '';
2427             }
2428             
2429             dlg.setContentSize(w, bodyEl.getHeight());
2430             if(dlg.isVisible()){
2431                 dlg.fixedcenter = true;
2432             }
2433             return this;
2434         },
2435
2436         /**
2437          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2438          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2439          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2440          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2441          * @return {Roo.MessageBox} This message box
2442          */
2443         updateProgress : function(value, text){
2444             if(text){
2445                 this.updateText(text);
2446             }
2447             if (pp) { // weird bug on my firefox - for some reason this is not defined
2448                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2449             }
2450             return this;
2451         },        
2452
2453         /**
2454          * Returns true if the message box is currently displayed
2455          * @return {Boolean} True if the message box is visible, else false
2456          */
2457         isVisible : function(){
2458             return dlg && dlg.isVisible();  
2459         },
2460
2461         /**
2462          * Hides the message box if it is displayed
2463          */
2464         hide : function(){
2465             if(this.isVisible()){
2466                 dlg.hide();
2467             }  
2468         },
2469
2470         /**
2471          * Displays a new message box, or reinitializes an existing message box, based on the config options
2472          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2473          * The following config object properties are supported:
2474          * <pre>
2475 Property    Type             Description
2476 ----------  ---------------  ------------------------------------------------------------------------------------
2477 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2478                                    closes (defaults to undefined)
2479 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2480                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2481 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2482                                    progress and wait dialogs will ignore this property and always hide the
2483                                    close button as they can only be closed programmatically.
2484 cls               String           A custom CSS class to apply to the message box element
2485 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2486                                    displayed (defaults to 75)
2487 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2488                                    function will be btn (the name of the button that was clicked, if applicable,
2489                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2490                                    Progress and wait dialogs will ignore this option since they do not respond to
2491                                    user actions and can only be closed programmatically, so any required function
2492                                    should be called by the same code after it closes the dialog.
2493 icon              String           A CSS class that provides a background image to be used as an icon for
2494                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2495 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2496 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2497 modal             Boolean          False to allow user interaction with the page while the message box is
2498                                    displayed (defaults to true)
2499 msg               String           A string that will replace the existing message box body text (defaults
2500                                    to the XHTML-compliant non-breaking space character '&#160;')
2501 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2502 progress          Boolean          True to display a progress bar (defaults to false)
2503 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2504 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2505 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2506 title             String           The title text
2507 value             String           The string value to set into the active textbox element if displayed
2508 wait              Boolean          True to display a progress bar (defaults to false)
2509 width             Number           The width of the dialog in pixels
2510 </pre>
2511          *
2512          * Example usage:
2513          * <pre><code>
2514 Roo.Msg.show({
2515    title: 'Address',
2516    msg: 'Please enter your address:',
2517    width: 300,
2518    buttons: Roo.MessageBox.OKCANCEL,
2519    multiline: true,
2520    fn: saveAddress,
2521    animEl: 'addAddressBtn'
2522 });
2523 </code></pre>
2524          * @param {Object} config Configuration options
2525          * @return {Roo.MessageBox} This message box
2526          */
2527         show : function(options)
2528         {
2529             
2530             // this causes nightmares if you show one dialog after another
2531             // especially on callbacks..
2532              
2533             if(this.isVisible()){
2534                 
2535                 this.hide();
2536                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2537                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2538                 Roo.log("New Dialog Message:" +  options.msg )
2539                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2540                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2541                 
2542             }
2543             var d = this.getDialog();
2544             opt = options;
2545             d.setTitle(opt.title || "&#160;");
2546             d.close.setDisplayed(opt.closable !== false);
2547             activeTextEl = textboxEl;
2548             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2549             if(opt.prompt){
2550                 if(opt.multiline){
2551                     textboxEl.hide();
2552                     textareaEl.show();
2553                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2554                         opt.multiline : this.defaultTextHeight);
2555                     activeTextEl = textareaEl;
2556                 }else{
2557                     textboxEl.show();
2558                     textareaEl.hide();
2559                 }
2560             }else{
2561                 textboxEl.hide();
2562                 textareaEl.hide();
2563             }
2564             progressEl.setDisplayed(opt.progress === true);
2565             this.updateProgress(0);
2566             activeTextEl.dom.value = opt.value || "";
2567             if(opt.prompt){
2568                 dlg.setDefaultButton(activeTextEl);
2569             }else{
2570                 var bs = opt.buttons;
2571                 var db = null;
2572                 if(bs && bs.ok){
2573                     db = buttons["ok"];
2574                 }else if(bs && bs.yes){
2575                     db = buttons["yes"];
2576                 }
2577                 dlg.setDefaultButton(db);
2578             }
2579             bwidth = updateButtons(opt.buttons);
2580             this.updateText(opt.msg);
2581             if(opt.cls){
2582                 d.el.addClass(opt.cls);
2583             }
2584             d.proxyDrag = opt.proxyDrag === true;
2585             d.modal = opt.modal !== false;
2586             d.mask = opt.modal !== false ? mask : false;
2587             if(!d.isVisible()){
2588                 // force it to the end of the z-index stack so it gets a cursor in FF
2589                 document.body.appendChild(dlg.el.dom);
2590                 d.animateTarget = null;
2591                 d.show(options.animEl);
2592             }
2593             return this;
2594         },
2595
2596         /**
2597          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2598          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2599          * and closing the message box when the process is complete.
2600          * @param {String} title The title bar text
2601          * @param {String} msg The message box body text
2602          * @return {Roo.MessageBox} This message box
2603          */
2604         progress : function(title, msg){
2605             this.show({
2606                 title : title,
2607                 msg : msg,
2608                 buttons: false,
2609                 progress:true,
2610                 closable:false,
2611                 minWidth: this.minProgressWidth,
2612                 modal : true
2613             });
2614             return this;
2615         },
2616
2617         /**
2618          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2619          * If a callback function is passed it will be called after the user clicks the button, and the
2620          * id of the button that was clicked will be passed as the only parameter to the callback
2621          * (could also be the top-right close button).
2622          * @param {String} title The title bar text
2623          * @param {String} msg The message box body text
2624          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2625          * @param {Object} scope (optional) The scope of the callback function
2626          * @return {Roo.MessageBox} This message box
2627          */
2628         alert : function(title, msg, fn, scope){
2629             this.show({
2630                 title : title,
2631                 msg : msg,
2632                 buttons: this.OK,
2633                 fn: fn,
2634                 scope : scope,
2635                 modal : true
2636             });
2637             return this;
2638         },
2639
2640         /**
2641          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2642          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2643          * You are responsible for closing the message box when the process is complete.
2644          * @param {String} msg The message box body text
2645          * @param {String} title (optional) The title bar text
2646          * @return {Roo.MessageBox} This message box
2647          */
2648         wait : function(msg, title){
2649             this.show({
2650                 title : title,
2651                 msg : msg,
2652                 buttons: false,
2653                 closable:false,
2654                 progress:true,
2655                 modal:true,
2656                 width:300,
2657                 wait:true
2658             });
2659             waitTimer = Roo.TaskMgr.start({
2660                 run: function(i){
2661                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2662                 },
2663                 interval: 1000
2664             });
2665             return this;
2666         },
2667
2668         /**
2669          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2670          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2671          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2672          * @param {String} title The title bar text
2673          * @param {String} msg The message box body text
2674          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2675          * @param {Object} scope (optional) The scope of the callback function
2676          * @return {Roo.MessageBox} This message box
2677          */
2678         confirm : function(title, msg, fn, scope){
2679             this.show({
2680                 title : title,
2681                 msg : msg,
2682                 buttons: this.YESNO,
2683                 fn: fn,
2684                 scope : scope,
2685                 modal : true
2686             });
2687             return this;
2688         },
2689
2690         /**
2691          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2692          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2693          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2694          * (could also be the top-right close button) and the text that was entered will be passed as the two
2695          * parameters to the callback.
2696          * @param {String} title The title bar text
2697          * @param {String} msg The message box body text
2698          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2699          * @param {Object} scope (optional) The scope of the callback function
2700          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2701          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2702          * @return {Roo.MessageBox} This message box
2703          */
2704         prompt : function(title, msg, fn, scope, multiline){
2705             this.show({
2706                 title : title,
2707                 msg : msg,
2708                 buttons: this.OKCANCEL,
2709                 fn: fn,
2710                 minWidth:250,
2711                 scope : scope,
2712                 prompt:true,
2713                 multiline: multiline,
2714                 modal : true
2715             });
2716             return this;
2717         },
2718
2719         /**
2720          * Button config that displays a single OK button
2721          * @type Object
2722          */
2723         OK : {ok:true},
2724         /**
2725          * Button config that displays Yes and No buttons
2726          * @type Object
2727          */
2728         YESNO : {yes:true, no:true},
2729         /**
2730          * Button config that displays OK and Cancel buttons
2731          * @type Object
2732          */
2733         OKCANCEL : {ok:true, cancel:true},
2734         /**
2735          * Button config that displays Yes, No and Cancel buttons
2736          * @type Object
2737          */
2738         YESNOCANCEL : {yes:true, no:true, cancel:true},
2739
2740         /**
2741          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2742          * @type Number
2743          */
2744         defaultTextHeight : 75,
2745         /**
2746          * The maximum width in pixels of the message box (defaults to 600)
2747          * @type Number
2748          */
2749         maxWidth : 600,
2750         /**
2751          * The minimum width in pixels of the message box (defaults to 100)
2752          * @type Number
2753          */
2754         minWidth : 100,
2755         /**
2756          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2757          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2758          * @type Number
2759          */
2760         minProgressWidth : 250,
2761         /**
2762          * An object containing the default button text strings that can be overriden for localized language support.
2763          * Supported properties are: ok, cancel, yes and no.
2764          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2765          * @type Object
2766          */
2767         buttonText : {
2768             ok : "OK",
2769             cancel : "Cancel",
2770             yes : "Yes",
2771             no : "No"
2772         }
2773     };
2774 }();
2775
2776 /**
2777  * Shorthand for {@link Roo.MessageBox}
2778  */
2779 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2780 Roo.Msg = Roo.Msg || Roo.MessageBox;
2781 /*
2782  * - LGPL
2783  *
2784  * navbar
2785  * 
2786  */
2787
2788 /**
2789  * @class Roo.bootstrap.Navbar
2790  * @extends Roo.bootstrap.Component
2791  * Bootstrap Navbar class
2792
2793  * @constructor
2794  * Create a new Navbar
2795  * @param {Object} config The config object
2796  */
2797
2798
2799 Roo.bootstrap.Navbar = function(config){
2800     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2801     
2802 };
2803
2804 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2805     
2806     
2807    
2808     // private
2809     navItems : false,
2810     loadMask : false,
2811     
2812     
2813     getAutoCreate : function(){
2814         
2815         
2816         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2817         
2818     },
2819     
2820     initEvents :function ()
2821     {
2822         //Roo.log(this.el.select('.navbar-toggle',true));
2823         this.el.select('.navbar-toggle',true).on('click', function() {
2824            // Roo.log('click');
2825             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2826         }, this);
2827         
2828         var mark = {
2829             tag: "div",
2830             cls:"x-dlg-mask"
2831         }
2832         
2833         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2834         
2835         var size = this.el.getSize();
2836         this.maskEl.setSize(size.width, size.height);
2837         this.maskEl.enableDisplayMode("block");
2838         this.maskEl.hide();
2839         
2840         if(this.loadMask){
2841             this.maskEl.show();
2842         }
2843     },
2844     
2845     
2846     getChildContainer : function()
2847     {
2848         if (this.el.select('.collapse').getCount()) {
2849             return this.el.select('.collapse',true).first();
2850         }
2851         
2852         return this.el;
2853     },
2854     
2855     mask : function()
2856     {
2857         this.maskEl.show();
2858     },
2859     
2860     unmask : function()
2861     {
2862         this.maskEl.hide();
2863     } 
2864     
2865     
2866     
2867     
2868 });
2869
2870
2871
2872  
2873
2874  /*
2875  * - LGPL
2876  *
2877  * navbar
2878  * 
2879  */
2880
2881 /**
2882  * @class Roo.bootstrap.NavSimplebar
2883  * @extends Roo.bootstrap.Navbar
2884  * Bootstrap Sidebar class
2885  *
2886  * @cfg {Boolean} inverse is inverted color
2887  * 
2888  * @cfg {String} type (nav | pills | tabs)
2889  * @cfg {Boolean} arrangement stacked | justified
2890  * @cfg {String} align (left | right) alignment
2891  * 
2892  * @cfg {Boolean} main (true|false) main nav bar? default false
2893  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2894  * 
2895  * @cfg {String} tag (header|footer|nav|div) default is nav 
2896
2897  * 
2898  * 
2899  * 
2900  * @constructor
2901  * Create a new Sidebar
2902  * @param {Object} config The config object
2903  */
2904
2905
2906 Roo.bootstrap.NavSimplebar = function(config){
2907     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2908 };
2909
2910 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2911     
2912     inverse: false,
2913     
2914     type: false,
2915     arrangement: '',
2916     align : false,
2917     
2918     
2919     
2920     main : false,
2921     
2922     
2923     tag : false,
2924     
2925     
2926     getAutoCreate : function(){
2927         
2928         
2929         var cfg = {
2930             tag : this.tag || 'div',
2931             cls : 'navbar'
2932         };
2933           
2934         
2935         cfg.cn = [
2936             {
2937                 cls: 'nav',
2938                 tag : 'ul'
2939             }
2940         ];
2941         
2942          
2943         this.type = this.type || 'nav';
2944         if (['tabs','pills'].indexOf(this.type)!==-1) {
2945             cfg.cn[0].cls += ' nav-' + this.type
2946         
2947         
2948         } else {
2949             if (this.type!=='nav') {
2950                 Roo.log('nav type must be nav/tabs/pills')
2951             }
2952             cfg.cn[0].cls += ' navbar-nav'
2953         }
2954         
2955         
2956         
2957         
2958         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2959             cfg.cn[0].cls += ' nav-' + this.arrangement;
2960         }
2961         
2962         
2963         if (this.align === 'right') {
2964             cfg.cn[0].cls += ' navbar-right';
2965         }
2966         
2967         if (this.inverse) {
2968             cfg.cls += ' navbar-inverse';
2969             
2970         }
2971         
2972         
2973         return cfg;
2974     
2975         
2976     }
2977     
2978     
2979     
2980 });
2981
2982
2983
2984  
2985
2986  
2987        /*
2988  * - LGPL
2989  *
2990  * navbar
2991  * 
2992  */
2993
2994 /**
2995  * @class Roo.bootstrap.NavHeaderbar
2996  * @extends Roo.bootstrap.NavSimplebar
2997  * Bootstrap Sidebar class
2998  *
2999  * @cfg {String} brand what is brand
3000  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3001  * @cfg {String} brand_href href of the brand
3002  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3003  * 
3004  * @constructor
3005  * Create a new Sidebar
3006  * @param {Object} config The config object
3007  */
3008
3009
3010 Roo.bootstrap.NavHeaderbar = function(config){
3011     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3012 };
3013
3014 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3015     
3016     position: '',
3017     brand: '',
3018     brand_href: false,
3019     srButton : true,
3020     
3021     
3022     getAutoCreate : function(){
3023         
3024         var   cfg = {
3025             tag: this.nav || 'nav',
3026             cls: 'navbar',
3027             role: 'navigation',
3028             cn: []
3029         };
3030         
3031         if(this.srButton){
3032             cfg.cn.push({
3033                 tag: 'div',
3034                 cls: 'navbar-header',
3035                 cn: [
3036                     {
3037                         tag: 'button',
3038                         type: 'button',
3039                         cls: 'navbar-toggle',
3040                         'data-toggle': 'collapse',
3041                         cn: [
3042                             {
3043                                 tag: 'span',
3044                                 cls: 'sr-only',
3045                                 html: 'Toggle navigation'
3046                             },
3047                             {
3048                                 tag: 'span',
3049                                 cls: 'icon-bar'
3050                             },
3051                             {
3052                                 tag: 'span',
3053                                 cls: 'icon-bar'
3054                             },
3055                             {
3056                                 tag: 'span',
3057                                 cls: 'icon-bar'
3058                             }
3059                         ]
3060                     }
3061                 ]
3062             });
3063         }
3064         
3065         cfg.cn.push({
3066             tag: 'div',
3067             cls: 'collapse navbar-collapse',
3068             cn : []
3069         });
3070         
3071         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3072         
3073         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3074             cfg.cls += ' navbar-' + this.position;
3075             
3076             // tag can override this..
3077             
3078             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3079         }
3080         
3081         if (this.brand !== '') {
3082             cfg.cn[0].cn.push({
3083                 tag: 'a',
3084                 href: this.brand_href ? this.brand_href : '#',
3085                 cls: 'navbar-brand',
3086                 cn: [
3087                 this.brand
3088                 ]
3089             });
3090         }
3091         
3092         if(this.main){
3093             cfg.cls += ' main-nav';
3094         }
3095         
3096         
3097         return cfg;
3098
3099         
3100     }
3101     
3102     
3103     
3104 });
3105
3106
3107
3108  
3109
3110  /*
3111  * - LGPL
3112  *
3113  * navbar
3114  * 
3115  */
3116
3117 /**
3118  * @class Roo.bootstrap.NavSidebar
3119  * @extends Roo.bootstrap.Navbar
3120  * Bootstrap Sidebar class
3121  * 
3122  * @constructor
3123  * Create a new Sidebar
3124  * @param {Object} config The config object
3125  */
3126
3127
3128 Roo.bootstrap.NavSidebar = function(config){
3129     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3130 };
3131
3132 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3133     
3134     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3135     
3136     getAutoCreate : function(){
3137         
3138         
3139         return  {
3140             tag: 'div',
3141             cls: 'sidebar sidebar-nav'
3142         };
3143     
3144         
3145     }
3146     
3147     
3148     
3149 });
3150
3151
3152
3153  
3154
3155  /*
3156  * - LGPL
3157  *
3158  * nav group
3159  * 
3160  */
3161
3162 /**
3163  * @class Roo.bootstrap.NavGroup
3164  * @extends Roo.bootstrap.Component
3165  * Bootstrap NavGroup class
3166  * @cfg {String} align left | right
3167  * @cfg {Boolean} inverse false | true
3168  * @cfg {String} type (nav|pills|tab) default nav
3169  * @cfg {String} navId - reference Id for navbar.
3170
3171  * 
3172  * @constructor
3173  * Create a new nav group
3174  * @param {Object} config The config object
3175  */
3176
3177 Roo.bootstrap.NavGroup = function(config){
3178     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3179     this.navItems = [];
3180    
3181     Roo.bootstrap.NavGroup.register(this);
3182      this.addEvents({
3183         /**
3184              * @event changed
3185              * Fires when the active item changes
3186              * @param {Roo.bootstrap.NavGroup} this
3187              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3188              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3189          */
3190         'changed': true
3191      });
3192     
3193 };
3194
3195 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3196     
3197     align: '',
3198     inverse: false,
3199     form: false,
3200     type: 'nav',
3201     navId : '',
3202     // private
3203     
3204     navItems : false, 
3205     
3206     getAutoCreate : function()
3207     {
3208         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3209         
3210         cfg = {
3211             tag : 'ul',
3212             cls: 'nav' 
3213         }
3214         
3215         if (['tabs','pills'].indexOf(this.type)!==-1) {
3216             cfg.cls += ' nav-' + this.type
3217         } else {
3218             if (this.type!=='nav') {
3219                 Roo.log('nav type must be nav/tabs/pills')
3220             }
3221             cfg.cls += ' navbar-nav'
3222         }
3223         
3224         if (this.parent().sidebar) {
3225             cfg = {
3226                 tag: 'ul',
3227                 cls: 'dashboard-menu sidebar-menu'
3228             }
3229             
3230             return cfg;
3231         }
3232         
3233         if (this.form === true) {
3234             cfg = {
3235                 tag: 'form',
3236                 cls: 'navbar-form'
3237             }
3238             
3239             if (this.align === 'right') {
3240                 cfg.cls += ' navbar-right';
3241             } else {
3242                 cfg.cls += ' navbar-left';
3243             }
3244         }
3245         
3246         if (this.align === 'right') {
3247             cfg.cls += ' navbar-right';
3248         }
3249         
3250         if (this.inverse) {
3251             cfg.cls += ' navbar-inverse';
3252             
3253         }
3254         
3255         
3256         return cfg;
3257     },
3258     /**
3259     * sets the active Navigation item
3260     * @param {Roo.bootstrap.NavItem} the new current navitem
3261     */
3262     setActiveItem : function(item)
3263     {
3264         var prev = false;
3265         Roo.each(this.navItems, function(v){
3266             if (v == item) {
3267                 return ;
3268             }
3269             if (v.isActive()) {
3270                 v.setActive(false, true);
3271                 prev = v;
3272                 
3273             }
3274             
3275         });
3276
3277         item.setActive(true, true);
3278         this.fireEvent('changed', this, item, prev);
3279         
3280         
3281     },
3282     /**
3283     * gets the active Navigation item
3284     * @return {Roo.bootstrap.NavItem} the current navitem
3285     */
3286     getActive : function()
3287     {
3288         
3289         var prev = false;
3290         Roo.each(this.navItems, function(v){
3291             
3292             if (v.isActive()) {
3293                 prev = v;
3294                 
3295             }
3296             
3297         });
3298         return prev;
3299     },
3300     
3301     indexOfNav : function()
3302     {
3303         
3304         var prev = false;
3305         Roo.each(this.navItems, function(v,i){
3306             
3307             if (v.isActive()) {
3308                 prev = i;
3309                 
3310             }
3311             
3312         });
3313         return prev;
3314     },
3315     /**
3316     * adds a Navigation item
3317     * @param {Roo.bootstrap.NavItem} the navitem to add
3318     */
3319     addItem : function(cfg)
3320     {
3321         var cn = new Roo.bootstrap.NavItem(cfg);
3322         this.register(cn);
3323         cn.parentId = this.id;
3324         cn.onRender(this.el, null);
3325         return cn;
3326     },
3327     /**
3328     * register a Navigation item
3329     * @param {Roo.bootstrap.NavItem} the navitem to add
3330     */
3331     register : function(item)
3332     {
3333         this.navItems.push( item);
3334         item.navId = this.navId;
3335     
3336     },
3337   
3338     
3339     getNavItem: function(tabId)
3340     {
3341         var ret = false;
3342         Roo.each(this.navItems, function(e) {
3343             if (e.tabId == tabId) {
3344                ret =  e;
3345                return false;
3346             }
3347             return true;
3348             
3349         });
3350         return ret;
3351     },
3352     
3353     setActiveNext : function()
3354     {
3355         var i = this.indexOfNav(this.getActive());
3356         if (i > this.navItems.length) {
3357             return;
3358         }
3359         this.setActiveItem(this.navItems[i+1]);
3360     },
3361     setActivePrev : function()
3362     {
3363         var i = this.indexOfNav(this.getActive());
3364         if (i  < 1) {
3365             return;
3366         }
3367         this.setActiveItem(this.navItems[i-1]);
3368     }
3369     
3370     
3371 });
3372
3373  
3374 Roo.apply(Roo.bootstrap.NavGroup, {
3375     
3376     groups: {},
3377      /**
3378     * register a Navigation Group
3379     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3380     */
3381     register : function(navgrp)
3382     {
3383         this.groups[navgrp.navId] = navgrp;
3384         
3385     },
3386     /**
3387     * fetch a Navigation Group based on the navigation ID
3388     * @param {string} the navgroup to add
3389     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3390     */
3391     get: function(navId) {
3392         if (typeof(this.groups[navId]) == 'undefined') {
3393             return false;
3394             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3395         }
3396         return this.groups[navId] ;
3397     }
3398     
3399     
3400     
3401 });
3402
3403  /*
3404  * - LGPL
3405  *
3406  * row
3407  * 
3408  */
3409
3410 /**
3411  * @class Roo.bootstrap.NavItem
3412  * @extends Roo.bootstrap.Component
3413  * Bootstrap Navbar.NavItem class
3414  * @cfg {String} href  link to
3415  * @cfg {String} html content of button
3416  * @cfg {String} badge text inside badge
3417  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3418  * @cfg {String} glyphicon name of glyphicon
3419  * @cfg {String} icon name of font awesome icon
3420  * @cfg {Boolean} active Is item active
3421  * @cfg {Boolean} disabled Is item disabled
3422  
3423  * @cfg {Boolean} preventDefault (true | false) default false
3424  * @cfg {String} tabId the tab that this item activates.
3425  * @cfg {String} tagtype (a|span) render as a href or span?
3426   
3427  * @constructor
3428  * Create a new Navbar Item
3429  * @param {Object} config The config object
3430  */
3431 Roo.bootstrap.NavItem = function(config){
3432     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3433     this.addEvents({
3434         // raw events
3435         /**
3436          * @event click
3437          * The raw click event for the entire grid.
3438          * @param {Roo.EventObject} e
3439          */
3440         "click" : true,
3441          /**
3442             * @event changed
3443             * Fires when the active item active state changes
3444             * @param {Roo.bootstrap.NavItem} this
3445             * @param {boolean} state the new state
3446              
3447          */
3448         'changed': true
3449     });
3450    
3451 };
3452
3453 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3454     
3455     href: false,
3456     html: '',
3457     badge: '',
3458     icon: false,
3459     glyphicon: false,
3460     active: false,
3461     preventDefault : false,
3462     tabId : false,
3463     tagtype : 'a',
3464     disabled : false,
3465     
3466     getAutoCreate : function(){
3467          
3468         var cfg = {
3469             tag: 'li',
3470             cls: 'nav-item'
3471             
3472         }
3473         if (this.active) {
3474             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3475         }
3476         if (this.disabled) {
3477             cfg.cls += ' disabled';
3478         }
3479         
3480         if (this.href || this.html || this.glyphicon || this.icon) {
3481             cfg.cn = [
3482                 {
3483                     tag: this.tagtype,
3484                     href : this.href || "#",
3485                     html: this.html || ''
3486                 }
3487             ];
3488             
3489             if (this.icon) {
3490                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3491             }
3492
3493             if(this.glyphicon) {
3494                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3495             }
3496             
3497             if (this.menu) {
3498                 
3499                 cfg.cn[0].html += " <span class='caret'></span>";
3500              
3501             }
3502             
3503             if (this.badge !== '') {
3504                  
3505                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3506             }
3507         }
3508         
3509         
3510         
3511         return cfg;
3512     },
3513     initEvents: function() {
3514        // Roo.log('init events?');
3515        // Roo.log(this.el.dom);
3516         if (typeof (this.menu) != 'undefined') {
3517             this.menu.parentType = this.xtype;
3518             this.menu.triggerEl = this.el;
3519             this.addxtype(Roo.apply({}, this.menu));
3520         }
3521
3522        
3523         this.el.select('a',true).on('click', this.onClick, this);
3524         // at this point parent should be available..
3525         this.parent().register(this);
3526     },
3527     
3528     onClick : function(e)
3529     {
3530          
3531         if(this.preventDefault){
3532             e.preventDefault();
3533         }
3534         if (this.disabled) {
3535             return;
3536         }
3537         Roo.log("fire event clicked");
3538         if(this.fireEvent('click', this, e) === false){
3539             return;
3540         };
3541         
3542         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3543             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3544                 this.parent().setActiveItem(this);
3545             }
3546             
3547             
3548             
3549         } 
3550     },
3551     
3552     isActive: function () {
3553         return this.active
3554     },
3555     setActive : function(state, fire)
3556     {
3557         this.active = state;
3558         if (!state ) {
3559             this.el.removeClass('active');
3560         } else if (!this.el.hasClass('active')) {
3561             this.el.addClass('active');
3562         }
3563         if (fire) {
3564             this.fireEvent('changed', this, state);
3565         }
3566         
3567         // show a panel if it's registered and related..
3568         
3569         if (!this.navId || !this.tabId || !state) {
3570             return;
3571         }
3572         
3573         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3574         if (!tg) {
3575             return;
3576         }
3577         var pan = tg.getPanelByName(this.tabId);
3578         if (!pan) {
3579             return;
3580         }
3581         tg.showPanel(pan);
3582         
3583         
3584         
3585     },
3586      // this should not be here...
3587     setDisabled : function(state)
3588     {
3589         this.disabled = state;
3590         if (!state ) {
3591             this.el.removeClass('disabled');
3592         } else if (!this.el.hasClass('disabled')) {
3593             this.el.addClass('disabled');
3594         }
3595         
3596     }
3597 });
3598  
3599
3600  /*
3601  * - LGPL
3602  *
3603  * sidebar item
3604  *
3605  *  li
3606  *    <span> icon </span>
3607  *    <span> text </span>
3608  *    <span>badge </span>
3609  */
3610
3611 /**
3612  * @class Roo.bootstrap.NavSidebarItem
3613  * @extends Roo.bootstrap.NavItem
3614  * Bootstrap Navbar.NavSidebarItem class
3615  * @constructor
3616  * Create a new Navbar Button
3617  * @param {Object} config The config object
3618  */
3619 Roo.bootstrap.NavSidebarItem = function(config){
3620     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3621     this.addEvents({
3622         // raw events
3623         /**
3624          * @event click
3625          * The raw click event for the entire grid.
3626          * @param {Roo.EventObject} e
3627          */
3628         "click" : true,
3629          /**
3630             * @event changed
3631             * Fires when the active item active state changes
3632             * @param {Roo.bootstrap.NavSidebarItem} this
3633             * @param {boolean} state the new state
3634              
3635          */
3636         'changed': true
3637     });
3638    
3639 };
3640
3641 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3642     
3643     
3644     getAutoCreate : function(){
3645         
3646         
3647         var a = {
3648                 tag: 'a',
3649                 href : this.href || '#',
3650                 cls: '',
3651                 html : '',
3652                 cn : []
3653         };
3654         var cfg = {
3655             tag: 'li',
3656             cls: '',
3657             cn: [ a ]
3658         }
3659         var span = {
3660             tag: 'span',
3661             html : this.html || ''
3662         }
3663         
3664         
3665         if (this.active) {
3666             cfg.cls += ' active';
3667         }
3668         
3669         // left icon..
3670         if (this.glyphicon || this.icon) {
3671             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3672             a.cn.push({ tag : 'i', cls : c }) ;
3673         }
3674         // html..
3675         a.cn.push(span);
3676         // then badge..
3677         if (this.badge !== '') {
3678             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3679         }
3680         // fi
3681         if (this.menu) {
3682             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3683             a.cls += 'dropdown-toggle treeview' ;
3684             
3685         }
3686         
3687         
3688         
3689         return cfg;
3690          
3691            
3692     }
3693    
3694      
3695  
3696 });
3697  
3698
3699  /*
3700  * - LGPL
3701  *
3702  * row
3703  * 
3704  */
3705
3706 /**
3707  * @class Roo.bootstrap.Row
3708  * @extends Roo.bootstrap.Component
3709  * Bootstrap Row class (contains columns...)
3710  * 
3711  * @constructor
3712  * Create a new Row
3713  * @param {Object} config The config object
3714  */
3715
3716 Roo.bootstrap.Row = function(config){
3717     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3718 };
3719
3720 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3721     
3722     getAutoCreate : function(){
3723        return {
3724             cls: 'row clearfix'
3725        };
3726     }
3727     
3728     
3729 });
3730
3731  
3732
3733  /*
3734  * - LGPL
3735  *
3736  * element
3737  * 
3738  */
3739
3740 /**
3741  * @class Roo.bootstrap.Element
3742  * @extends Roo.bootstrap.Component
3743  * Bootstrap Element class
3744  * @cfg {String} html contents of the element
3745  * @cfg {String} tag tag of the element
3746  * @cfg {String} cls class of the element
3747  * 
3748  * @constructor
3749  * Create a new Element
3750  * @param {Object} config The config object
3751  */
3752
3753 Roo.bootstrap.Element = function(config){
3754     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3755 };
3756
3757 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3758     
3759     tag: 'div',
3760     cls: '',
3761     html: '',
3762      
3763     
3764     getAutoCreate : function(){
3765         
3766         var cfg = {
3767             tag: this.tag,
3768             cls: this.cls,
3769             html: this.html
3770         }
3771         
3772         
3773         
3774         return cfg;
3775     }
3776    
3777 });
3778
3779  
3780
3781  /*
3782  * - LGPL
3783  *
3784  * pagination
3785  * 
3786  */
3787
3788 /**
3789  * @class Roo.bootstrap.Pagination
3790  * @extends Roo.bootstrap.Component
3791  * Bootstrap Pagination class
3792  * @cfg {String} size xs | sm | md | lg
3793  * @cfg {Boolean} inverse false | true
3794  * 
3795  * @constructor
3796  * Create a new Pagination
3797  * @param {Object} config The config object
3798  */
3799
3800 Roo.bootstrap.Pagination = function(config){
3801     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3802 };
3803
3804 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3805     
3806     cls: false,
3807     size: false,
3808     inverse: false,
3809     
3810     getAutoCreate : function(){
3811         var cfg = {
3812             tag: 'ul',
3813                 cls: 'pagination'
3814         };
3815         if (this.inverse) {
3816             cfg.cls += ' inverse';
3817         }
3818         if (this.html) {
3819             cfg.html=this.html;
3820         }
3821         if (this.cls) {
3822             cfg.cls += " " + this.cls;
3823         }
3824         return cfg;
3825     }
3826    
3827 });
3828
3829  
3830
3831  /*
3832  * - LGPL
3833  *
3834  * Pagination item
3835  * 
3836  */
3837
3838
3839 /**
3840  * @class Roo.bootstrap.PaginationItem
3841  * @extends Roo.bootstrap.Component
3842  * Bootstrap PaginationItem class
3843  * @cfg {String} html text
3844  * @cfg {String} href the link
3845  * @cfg {Boolean} preventDefault (true | false) default true
3846  * @cfg {Boolean} active (true | false) default false
3847  * 
3848  * 
3849  * @constructor
3850  * Create a new PaginationItem
3851  * @param {Object} config The config object
3852  */
3853
3854
3855 Roo.bootstrap.PaginationItem = function(config){
3856     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3857     this.addEvents({
3858         // raw events
3859         /**
3860          * @event click
3861          * The raw click event for the entire grid.
3862          * @param {Roo.EventObject} e
3863          */
3864         "click" : true
3865     });
3866 };
3867
3868 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3869     
3870     href : false,
3871     html : false,
3872     preventDefault: true,
3873     active : false,
3874     cls : false,
3875     
3876     getAutoCreate : function(){
3877         var cfg= {
3878             tag: 'li',
3879             cn: [
3880                 {
3881                     tag : 'a',
3882                     href : this.href ? this.href : '#',
3883                     html : this.html ? this.html : ''
3884                 }
3885             ]
3886         };
3887         
3888         if(this.cls){
3889             cfg.cls = this.cls;
3890         }
3891         
3892         if(this.active){
3893             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3894         }
3895         
3896         return cfg;
3897     },
3898     
3899     initEvents: function() {
3900         
3901         this.el.on('click', this.onClick, this);
3902         
3903     },
3904     onClick : function(e)
3905     {
3906         Roo.log('PaginationItem on click ');
3907         if(this.preventDefault){
3908             e.preventDefault();
3909         }
3910         
3911         this.fireEvent('click', this, e);
3912     }
3913    
3914 });
3915
3916  
3917
3918  /*
3919  * - LGPL
3920  *
3921  * slider
3922  * 
3923  */
3924
3925
3926 /**
3927  * @class Roo.bootstrap.Slider
3928  * @extends Roo.bootstrap.Component
3929  * Bootstrap Slider class
3930  *    
3931  * @constructor
3932  * Create a new Slider
3933  * @param {Object} config The config object
3934  */
3935
3936 Roo.bootstrap.Slider = function(config){
3937     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3938 };
3939
3940 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3941     
3942     getAutoCreate : function(){
3943         
3944         var cfg = {
3945             tag: 'div',
3946             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3947             cn: [
3948                 {
3949                     tag: 'a',
3950                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3951                 }
3952             ]
3953         }
3954         
3955         return cfg;
3956     }
3957    
3958 });
3959
3960  /*
3961  * Based on:
3962  * Ext JS Library 1.1.1
3963  * Copyright(c) 2006-2007, Ext JS, LLC.
3964  *
3965  * Originally Released Under LGPL - original licence link has changed is not relivant.
3966  *
3967  * Fork - LGPL
3968  * <script type="text/javascript">
3969  */
3970  
3971
3972 /**
3973  * @class Roo.grid.ColumnModel
3974  * @extends Roo.util.Observable
3975  * This is the default implementation of a ColumnModel used by the Grid. It defines
3976  * the columns in the grid.
3977  * <br>Usage:<br>
3978  <pre><code>
3979  var colModel = new Roo.grid.ColumnModel([
3980         {header: "Ticker", width: 60, sortable: true, locked: true},
3981         {header: "Company Name", width: 150, sortable: true},
3982         {header: "Market Cap.", width: 100, sortable: true},
3983         {header: "$ Sales", width: 100, sortable: true, renderer: money},
3984         {header: "Employees", width: 100, sortable: true, resizable: false}
3985  ]);
3986  </code></pre>
3987  * <p>
3988  
3989  * The config options listed for this class are options which may appear in each
3990  * individual column definition.
3991  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
3992  * @constructor
3993  * @param {Object} config An Array of column config objects. See this class's
3994  * config objects for details.
3995 */
3996 Roo.grid.ColumnModel = function(config){
3997         /**
3998      * The config passed into the constructor
3999      */
4000     this.config = config;
4001     this.lookup = {};
4002
4003     // if no id, create one
4004     // if the column does not have a dataIndex mapping,
4005     // map it to the order it is in the config
4006     for(var i = 0, len = config.length; i < len; i++){
4007         var c = config[i];
4008         if(typeof c.dataIndex == "undefined"){
4009             c.dataIndex = i;
4010         }
4011         if(typeof c.renderer == "string"){
4012             c.renderer = Roo.util.Format[c.renderer];
4013         }
4014         if(typeof c.id == "undefined"){
4015             c.id = Roo.id();
4016         }
4017         if(c.editor && c.editor.xtype){
4018             c.editor  = Roo.factory(c.editor, Roo.grid);
4019         }
4020         if(c.editor && c.editor.isFormField){
4021             c.editor = new Roo.grid.GridEditor(c.editor);
4022         }
4023         this.lookup[c.id] = c;
4024     }
4025
4026     /**
4027      * The width of columns which have no width specified (defaults to 100)
4028      * @type Number
4029      */
4030     this.defaultWidth = 100;
4031
4032     /**
4033      * Default sortable of columns which have no sortable specified (defaults to false)
4034      * @type Boolean
4035      */
4036     this.defaultSortable = false;
4037
4038     this.addEvents({
4039         /**
4040              * @event widthchange
4041              * Fires when the width of a column changes.
4042              * @param {ColumnModel} this
4043              * @param {Number} columnIndex The column index
4044              * @param {Number} newWidth The new width
4045              */
4046             "widthchange": true,
4047         /**
4048              * @event headerchange
4049              * Fires when the text of a header changes.
4050              * @param {ColumnModel} this
4051              * @param {Number} columnIndex The column index
4052              * @param {Number} newText The new header text
4053              */
4054             "headerchange": true,
4055         /**
4056              * @event hiddenchange
4057              * Fires when a column is hidden or "unhidden".
4058              * @param {ColumnModel} this
4059              * @param {Number} columnIndex The column index
4060              * @param {Boolean} hidden true if hidden, false otherwise
4061              */
4062             "hiddenchange": true,
4063             /**
4064          * @event columnmoved
4065          * Fires when a column is moved.
4066          * @param {ColumnModel} this
4067          * @param {Number} oldIndex
4068          * @param {Number} newIndex
4069          */
4070         "columnmoved" : true,
4071         /**
4072          * @event columlockchange
4073          * Fires when a column's locked state is changed
4074          * @param {ColumnModel} this
4075          * @param {Number} colIndex
4076          * @param {Boolean} locked true if locked
4077          */
4078         "columnlockchange" : true
4079     });
4080     Roo.grid.ColumnModel.superclass.constructor.call(this);
4081 };
4082 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4083     /**
4084      * @cfg {String} header The header text to display in the Grid view.
4085      */
4086     /**
4087      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4088      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4089      * specified, the column's index is used as an index into the Record's data Array.
4090      */
4091     /**
4092      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4093      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4094      */
4095     /**
4096      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4097      * Defaults to the value of the {@link #defaultSortable} property.
4098      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4099      */
4100     /**
4101      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4102      */
4103     /**
4104      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4105      */
4106     /**
4107      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4108      */
4109     /**
4110      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4111      */
4112     /**
4113      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4114      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4115      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4116      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4117      */
4118        /**
4119      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4120      */
4121     /**
4122      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4123      */
4124
4125     /**
4126      * Returns the id of the column at the specified index.
4127      * @param {Number} index The column index
4128      * @return {String} the id
4129      */
4130     getColumnId : function(index){
4131         return this.config[index].id;
4132     },
4133
4134     /**
4135      * Returns the column for a specified id.
4136      * @param {String} id The column id
4137      * @return {Object} the column
4138      */
4139     getColumnById : function(id){
4140         return this.lookup[id];
4141     },
4142
4143     
4144     /**
4145      * Returns the column for a specified dataIndex.
4146      * @param {String} dataIndex The column dataIndex
4147      * @return {Object|Boolean} the column or false if not found
4148      */
4149     getColumnByDataIndex: function(dataIndex){
4150         var index = this.findColumnIndex(dataIndex);
4151         return index > -1 ? this.config[index] : false;
4152     },
4153     
4154     /**
4155      * Returns the index for a specified column id.
4156      * @param {String} id The column id
4157      * @return {Number} the index, or -1 if not found
4158      */
4159     getIndexById : function(id){
4160         for(var i = 0, len = this.config.length; i < len; i++){
4161             if(this.config[i].id == id){
4162                 return i;
4163             }
4164         }
4165         return -1;
4166     },
4167     
4168     /**
4169      * Returns the index for a specified column dataIndex.
4170      * @param {String} dataIndex The column dataIndex
4171      * @return {Number} the index, or -1 if not found
4172      */
4173     
4174     findColumnIndex : function(dataIndex){
4175         for(var i = 0, len = this.config.length; i < len; i++){
4176             if(this.config[i].dataIndex == dataIndex){
4177                 return i;
4178             }
4179         }
4180         return -1;
4181     },
4182     
4183     
4184     moveColumn : function(oldIndex, newIndex){
4185         var c = this.config[oldIndex];
4186         this.config.splice(oldIndex, 1);
4187         this.config.splice(newIndex, 0, c);
4188         this.dataMap = null;
4189         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4190     },
4191
4192     isLocked : function(colIndex){
4193         return this.config[colIndex].locked === true;
4194     },
4195
4196     setLocked : function(colIndex, value, suppressEvent){
4197         if(this.isLocked(colIndex) == value){
4198             return;
4199         }
4200         this.config[colIndex].locked = value;
4201         if(!suppressEvent){
4202             this.fireEvent("columnlockchange", this, colIndex, value);
4203         }
4204     },
4205
4206     getTotalLockedWidth : function(){
4207         var totalWidth = 0;
4208         for(var i = 0; i < this.config.length; i++){
4209             if(this.isLocked(i) && !this.isHidden(i)){
4210                 this.totalWidth += this.getColumnWidth(i);
4211             }
4212         }
4213         return totalWidth;
4214     },
4215
4216     getLockedCount : function(){
4217         for(var i = 0, len = this.config.length; i < len; i++){
4218             if(!this.isLocked(i)){
4219                 return i;
4220             }
4221         }
4222     },
4223
4224     /**
4225      * Returns the number of columns.
4226      * @return {Number}
4227      */
4228     getColumnCount : function(visibleOnly){
4229         if(visibleOnly === true){
4230             var c = 0;
4231             for(var i = 0, len = this.config.length; i < len; i++){
4232                 if(!this.isHidden(i)){
4233                     c++;
4234                 }
4235             }
4236             return c;
4237         }
4238         return this.config.length;
4239     },
4240
4241     /**
4242      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4243      * @param {Function} fn
4244      * @param {Object} scope (optional)
4245      * @return {Array} result
4246      */
4247     getColumnsBy : function(fn, scope){
4248         var r = [];
4249         for(var i = 0, len = this.config.length; i < len; i++){
4250             var c = this.config[i];
4251             if(fn.call(scope||this, c, i) === true){
4252                 r[r.length] = c;
4253             }
4254         }
4255         return r;
4256     },
4257
4258     /**
4259      * Returns true if the specified column is sortable.
4260      * @param {Number} col The column index
4261      * @return {Boolean}
4262      */
4263     isSortable : function(col){
4264         if(typeof this.config[col].sortable == "undefined"){
4265             return this.defaultSortable;
4266         }
4267         return this.config[col].sortable;
4268     },
4269
4270     /**
4271      * Returns the rendering (formatting) function defined for the column.
4272      * @param {Number} col The column index.
4273      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4274      */
4275     getRenderer : function(col){
4276         if(!this.config[col].renderer){
4277             return Roo.grid.ColumnModel.defaultRenderer;
4278         }
4279         return this.config[col].renderer;
4280     },
4281
4282     /**
4283      * Sets the rendering (formatting) function for a column.
4284      * @param {Number} col The column index
4285      * @param {Function} fn The function to use to process the cell's raw data
4286      * to return HTML markup for the grid view. The render function is called with
4287      * the following parameters:<ul>
4288      * <li>Data value.</li>
4289      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4290      * <li>css A CSS style string to apply to the table cell.</li>
4291      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4292      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4293      * <li>Row index</li>
4294      * <li>Column index</li>
4295      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4296      */
4297     setRenderer : function(col, fn){
4298         this.config[col].renderer = fn;
4299     },
4300
4301     /**
4302      * Returns the width for the specified column.
4303      * @param {Number} col The column index
4304      * @return {Number}
4305      */
4306     getColumnWidth : function(col){
4307         return this.config[col].width * 1 || this.defaultWidth;
4308     },
4309
4310     /**
4311      * Sets the width for a column.
4312      * @param {Number} col The column index
4313      * @param {Number} width The new width
4314      */
4315     setColumnWidth : function(col, width, suppressEvent){
4316         this.config[col].width = width;
4317         this.totalWidth = null;
4318         if(!suppressEvent){
4319              this.fireEvent("widthchange", this, col, width);
4320         }
4321     },
4322
4323     /**
4324      * Returns the total width of all columns.
4325      * @param {Boolean} includeHidden True to include hidden column widths
4326      * @return {Number}
4327      */
4328     getTotalWidth : function(includeHidden){
4329         if(!this.totalWidth){
4330             this.totalWidth = 0;
4331             for(var i = 0, len = this.config.length; i < len; i++){
4332                 if(includeHidden || !this.isHidden(i)){
4333                     this.totalWidth += this.getColumnWidth(i);
4334                 }
4335             }
4336         }
4337         return this.totalWidth;
4338     },
4339
4340     /**
4341      * Returns the header for the specified column.
4342      * @param {Number} col The column index
4343      * @return {String}
4344      */
4345     getColumnHeader : function(col){
4346         return this.config[col].header;
4347     },
4348
4349     /**
4350      * Sets the header for a column.
4351      * @param {Number} col The column index
4352      * @param {String} header The new header
4353      */
4354     setColumnHeader : function(col, header){
4355         this.config[col].header = header;
4356         this.fireEvent("headerchange", this, col, header);
4357     },
4358
4359     /**
4360      * Returns the tooltip for the specified column.
4361      * @param {Number} col The column index
4362      * @return {String}
4363      */
4364     getColumnTooltip : function(col){
4365             return this.config[col].tooltip;
4366     },
4367     /**
4368      * Sets the tooltip for a column.
4369      * @param {Number} col The column index
4370      * @param {String} tooltip The new tooltip
4371      */
4372     setColumnTooltip : function(col, tooltip){
4373             this.config[col].tooltip = tooltip;
4374     },
4375
4376     /**
4377      * Returns the dataIndex for the specified column.
4378      * @param {Number} col The column index
4379      * @return {Number}
4380      */
4381     getDataIndex : function(col){
4382         return this.config[col].dataIndex;
4383     },
4384
4385     /**
4386      * Sets the dataIndex for a column.
4387      * @param {Number} col The column index
4388      * @param {Number} dataIndex The new dataIndex
4389      */
4390     setDataIndex : function(col, dataIndex){
4391         this.config[col].dataIndex = dataIndex;
4392     },
4393
4394     
4395     
4396     /**
4397      * Returns true if the cell is editable.
4398      * @param {Number} colIndex The column index
4399      * @param {Number} rowIndex The row index
4400      * @return {Boolean}
4401      */
4402     isCellEditable : function(colIndex, rowIndex){
4403         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4404     },
4405
4406     /**
4407      * Returns the editor defined for the cell/column.
4408      * return false or null to disable editing.
4409      * @param {Number} colIndex The column index
4410      * @param {Number} rowIndex The row index
4411      * @return {Object}
4412      */
4413     getCellEditor : function(colIndex, rowIndex){
4414         return this.config[colIndex].editor;
4415     },
4416
4417     /**
4418      * Sets if a column is editable.
4419      * @param {Number} col The column index
4420      * @param {Boolean} editable True if the column is editable
4421      */
4422     setEditable : function(col, editable){
4423         this.config[col].editable = editable;
4424     },
4425
4426
4427     /**
4428      * Returns true if the column is hidden.
4429      * @param {Number} colIndex The column index
4430      * @return {Boolean}
4431      */
4432     isHidden : function(colIndex){
4433         return this.config[colIndex].hidden;
4434     },
4435
4436
4437     /**
4438      * Returns true if the column width cannot be changed
4439      */
4440     isFixed : function(colIndex){
4441         return this.config[colIndex].fixed;
4442     },
4443
4444     /**
4445      * Returns true if the column can be resized
4446      * @return {Boolean}
4447      */
4448     isResizable : function(colIndex){
4449         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4450     },
4451     /**
4452      * Sets if a column is hidden.
4453      * @param {Number} colIndex The column index
4454      * @param {Boolean} hidden True if the column is hidden
4455      */
4456     setHidden : function(colIndex, hidden){
4457         this.config[colIndex].hidden = hidden;
4458         this.totalWidth = null;
4459         this.fireEvent("hiddenchange", this, colIndex, hidden);
4460     },
4461
4462     /**
4463      * Sets the editor for a column.
4464      * @param {Number} col The column index
4465      * @param {Object} editor The editor object
4466      */
4467     setEditor : function(col, editor){
4468         this.config[col].editor = editor;
4469     }
4470 });
4471
4472 Roo.grid.ColumnModel.defaultRenderer = function(value){
4473         if(typeof value == "string" && value.length < 1){
4474             return "&#160;";
4475         }
4476         return value;
4477 };
4478
4479 // Alias for backwards compatibility
4480 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4481 /*
4482  * Based on:
4483  * Ext JS Library 1.1.1
4484  * Copyright(c) 2006-2007, Ext JS, LLC.
4485  *
4486  * Originally Released Under LGPL - original licence link has changed is not relivant.
4487  *
4488  * Fork - LGPL
4489  * <script type="text/javascript">
4490  */
4491  
4492 /**
4493  * @class Roo.LoadMask
4494  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4495  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4496  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4497  * element's UpdateManager load indicator and will be destroyed after the initial load.
4498  * @constructor
4499  * Create a new LoadMask
4500  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4501  * @param {Object} config The config object
4502  */
4503 Roo.LoadMask = function(el, config){
4504     this.el = Roo.get(el);
4505     Roo.apply(this, config);
4506     if(this.store){
4507         this.store.on('beforeload', this.onBeforeLoad, this);
4508         this.store.on('load', this.onLoad, this);
4509         this.store.on('loadexception', this.onLoadException, this);
4510         this.removeMask = false;
4511     }else{
4512         var um = this.el.getUpdateManager();
4513         um.showLoadIndicator = false; // disable the default indicator
4514         um.on('beforeupdate', this.onBeforeLoad, this);
4515         um.on('update', this.onLoad, this);
4516         um.on('failure', this.onLoad, this);
4517         this.removeMask = true;
4518     }
4519 };
4520
4521 Roo.LoadMask.prototype = {
4522     /**
4523      * @cfg {Boolean} removeMask
4524      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4525      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4526      */
4527     /**
4528      * @cfg {String} msg
4529      * The text to display in a centered loading message box (defaults to 'Loading...')
4530      */
4531     msg : 'Loading...',
4532     /**
4533      * @cfg {String} msgCls
4534      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4535      */
4536     msgCls : 'x-mask-loading',
4537
4538     /**
4539      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4540      * @type Boolean
4541      */
4542     disabled: false,
4543
4544     /**
4545      * Disables the mask to prevent it from being displayed
4546      */
4547     disable : function(){
4548        this.disabled = true;
4549     },
4550
4551     /**
4552      * Enables the mask so that it can be displayed
4553      */
4554     enable : function(){
4555         this.disabled = false;
4556     },
4557     
4558     onLoadException : function()
4559     {
4560         Roo.log(arguments);
4561         
4562         if (typeof(arguments[3]) != 'undefined') {
4563             Roo.MessageBox.alert("Error loading",arguments[3]);
4564         } 
4565         /*
4566         try {
4567             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4568                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4569             }   
4570         } catch(e) {
4571             
4572         }
4573         */
4574     
4575         
4576         
4577         this.el.unmask(this.removeMask);
4578     },
4579     // private
4580     onLoad : function()
4581     {
4582         this.el.unmask(this.removeMask);
4583     },
4584
4585     // private
4586     onBeforeLoad : function(){
4587         if(!this.disabled){
4588             this.el.mask(this.msg, this.msgCls);
4589         }
4590     },
4591
4592     // private
4593     destroy : function(){
4594         if(this.store){
4595             this.store.un('beforeload', this.onBeforeLoad, this);
4596             this.store.un('load', this.onLoad, this);
4597             this.store.un('loadexception', this.onLoadException, this);
4598         }else{
4599             var um = this.el.getUpdateManager();
4600             um.un('beforeupdate', this.onBeforeLoad, this);
4601             um.un('update', this.onLoad, this);
4602             um.un('failure', this.onLoad, this);
4603         }
4604     }
4605 };/*
4606  * - LGPL
4607  *
4608  * table
4609  * 
4610  */
4611
4612 /**
4613  * @class Roo.bootstrap.Table
4614  * @extends Roo.bootstrap.Component
4615  * Bootstrap Table class
4616  * @cfg {String} cls table class
4617  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4618  * @cfg {String} bgcolor Specifies the background color for a table
4619  * @cfg {Number} border Specifies whether the table cells should have borders or not
4620  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4621  * @cfg {Number} cellspacing Specifies the space between cells
4622  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4623  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4624  * @cfg {String} sortable Specifies that the table should be sortable
4625  * @cfg {String} summary Specifies a summary of the content of a table
4626  * @cfg {Number} width Specifies the width of a table
4627  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4628  * 
4629  * @cfg {boolean} striped Should the rows be alternative striped
4630  * @cfg {boolean} bordered Add borders to the table
4631  * @cfg {boolean} hover Add hover highlighting
4632  * @cfg {boolean} condensed Format condensed
4633  * @cfg {boolean} responsive Format condensed
4634  * @cfg {Boolean} loadMask (true|false) default false
4635  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4636  * @cfg {Boolean} thead (true|false) generate thead, default true
4637  * @cfg {Boolean} RowSelection (true|false) default false
4638  * @cfg {Boolean} CellSelection (true|false) default false
4639  *
4640  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4641  
4642  * 
4643  * @constructor
4644  * Create a new Table
4645  * @param {Object} config The config object
4646  */
4647
4648 Roo.bootstrap.Table = function(config){
4649     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4650     
4651     if (this.sm) {
4652         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4653         this.sm = this.selModel;
4654         this.sm.xmodule = this.xmodule || false;
4655     }
4656     if (this.cm && typeof(this.cm.config) == 'undefined') {
4657         this.colModel = new Roo.grid.ColumnModel(this.cm);
4658         this.cm = this.colModel;
4659         this.cm.xmodule = this.xmodule || false;
4660     }
4661     if (this.store) {
4662         this.store= Roo.factory(this.store, Roo.data);
4663         this.ds = this.store;
4664         this.ds.xmodule = this.xmodule || false;
4665          
4666     }
4667     if (this.footer && this.store) {
4668         this.footer.dataSource = this.ds;
4669         this.footer = Roo.factory(this.footer);
4670     }
4671     
4672     /** @private */
4673     this.addEvents({
4674         /**
4675          * @event cellclick
4676          * Fires when a cell is clicked
4677          * @param {Roo.bootstrap.Table} this
4678          * @param {Roo.Element} el
4679          * @param {Number} rowIndex
4680          * @param {Number} columnIndex
4681          * @param {Roo.EventObject} e
4682          */
4683         "cellclick" : true,
4684         /**
4685          * @event celldblclick
4686          * Fires when a cell is double clicked
4687          * @param {Roo.bootstrap.Table} this
4688          * @param {Roo.Element} el
4689          * @param {Number} rowIndex
4690          * @param {Number} columnIndex
4691          * @param {Roo.EventObject} e
4692          */
4693         "celldblclick" : true,
4694         /**
4695          * @event rowclick
4696          * Fires when a row is clicked
4697          * @param {Roo.bootstrap.Table} this
4698          * @param {Roo.Element} el
4699          * @param {Number} rowIndex
4700          * @param {Roo.EventObject} e
4701          */
4702         "rowclick" : true,
4703         /**
4704          * @event rowdblclick
4705          * Fires when a row is double clicked
4706          * @param {Roo.bootstrap.Table} this
4707          * @param {Roo.Element} el
4708          * @param {Number} rowIndex
4709          * @param {Roo.EventObject} e
4710          */
4711         "rowdblclick" : true,
4712         /**
4713          * @event mouseover
4714          * Fires when a mouseover occur
4715          * @param {Roo.bootstrap.Table} this
4716          * @param {Roo.Element} el
4717          * @param {Number} rowIndex
4718          * @param {Number} columnIndex
4719          * @param {Roo.EventObject} e
4720          */
4721         "mouseover" : true,
4722         /**
4723          * @event mouseout
4724          * Fires when a mouseout occur
4725          * @param {Roo.bootstrap.Table} this
4726          * @param {Roo.Element} el
4727          * @param {Number} rowIndex
4728          * @param {Number} columnIndex
4729          * @param {Roo.EventObject} e
4730          */
4731         "mouseout" : true,
4732         /**
4733          * @event rowclass
4734          * Fires when a row is rendered, so you can change add a style to it.
4735          * @param {Roo.bootstrap.Table} this
4736          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4737          */
4738         'rowclass' : true
4739         
4740     });
4741 };
4742
4743 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4744     
4745     cls: false,
4746     align: false,
4747     bgcolor: false,
4748     border: false,
4749     cellpadding: false,
4750     cellspacing: false,
4751     frame: false,
4752     rules: false,
4753     sortable: false,
4754     summary: false,
4755     width: false,
4756     striped : false,
4757     bordered: false,
4758     hover:  false,
4759     condensed : false,
4760     responsive : false,
4761     sm : false,
4762     cm : false,
4763     store : false,
4764     loadMask : false,
4765     tfoot : true,
4766     thead : true,
4767     RowSelection : false,
4768     CellSelection : false,
4769     layout : false,
4770     
4771     // Roo.Element - the tbody
4772     mainBody: false, 
4773     
4774     getAutoCreate : function(){
4775         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4776         
4777         cfg = {
4778             tag: 'table',
4779             cls : 'table',
4780             cn : []
4781         }
4782             
4783         if (this.striped) {
4784             cfg.cls += ' table-striped';
4785         }
4786         
4787         if (this.hover) {
4788             cfg.cls += ' table-hover';
4789         }
4790         if (this.bordered) {
4791             cfg.cls += ' table-bordered';
4792         }
4793         if (this.condensed) {
4794             cfg.cls += ' table-condensed';
4795         }
4796         if (this.responsive) {
4797             cfg.cls += ' table-responsive';
4798         }
4799         
4800         if (this.cls) {
4801             cfg.cls+=  ' ' +this.cls;
4802         }
4803         
4804         // this lot should be simplifed...
4805         
4806         if (this.align) {
4807             cfg.align=this.align;
4808         }
4809         if (this.bgcolor) {
4810             cfg.bgcolor=this.bgcolor;
4811         }
4812         if (this.border) {
4813             cfg.border=this.border;
4814         }
4815         if (this.cellpadding) {
4816             cfg.cellpadding=this.cellpadding;
4817         }
4818         if (this.cellspacing) {
4819             cfg.cellspacing=this.cellspacing;
4820         }
4821         if (this.frame) {
4822             cfg.frame=this.frame;
4823         }
4824         if (this.rules) {
4825             cfg.rules=this.rules;
4826         }
4827         if (this.sortable) {
4828             cfg.sortable=this.sortable;
4829         }
4830         if (this.summary) {
4831             cfg.summary=this.summary;
4832         }
4833         if (this.width) {
4834             cfg.width=this.width;
4835         }
4836         if (this.layout) {
4837             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4838         }
4839         
4840         if(this.store || this.cm){
4841             if(this.thead){
4842                 cfg.cn.push(this.renderHeader());
4843             }
4844             
4845             cfg.cn.push(this.renderBody());
4846             
4847             if(this.tfoot){
4848                 cfg.cn.push(this.renderFooter());
4849             }
4850             
4851             cfg.cls+=  ' TableGrid';
4852         }
4853         
4854         return { cn : [ cfg ] };
4855     },
4856     
4857     initEvents : function()
4858     {   
4859         if(!this.store || !this.cm){
4860             return;
4861         }
4862         
4863         //Roo.log('initEvents with ds!!!!');
4864         
4865         this.mainBody = this.el.select('tbody', true).first();
4866         
4867         
4868         var _this = this;
4869         
4870         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4871             e.on('click', _this.sort, _this);
4872         });
4873         
4874         this.el.on("click", this.onClick, this);
4875         this.el.on("dblclick", this.onDblClick, this);
4876         
4877         this.parent().el.setStyle('position', 'relative');
4878         if (this.footer) {
4879             this.footer.parentId = this.id;
4880             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4881         }
4882         
4883         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4884         
4885         this.store.on('load', this.onLoad, this);
4886         this.store.on('beforeload', this.onBeforeLoad, this);
4887         this.store.on('update', this.onUpdate, this);
4888         
4889     },
4890     
4891     onMouseover : function(e, el)
4892     {
4893         var cell = Roo.get(el);
4894         
4895         if(!cell){
4896             return;
4897         }
4898         
4899         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4900             cell = cell.findParent('td', false, true);
4901         }
4902         
4903         var row = cell.findParent('tr', false, true);
4904         var cellIndex = cell.dom.cellIndex;
4905         var rowIndex = row.dom.rowIndex - 1; // start from 0
4906         
4907         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
4908         
4909     },
4910     
4911     onMouseout : function(e, el)
4912     {
4913         var cell = Roo.get(el);
4914         
4915         if(!cell){
4916             return;
4917         }
4918         
4919         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4920             cell = cell.findParent('td', false, true);
4921         }
4922         
4923         var row = cell.findParent('tr', false, true);
4924         var cellIndex = cell.dom.cellIndex;
4925         var rowIndex = row.dom.rowIndex - 1; // start from 0
4926         
4927         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
4928         
4929     },
4930     
4931     onClick : function(e, el)
4932     {
4933         var cell = Roo.get(el);
4934         
4935         if(!cell || (!this.CellSelection && !this.RowSelection)){
4936             return;
4937         }
4938         
4939         
4940         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4941             cell = cell.findParent('td', false, true);
4942         }
4943         
4944         var row = cell.findParent('tr', false, true);
4945         var cellIndex = cell.dom.cellIndex;
4946         var rowIndex = row.dom.rowIndex - 1;
4947         
4948         if(this.CellSelection){
4949             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
4950         }
4951         
4952         if(this.RowSelection){
4953             this.fireEvent('rowclick', this, row, rowIndex, e);
4954         }
4955         
4956         
4957     },
4958     
4959     onDblClick : function(e,el)
4960     {
4961         var cell = Roo.get(el);
4962         
4963         if(!cell || (!this.CellSelection && !this.RowSelection)){
4964             return;
4965         }
4966         
4967         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4968             cell = cell.findParent('td', false, true);
4969         }
4970         
4971         var row = cell.findParent('tr', false, true);
4972         var cellIndex = cell.dom.cellIndex;
4973         var rowIndex = row.dom.rowIndex - 1;
4974         
4975         if(this.CellSelection){
4976             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
4977         }
4978         
4979         if(this.RowSelection){
4980             this.fireEvent('rowdblclick', this, row, rowIndex, e);
4981         }
4982     },
4983     
4984     sort : function(e,el)
4985     {
4986         var col = Roo.get(el)
4987         
4988         if(!col.hasClass('sortable')){
4989             return;
4990         }
4991         
4992         var sort = col.attr('sort');
4993         var dir = 'ASC';
4994         
4995         if(col.hasClass('glyphicon-arrow-up')){
4996             dir = 'DESC';
4997         }
4998         
4999         this.store.sortInfo = {field : sort, direction : dir};
5000         
5001         if (this.footer) {
5002             Roo.log("calling footer first");
5003             this.footer.onClick('first');
5004         } else {
5005         
5006             this.store.load({ params : { start : 0 } });
5007         }
5008     },
5009     
5010     renderHeader : function()
5011     {
5012         var header = {
5013             tag: 'thead',
5014             cn : []
5015         };
5016         
5017         var cm = this.cm;
5018         
5019         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5020             
5021             var config = cm.config[i];
5022                     
5023             var c = {
5024                 tag: 'th',
5025                 style : '',
5026                 html: cm.getColumnHeader(i)
5027             };
5028             
5029             if(typeof(config.hidden) != 'undefined' && config.hidden){
5030                 c.style += ' display:none;';
5031             }
5032             
5033             if(typeof(config.dataIndex) != 'undefined'){
5034                 c.sort = config.dataIndex;
5035             }
5036             
5037             if(typeof(config.sortable) != 'undefined' && config.sortable){
5038                 c.cls = 'sortable';
5039             }
5040             
5041 //            if(typeof(config.align) != 'undefined' && config.align.length){
5042 //                c.style += ' text-align:' + config.align + ';';
5043 //            }
5044             
5045             if(typeof(config.width) != 'undefined'){
5046                 c.style += ' width:' + config.width + 'px;';
5047             }
5048             
5049             header.cn.push(c)
5050         }
5051         
5052         return header;
5053     },
5054     
5055     renderBody : function()
5056     {
5057         var body = {
5058             tag: 'tbody',
5059             cn : [
5060                 {
5061                     tag: 'tr',
5062                     cn : [
5063                         {
5064                             tag : 'td',
5065                             colspan :  this.cm.getColumnCount()
5066                         }
5067                     ]
5068                 }
5069             ]
5070         };
5071         
5072         return body;
5073     },
5074     
5075     renderFooter : function()
5076     {
5077         var footer = {
5078             tag: 'tfoot',
5079             cn : [
5080                 {
5081                     tag: 'tr',
5082                     cn : [
5083                         {
5084                             tag : 'td',
5085                             colspan :  this.cm.getColumnCount()
5086                         }
5087                     ]
5088                 }
5089             ]
5090         };
5091         
5092         return footer;
5093     },
5094     
5095     
5096     
5097     onLoad : function()
5098     {
5099         Roo.log('ds onload');
5100         this.clear();
5101         
5102         var _this = this;
5103         var cm = this.cm;
5104         var ds = this.store;
5105         
5106         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5107             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5108             
5109             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5110                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5111             }
5112             
5113             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5114                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5115             }
5116         });
5117         
5118         var tbody =  this.mainBody;
5119         
5120         var renders = [];
5121                     
5122         if(ds.getCount() > 0){
5123             ds.data.each(function(d,rowIndex){
5124                 var row =  this.renderRow(cm, ds, rowIndex);
5125                 
5126                 tbody.createChild(row);
5127                 
5128                 var _this = this;
5129                 
5130                 if(row.cellObjects.length){
5131                     Roo.each(row.cellObjects, function(r){
5132                         _this.renderCellObject(r);
5133                     })
5134                 }
5135                 
5136             }, this);
5137         }
5138         
5139         Roo.each(this.el.select('tbody td', true).elements, function(e){
5140             e.on('mouseover', _this.onMouseover, _this);
5141         });
5142         
5143         Roo.each(this.el.select('tbody td', true).elements, function(e){
5144             e.on('mouseout', _this.onMouseout, _this);
5145         });
5146
5147         //if(this.loadMask){
5148         //    this.maskEl.hide();
5149         //}
5150     },
5151     
5152     
5153     onUpdate : function(ds,record)
5154     {
5155         this.refreshRow(record);
5156     },
5157     onRemove : function(ds, record, index, isUpdate){
5158         if(isUpdate !== true){
5159             this.fireEvent("beforerowremoved", this, index, record);
5160         }
5161         var bt = this.mainBody.dom;
5162         if(bt.rows[index]){
5163             bt.removeChild(bt.rows[index]);
5164         }
5165         
5166         if(isUpdate !== true){
5167             //this.stripeRows(index);
5168             //this.syncRowHeights(index, index);
5169             //this.layout();
5170             this.fireEvent("rowremoved", this, index, record);
5171         }
5172     },
5173     
5174     
5175     refreshRow : function(record){
5176         var ds = this.store, index;
5177         if(typeof record == 'number'){
5178             index = record;
5179             record = ds.getAt(index);
5180         }else{
5181             index = ds.indexOf(record);
5182         }
5183         this.insertRow(ds, index, true);
5184         this.onRemove(ds, record, index+1, true);
5185         //this.syncRowHeights(index, index);
5186         //this.layout();
5187         this.fireEvent("rowupdated", this, index, record);
5188     },
5189     
5190     insertRow : function(dm, rowIndex, isUpdate){
5191         
5192         if(!isUpdate){
5193             this.fireEvent("beforerowsinserted", this, rowIndex);
5194         }
5195             //var s = this.getScrollState();
5196         var row = this.renderRow(this.cm, this.store, rowIndex);
5197         // insert before rowIndex..
5198         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5199         Roo.log(e);
5200         
5201         var _this = this;
5202                 
5203         if(row.cellObjects.length){
5204             Roo.each(row.cellObjects, function(r){
5205                 _this.renderCellObject(r);
5206             })
5207         }
5208             
5209         if(!isUpdate){
5210             this.fireEvent("rowsinserted", this, rowIndex);
5211             //this.syncRowHeights(firstRow, lastRow);
5212             //this.stripeRows(firstRow);
5213             //this.layout();
5214         }
5215         
5216     },
5217     
5218     
5219     getRowDom : function(rowIndex)
5220     {
5221         // not sure if I need to check this.. but let's do it anyway..
5222         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5223                 this.mainBody.dom.rows[rowIndex] : false
5224     },
5225     // returns the object tree for a tr..
5226   
5227     
5228     renderRow : function(cm, ds, rowIndex) {
5229         
5230         var d = ds.getAt(rowIndex);
5231         
5232         var row = {
5233             tag : 'tr',
5234             cn : []
5235         };
5236             
5237         var cellObjects = [];
5238         
5239         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5240             var config = cm.config[i];
5241             
5242             var renderer = cm.getRenderer(i);
5243             var value = '';
5244             var id = false;
5245             
5246             if(typeof(renderer) !== 'undefined'){
5247                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5248             }
5249             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5250             // and are rendered into the cells after the row is rendered - using the id for the element.
5251             
5252             if(typeof(value) === 'object'){
5253                 id = Roo.id();
5254                 cellObjects.push({
5255                     container : id,
5256                     cfg : value 
5257                 })
5258             }
5259             
5260             var rowcfg = {
5261                 record: d,
5262                 rowIndex : rowIndex,
5263                 colIndex : i,
5264                 rowClass : ''
5265             }
5266
5267             this.fireEvent('rowclass', this, rowcfg);
5268             
5269             var td = {
5270                 tag: 'td',
5271                 cls : rowcfg.rowClass,
5272                 style: '',
5273                 html: (typeof(value) === 'object') ? '' : value
5274             };
5275             
5276             if (id) {
5277                 td.id = id;
5278             }
5279             
5280             if(typeof(config.hidden) != 'undefined' && config.hidden){
5281                 td.style += ' display:none;';
5282             }
5283             
5284             if(typeof(config.align) != 'undefined' && config.align.length){
5285                 td.style += ' text-align:' + config.align + ';';
5286             }
5287             
5288             if(typeof(config.width) != 'undefined'){
5289                 td.style += ' width:' +  config.width + 'px;';
5290             }
5291              
5292             row.cn.push(td);
5293            
5294         }
5295         
5296         row.cellObjects = cellObjects;
5297         
5298         return row;
5299           
5300     },
5301     
5302     
5303     
5304     onBeforeLoad : function()
5305     {
5306         //Roo.log('ds onBeforeLoad');
5307         
5308         //this.clear();
5309         
5310         //if(this.loadMask){
5311         //    this.maskEl.show();
5312         //}
5313     },
5314     
5315     clear : function()
5316     {
5317         this.el.select('tbody', true).first().dom.innerHTML = '';
5318     },
5319     
5320     getSelectionModel : function(){
5321         if(!this.selModel){
5322             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5323         }
5324         return this.selModel;
5325     },
5326     /*
5327      * Render the Roo.bootstrap object from renderder
5328      */
5329     renderCellObject : function(r)
5330     {
5331         var _this = this;
5332         
5333         var t = r.cfg.render(r.container);
5334         
5335         if(r.cfg.cn){
5336             Roo.each(r.cfg.cn, function(c){
5337                 var child = {
5338                     container: t.getChildContainer(),
5339                     cfg: c
5340                 }
5341                 _this.renderCellObject(child);
5342             })
5343         }
5344     }
5345    
5346 });
5347
5348  
5349
5350  /*
5351  * - LGPL
5352  *
5353  * table cell
5354  * 
5355  */
5356
5357 /**
5358  * @class Roo.bootstrap.TableCell
5359  * @extends Roo.bootstrap.Component
5360  * Bootstrap TableCell class
5361  * @cfg {String} html cell contain text
5362  * @cfg {String} cls cell class
5363  * @cfg {String} tag cell tag (td|th) default td
5364  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5365  * @cfg {String} align Aligns the content in a cell
5366  * @cfg {String} axis Categorizes cells
5367  * @cfg {String} bgcolor Specifies the background color of a cell
5368  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5369  * @cfg {Number} colspan Specifies the number of columns a cell should span
5370  * @cfg {String} headers Specifies one or more header cells a cell is related to
5371  * @cfg {Number} height Sets the height of a cell
5372  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5373  * @cfg {Number} rowspan Sets the number of rows a cell should span
5374  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5375  * @cfg {String} valign Vertical aligns the content in a cell
5376  * @cfg {Number} width Specifies the width of a cell
5377  * 
5378  * @constructor
5379  * Create a new TableCell
5380  * @param {Object} config The config object
5381  */
5382
5383 Roo.bootstrap.TableCell = function(config){
5384     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5385 };
5386
5387 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5388     
5389     html: false,
5390     cls: false,
5391     tag: false,
5392     abbr: false,
5393     align: false,
5394     axis: false,
5395     bgcolor: false,
5396     charoff: false,
5397     colspan: false,
5398     headers: false,
5399     height: false,
5400     nowrap: false,
5401     rowspan: false,
5402     scope: false,
5403     valign: false,
5404     width: false,
5405     
5406     
5407     getAutoCreate : function(){
5408         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5409         
5410         cfg = {
5411             tag: 'td'
5412         }
5413         
5414         if(this.tag){
5415             cfg.tag = this.tag;
5416         }
5417         
5418         if (this.html) {
5419             cfg.html=this.html
5420         }
5421         if (this.cls) {
5422             cfg.cls=this.cls
5423         }
5424         if (this.abbr) {
5425             cfg.abbr=this.abbr
5426         }
5427         if (this.align) {
5428             cfg.align=this.align
5429         }
5430         if (this.axis) {
5431             cfg.axis=this.axis
5432         }
5433         if (this.bgcolor) {
5434             cfg.bgcolor=this.bgcolor
5435         }
5436         if (this.charoff) {
5437             cfg.charoff=this.charoff
5438         }
5439         if (this.colspan) {
5440             cfg.colspan=this.colspan
5441         }
5442         if (this.headers) {
5443             cfg.headers=this.headers
5444         }
5445         if (this.height) {
5446             cfg.height=this.height
5447         }
5448         if (this.nowrap) {
5449             cfg.nowrap=this.nowrap
5450         }
5451         if (this.rowspan) {
5452             cfg.rowspan=this.rowspan
5453         }
5454         if (this.scope) {
5455             cfg.scope=this.scope
5456         }
5457         if (this.valign) {
5458             cfg.valign=this.valign
5459         }
5460         if (this.width) {
5461             cfg.width=this.width
5462         }
5463         
5464         
5465         return cfg;
5466     }
5467    
5468 });
5469
5470  
5471
5472  /*
5473  * - LGPL
5474  *
5475  * table row
5476  * 
5477  */
5478
5479 /**
5480  * @class Roo.bootstrap.TableRow
5481  * @extends Roo.bootstrap.Component
5482  * Bootstrap TableRow class
5483  * @cfg {String} cls row class
5484  * @cfg {String} align Aligns the content in a table row
5485  * @cfg {String} bgcolor Specifies a background color for a table row
5486  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5487  * @cfg {String} valign Vertical aligns the content in a table row
5488  * 
5489  * @constructor
5490  * Create a new TableRow
5491  * @param {Object} config The config object
5492  */
5493
5494 Roo.bootstrap.TableRow = function(config){
5495     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5496 };
5497
5498 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5499     
5500     cls: false,
5501     align: false,
5502     bgcolor: false,
5503     charoff: false,
5504     valign: false,
5505     
5506     getAutoCreate : function(){
5507         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5508         
5509         cfg = {
5510             tag: 'tr'
5511         }
5512             
5513         if(this.cls){
5514             cfg.cls = this.cls;
5515         }
5516         if(this.align){
5517             cfg.align = this.align;
5518         }
5519         if(this.bgcolor){
5520             cfg.bgcolor = this.bgcolor;
5521         }
5522         if(this.charoff){
5523             cfg.charoff = this.charoff;
5524         }
5525         if(this.valign){
5526             cfg.valign = this.valign;
5527         }
5528         
5529         return cfg;
5530     }
5531    
5532 });
5533
5534  
5535
5536  /*
5537  * - LGPL
5538  *
5539  * table body
5540  * 
5541  */
5542
5543 /**
5544  * @class Roo.bootstrap.TableBody
5545  * @extends Roo.bootstrap.Component
5546  * Bootstrap TableBody class
5547  * @cfg {String} cls element class
5548  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5549  * @cfg {String} align Aligns the content inside the element
5550  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5551  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5552  * 
5553  * @constructor
5554  * Create a new TableBody
5555  * @param {Object} config The config object
5556  */
5557
5558 Roo.bootstrap.TableBody = function(config){
5559     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5560 };
5561
5562 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5563     
5564     cls: false,
5565     tag: false,
5566     align: false,
5567     charoff: false,
5568     valign: false,
5569     
5570     getAutoCreate : function(){
5571         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5572         
5573         cfg = {
5574             tag: 'tbody'
5575         }
5576             
5577         if (this.cls) {
5578             cfg.cls=this.cls
5579         }
5580         if(this.tag){
5581             cfg.tag = this.tag;
5582         }
5583         
5584         if(this.align){
5585             cfg.align = this.align;
5586         }
5587         if(this.charoff){
5588             cfg.charoff = this.charoff;
5589         }
5590         if(this.valign){
5591             cfg.valign = this.valign;
5592         }
5593         
5594         return cfg;
5595     }
5596     
5597     
5598 //    initEvents : function()
5599 //    {
5600 //        
5601 //        if(!this.store){
5602 //            return;
5603 //        }
5604 //        
5605 //        this.store = Roo.factory(this.store, Roo.data);
5606 //        this.store.on('load', this.onLoad, this);
5607 //        
5608 //        this.store.load();
5609 //        
5610 //    },
5611 //    
5612 //    onLoad: function () 
5613 //    {   
5614 //        this.fireEvent('load', this);
5615 //    }
5616 //    
5617 //   
5618 });
5619
5620  
5621
5622  /*
5623  * Based on:
5624  * Ext JS Library 1.1.1
5625  * Copyright(c) 2006-2007, Ext JS, LLC.
5626  *
5627  * Originally Released Under LGPL - original licence link has changed is not relivant.
5628  *
5629  * Fork - LGPL
5630  * <script type="text/javascript">
5631  */
5632
5633 // as we use this in bootstrap.
5634 Roo.namespace('Roo.form');
5635  /**
5636  * @class Roo.form.Action
5637  * Internal Class used to handle form actions
5638  * @constructor
5639  * @param {Roo.form.BasicForm} el The form element or its id
5640  * @param {Object} config Configuration options
5641  */
5642
5643  
5644  
5645 // define the action interface
5646 Roo.form.Action = function(form, options){
5647     this.form = form;
5648     this.options = options || {};
5649 };
5650 /**
5651  * Client Validation Failed
5652  * @const 
5653  */
5654 Roo.form.Action.CLIENT_INVALID = 'client';
5655 /**
5656  * Server Validation Failed
5657  * @const 
5658  */
5659 Roo.form.Action.SERVER_INVALID = 'server';
5660  /**
5661  * Connect to Server Failed
5662  * @const 
5663  */
5664 Roo.form.Action.CONNECT_FAILURE = 'connect';
5665 /**
5666  * Reading Data from Server Failed
5667  * @const 
5668  */
5669 Roo.form.Action.LOAD_FAILURE = 'load';
5670
5671 Roo.form.Action.prototype = {
5672     type : 'default',
5673     failureType : undefined,
5674     response : undefined,
5675     result : undefined,
5676
5677     // interface method
5678     run : function(options){
5679
5680     },
5681
5682     // interface method
5683     success : function(response){
5684
5685     },
5686
5687     // interface method
5688     handleResponse : function(response){
5689
5690     },
5691
5692     // default connection failure
5693     failure : function(response){
5694         
5695         this.response = response;
5696         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5697         this.form.afterAction(this, false);
5698     },
5699
5700     processResponse : function(response){
5701         this.response = response;
5702         if(!response.responseText){
5703             return true;
5704         }
5705         this.result = this.handleResponse(response);
5706         return this.result;
5707     },
5708
5709     // utility functions used internally
5710     getUrl : function(appendParams){
5711         var url = this.options.url || this.form.url || this.form.el.dom.action;
5712         if(appendParams){
5713             var p = this.getParams();
5714             if(p){
5715                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5716             }
5717         }
5718         return url;
5719     },
5720
5721     getMethod : function(){
5722         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5723     },
5724
5725     getParams : function(){
5726         var bp = this.form.baseParams;
5727         var p = this.options.params;
5728         if(p){
5729             if(typeof p == "object"){
5730                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5731             }else if(typeof p == 'string' && bp){
5732                 p += '&' + Roo.urlEncode(bp);
5733             }
5734         }else if(bp){
5735             p = Roo.urlEncode(bp);
5736         }
5737         return p;
5738     },
5739
5740     createCallback : function(){
5741         return {
5742             success: this.success,
5743             failure: this.failure,
5744             scope: this,
5745             timeout: (this.form.timeout*1000),
5746             upload: this.form.fileUpload ? this.success : undefined
5747         };
5748     }
5749 };
5750
5751 Roo.form.Action.Submit = function(form, options){
5752     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5753 };
5754
5755 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5756     type : 'submit',
5757
5758     haveProgress : false,
5759     uploadComplete : false,
5760     
5761     // uploadProgress indicator.
5762     uploadProgress : function()
5763     {
5764         if (!this.form.progressUrl) {
5765             return;
5766         }
5767         
5768         if (!this.haveProgress) {
5769             Roo.MessageBox.progress("Uploading", "Uploading");
5770         }
5771         if (this.uploadComplete) {
5772            Roo.MessageBox.hide();
5773            return;
5774         }
5775         
5776         this.haveProgress = true;
5777    
5778         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5779         
5780         var c = new Roo.data.Connection();
5781         c.request({
5782             url : this.form.progressUrl,
5783             params: {
5784                 id : uid
5785             },
5786             method: 'GET',
5787             success : function(req){
5788                //console.log(data);
5789                 var rdata = false;
5790                 var edata;
5791                 try  {
5792                    rdata = Roo.decode(req.responseText)
5793                 } catch (e) {
5794                     Roo.log("Invalid data from server..");
5795                     Roo.log(edata);
5796                     return;
5797                 }
5798                 if (!rdata || !rdata.success) {
5799                     Roo.log(rdata);
5800                     Roo.MessageBox.alert(Roo.encode(rdata));
5801                     return;
5802                 }
5803                 var data = rdata.data;
5804                 
5805                 if (this.uploadComplete) {
5806                    Roo.MessageBox.hide();
5807                    return;
5808                 }
5809                    
5810                 if (data){
5811                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5812                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5813                     );
5814                 }
5815                 this.uploadProgress.defer(2000,this);
5816             },
5817        
5818             failure: function(data) {
5819                 Roo.log('progress url failed ');
5820                 Roo.log(data);
5821             },
5822             scope : this
5823         });
5824            
5825     },
5826     
5827     
5828     run : function()
5829     {
5830         // run get Values on the form, so it syncs any secondary forms.
5831         this.form.getValues();
5832         
5833         var o = this.options;
5834         var method = this.getMethod();
5835         var isPost = method == 'POST';
5836         if(o.clientValidation === false || this.form.isValid()){
5837             
5838             if (this.form.progressUrl) {
5839                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5840                     (new Date() * 1) + '' + Math.random());
5841                     
5842             } 
5843             
5844             
5845             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5846                 form:this.form.el.dom,
5847                 url:this.getUrl(!isPost),
5848                 method: method,
5849                 params:isPost ? this.getParams() : null,
5850                 isUpload: this.form.fileUpload
5851             }));
5852             
5853             this.uploadProgress();
5854
5855         }else if (o.clientValidation !== false){ // client validation failed
5856             this.failureType = Roo.form.Action.CLIENT_INVALID;
5857             this.form.afterAction(this, false);
5858         }
5859     },
5860
5861     success : function(response)
5862     {
5863         this.uploadComplete= true;
5864         if (this.haveProgress) {
5865             Roo.MessageBox.hide();
5866         }
5867         
5868         
5869         var result = this.processResponse(response);
5870         if(result === true || result.success){
5871             this.form.afterAction(this, true);
5872             return;
5873         }
5874         if(result.errors){
5875             this.form.markInvalid(result.errors);
5876             this.failureType = Roo.form.Action.SERVER_INVALID;
5877         }
5878         this.form.afterAction(this, false);
5879     },
5880     failure : function(response)
5881     {
5882         this.uploadComplete= true;
5883         if (this.haveProgress) {
5884             Roo.MessageBox.hide();
5885         }
5886         
5887         this.response = response;
5888         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5889         this.form.afterAction(this, false);
5890     },
5891     
5892     handleResponse : function(response){
5893         if(this.form.errorReader){
5894             var rs = this.form.errorReader.read(response);
5895             var errors = [];
5896             if(rs.records){
5897                 for(var i = 0, len = rs.records.length; i < len; i++) {
5898                     var r = rs.records[i];
5899                     errors[i] = r.data;
5900                 }
5901             }
5902             if(errors.length < 1){
5903                 errors = null;
5904             }
5905             return {
5906                 success : rs.success,
5907                 errors : errors
5908             };
5909         }
5910         var ret = false;
5911         try {
5912             ret = Roo.decode(response.responseText);
5913         } catch (e) {
5914             ret = {
5915                 success: false,
5916                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5917                 errors : []
5918             };
5919         }
5920         return ret;
5921         
5922     }
5923 });
5924
5925
5926 Roo.form.Action.Load = function(form, options){
5927     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5928     this.reader = this.form.reader;
5929 };
5930
5931 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5932     type : 'load',
5933
5934     run : function(){
5935         
5936         Roo.Ajax.request(Roo.apply(
5937                 this.createCallback(), {
5938                     method:this.getMethod(),
5939                     url:this.getUrl(false),
5940                     params:this.getParams()
5941         }));
5942     },
5943
5944     success : function(response){
5945         
5946         var result = this.processResponse(response);
5947         if(result === true || !result.success || !result.data){
5948             this.failureType = Roo.form.Action.LOAD_FAILURE;
5949             this.form.afterAction(this, false);
5950             return;
5951         }
5952         this.form.clearInvalid();
5953         this.form.setValues(result.data);
5954         this.form.afterAction(this, true);
5955     },
5956
5957     handleResponse : function(response){
5958         if(this.form.reader){
5959             var rs = this.form.reader.read(response);
5960             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5961             return {
5962                 success : rs.success,
5963                 data : data
5964             };
5965         }
5966         return Roo.decode(response.responseText);
5967     }
5968 });
5969
5970 Roo.form.Action.ACTION_TYPES = {
5971     'load' : Roo.form.Action.Load,
5972     'submit' : Roo.form.Action.Submit
5973 };/*
5974  * - LGPL
5975  *
5976  * form
5977  * 
5978  */
5979
5980 /**
5981  * @class Roo.bootstrap.Form
5982  * @extends Roo.bootstrap.Component
5983  * Bootstrap Form class
5984  * @cfg {String} method  GET | POST (default POST)
5985  * @cfg {String} labelAlign top | left (default top)
5986   * @cfg {String} align left  | right - for navbars
5987
5988  * 
5989  * @constructor
5990  * Create a new Form
5991  * @param {Object} config The config object
5992  */
5993
5994
5995 Roo.bootstrap.Form = function(config){
5996     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5997     this.addEvents({
5998         /**
5999          * @event clientvalidation
6000          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6001          * @param {Form} this
6002          * @param {Boolean} valid true if the form has passed client-side validation
6003          */
6004         clientvalidation: true,
6005         /**
6006          * @event beforeaction
6007          * Fires before any action is performed. Return false to cancel the action.
6008          * @param {Form} this
6009          * @param {Action} action The action to be performed
6010          */
6011         beforeaction: true,
6012         /**
6013          * @event actionfailed
6014          * Fires when an action fails.
6015          * @param {Form} this
6016          * @param {Action} action The action that failed
6017          */
6018         actionfailed : true,
6019         /**
6020          * @event actioncomplete
6021          * Fires when an action is completed.
6022          * @param {Form} this
6023          * @param {Action} action The action that completed
6024          */
6025         actioncomplete : true
6026     });
6027     
6028 };
6029
6030 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6031       
6032      /**
6033      * @cfg {String} method
6034      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6035      */
6036     method : 'POST',
6037     /**
6038      * @cfg {String} url
6039      * The URL to use for form actions if one isn't supplied in the action options.
6040      */
6041     /**
6042      * @cfg {Boolean} fileUpload
6043      * Set to true if this form is a file upload.
6044      */
6045      
6046     /**
6047      * @cfg {Object} baseParams
6048      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6049      */
6050       
6051     /**
6052      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6053      */
6054     timeout: 30,
6055     /**
6056      * @cfg {Sting} align (left|right) for navbar forms
6057      */
6058     align : 'left',
6059
6060     // private
6061     activeAction : null,
6062  
6063     /**
6064      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6065      * element by passing it or its id or mask the form itself by passing in true.
6066      * @type Mixed
6067      */
6068     waitMsgTarget : false,
6069     
6070      
6071     
6072     /**
6073      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6074      * element by passing it or its id or mask the form itself by passing in true.
6075      * @type Mixed
6076      */
6077     
6078     getAutoCreate : function(){
6079         
6080         var cfg = {
6081             tag: 'form',
6082             method : this.method || 'POST',
6083             id : this.id || Roo.id(),
6084             cls : ''
6085         }
6086         if (this.parent().xtype.match(/^Nav/)) {
6087             cfg.cls = 'navbar-form navbar-' + this.align;
6088             
6089         }
6090         
6091         if (this.labelAlign == 'left' ) {
6092             cfg.cls += ' form-horizontal';
6093         }
6094         
6095         
6096         return cfg;
6097     },
6098     initEvents : function()
6099     {
6100         this.el.on('submit', this.onSubmit, this);
6101         // this was added as random key presses on the form where triggering form submit.
6102         this.el.on('keypress', function(e) {
6103             if (e.getCharCode() != 13) {
6104                 return true;
6105             }
6106             // we might need to allow it for textareas.. and some other items.
6107             // check e.getTarget().
6108             
6109             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6110                 return true;
6111             }
6112         
6113             Roo.log("keypress blocked");
6114             
6115             e.preventDefault();
6116             return false;
6117         });
6118         
6119     },
6120     // private
6121     onSubmit : function(e){
6122         e.stopEvent();
6123     },
6124     
6125      /**
6126      * Returns true if client-side validation on the form is successful.
6127      * @return Boolean
6128      */
6129     isValid : function(){
6130         var items = this.getItems();
6131         var valid = true;
6132         items.each(function(f){
6133            if(!f.validate()){
6134                valid = false;
6135                
6136            }
6137         });
6138         return valid;
6139     },
6140     /**
6141      * Returns true if any fields in this form have changed since their original load.
6142      * @return Boolean
6143      */
6144     isDirty : function(){
6145         var dirty = false;
6146         var items = this.getItems();
6147         items.each(function(f){
6148            if(f.isDirty()){
6149                dirty = true;
6150                return false;
6151            }
6152            return true;
6153         });
6154         return dirty;
6155     },
6156      /**
6157      * Performs a predefined action (submit or load) or custom actions you define on this form.
6158      * @param {String} actionName The name of the action type
6159      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6160      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6161      * accept other config options):
6162      * <pre>
6163 Property          Type             Description
6164 ----------------  ---------------  ----------------------------------------------------------------------------------
6165 url               String           The url for the action (defaults to the form's url)
6166 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6167 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6168 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6169                                    validate the form on the client (defaults to false)
6170      * </pre>
6171      * @return {BasicForm} this
6172      */
6173     doAction : function(action, options){
6174         if(typeof action == 'string'){
6175             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6176         }
6177         if(this.fireEvent('beforeaction', this, action) !== false){
6178             this.beforeAction(action);
6179             action.run.defer(100, action);
6180         }
6181         return this;
6182     },
6183     
6184     // private
6185     beforeAction : function(action){
6186         var o = action.options;
6187         
6188         // not really supported yet.. ??
6189         
6190         //if(this.waitMsgTarget === true){
6191             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6192         //}else if(this.waitMsgTarget){
6193         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6194         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6195         //}else {
6196         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6197        // }
6198          
6199     },
6200
6201     // private
6202     afterAction : function(action, success){
6203         this.activeAction = null;
6204         var o = action.options;
6205         
6206         //if(this.waitMsgTarget === true){
6207             this.el.unmask();
6208         //}else if(this.waitMsgTarget){
6209         //    this.waitMsgTarget.unmask();
6210         //}else{
6211         //    Roo.MessageBox.updateProgress(1);
6212         //    Roo.MessageBox.hide();
6213        // }
6214         // 
6215         if(success){
6216             if(o.reset){
6217                 this.reset();
6218             }
6219             Roo.callback(o.success, o.scope, [this, action]);
6220             this.fireEvent('actioncomplete', this, action);
6221             
6222         }else{
6223             
6224             // failure condition..
6225             // we have a scenario where updates need confirming.
6226             // eg. if a locking scenario exists..
6227             // we look for { errors : { needs_confirm : true }} in the response.
6228             if (
6229                 (typeof(action.result) != 'undefined')  &&
6230                 (typeof(action.result.errors) != 'undefined')  &&
6231                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6232            ){
6233                 var _t = this;
6234                 Roo.log("not supported yet");
6235                  /*
6236                 
6237                 Roo.MessageBox.confirm(
6238                     "Change requires confirmation",
6239                     action.result.errorMsg,
6240                     function(r) {
6241                         if (r != 'yes') {
6242                             return;
6243                         }
6244                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6245                     }
6246                     
6247                 );
6248                 */
6249                 
6250                 
6251                 return;
6252             }
6253             
6254             Roo.callback(o.failure, o.scope, [this, action]);
6255             // show an error message if no failed handler is set..
6256             if (!this.hasListener('actionfailed')) {
6257                 Roo.log("need to add dialog support");
6258                 /*
6259                 Roo.MessageBox.alert("Error",
6260                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6261                         action.result.errorMsg :
6262                         "Saving Failed, please check your entries or try again"
6263                 );
6264                 */
6265             }
6266             
6267             this.fireEvent('actionfailed', this, action);
6268         }
6269         
6270     },
6271     /**
6272      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6273      * @param {String} id The value to search for
6274      * @return Field
6275      */
6276     findField : function(id){
6277         var items = this.getItems();
6278         var field = items.get(id);
6279         if(!field){
6280              items.each(function(f){
6281                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6282                     field = f;
6283                     return false;
6284                 }
6285                 return true;
6286             });
6287         }
6288         return field || null;
6289     },
6290      /**
6291      * Mark fields in this form invalid in bulk.
6292      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6293      * @return {BasicForm} this
6294      */
6295     markInvalid : function(errors){
6296         if(errors instanceof Array){
6297             for(var i = 0, len = errors.length; i < len; i++){
6298                 var fieldError = errors[i];
6299                 var f = this.findField(fieldError.id);
6300                 if(f){
6301                     f.markInvalid(fieldError.msg);
6302                 }
6303             }
6304         }else{
6305             var field, id;
6306             for(id in errors){
6307                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6308                     field.markInvalid(errors[id]);
6309                 }
6310             }
6311         }
6312         //Roo.each(this.childForms || [], function (f) {
6313         //    f.markInvalid(errors);
6314         //});
6315         
6316         return this;
6317     },
6318
6319     /**
6320      * Set values for fields in this form in bulk.
6321      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6322      * @return {BasicForm} this
6323      */
6324     setValues : function(values){
6325         if(values instanceof Array){ // array of objects
6326             for(var i = 0, len = values.length; i < len; i++){
6327                 var v = values[i];
6328                 var f = this.findField(v.id);
6329                 if(f){
6330                     f.setValue(v.value);
6331                     if(this.trackResetOnLoad){
6332                         f.originalValue = f.getValue();
6333                     }
6334                 }
6335             }
6336         }else{ // object hash
6337             var field, id;
6338             for(id in values){
6339                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6340                     
6341                     if (field.setFromData && 
6342                         field.valueField && 
6343                         field.displayField &&
6344                         // combos' with local stores can 
6345                         // be queried via setValue()
6346                         // to set their value..
6347                         (field.store && !field.store.isLocal)
6348                         ) {
6349                         // it's a combo
6350                         var sd = { };
6351                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6352                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6353                         field.setFromData(sd);
6354                         
6355                     } else {
6356                         field.setValue(values[id]);
6357                     }
6358                     
6359                     
6360                     if(this.trackResetOnLoad){
6361                         field.originalValue = field.getValue();
6362                     }
6363                 }
6364             }
6365         }
6366          
6367         //Roo.each(this.childForms || [], function (f) {
6368         //    f.setValues(values);
6369         //});
6370                 
6371         return this;
6372     },
6373
6374     /**
6375      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6376      * they are returned as an array.
6377      * @param {Boolean} asString
6378      * @return {Object}
6379      */
6380     getValues : function(asString){
6381         //if (this.childForms) {
6382             // copy values from the child forms
6383         //    Roo.each(this.childForms, function (f) {
6384         //        this.setValues(f.getValues());
6385         //    }, this);
6386         //}
6387         
6388         
6389         
6390         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6391         if(asString === true){
6392             return fs;
6393         }
6394         return Roo.urlDecode(fs);
6395     },
6396     
6397     /**
6398      * Returns the fields in this form as an object with key/value pairs. 
6399      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6400      * @return {Object}
6401      */
6402     getFieldValues : function(with_hidden)
6403     {
6404         var items = this.getItems();
6405         var ret = {};
6406         items.each(function(f){
6407             if (!f.getName()) {
6408                 return;
6409             }
6410             var v = f.getValue();
6411             if (f.inputType =='radio') {
6412                 if (typeof(ret[f.getName()]) == 'undefined') {
6413                     ret[f.getName()] = ''; // empty..
6414                 }
6415                 
6416                 if (!f.el.dom.checked) {
6417                     return;
6418                     
6419                 }
6420                 v = f.el.dom.value;
6421                 
6422             }
6423             
6424             // not sure if this supported any more..
6425             if ((typeof(v) == 'object') && f.getRawValue) {
6426                 v = f.getRawValue() ; // dates..
6427             }
6428             // combo boxes where name != hiddenName...
6429             if (f.name != f.getName()) {
6430                 ret[f.name] = f.getRawValue();
6431             }
6432             ret[f.getName()] = v;
6433         });
6434         
6435         return ret;
6436     },
6437
6438     /**
6439      * Clears all invalid messages in this form.
6440      * @return {BasicForm} this
6441      */
6442     clearInvalid : function(){
6443         var items = this.getItems();
6444         
6445         items.each(function(f){
6446            f.clearInvalid();
6447         });
6448         
6449         
6450         
6451         return this;
6452     },
6453
6454     /**
6455      * Resets this form.
6456      * @return {BasicForm} this
6457      */
6458     reset : function(){
6459         var items = this.getItems();
6460         items.each(function(f){
6461             f.reset();
6462         });
6463         
6464         Roo.each(this.childForms || [], function (f) {
6465             f.reset();
6466         });
6467        
6468         
6469         return this;
6470     },
6471     getItems : function()
6472     {
6473         var r=new Roo.util.MixedCollection(false, function(o){
6474             return o.id || (o.id = Roo.id());
6475         });
6476         var iter = function(el) {
6477             if (el.inputEl) {
6478                 r.add(el);
6479             }
6480             if (!el.items) {
6481                 return;
6482             }
6483             Roo.each(el.items,function(e) {
6484                 iter(e);
6485             });
6486             
6487             
6488         };
6489         iter(this);
6490         return r;
6491         
6492         
6493         
6494         
6495     }
6496     
6497 });
6498
6499  
6500 /*
6501  * Based on:
6502  * Ext JS Library 1.1.1
6503  * Copyright(c) 2006-2007, Ext JS, LLC.
6504  *
6505  * Originally Released Under LGPL - original licence link has changed is not relivant.
6506  *
6507  * Fork - LGPL
6508  * <script type="text/javascript">
6509  */
6510 /**
6511  * @class Roo.form.VTypes
6512  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6513  * @singleton
6514  */
6515 Roo.form.VTypes = function(){
6516     // closure these in so they are only created once.
6517     var alpha = /^[a-zA-Z_]+$/;
6518     var alphanum = /^[a-zA-Z0-9_]+$/;
6519     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6520     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6521
6522     // All these messages and functions are configurable
6523     return {
6524         /**
6525          * The function used to validate email addresses
6526          * @param {String} value The email address
6527          */
6528         'email' : function(v){
6529             return email.test(v);
6530         },
6531         /**
6532          * The error text to display when the email validation function returns false
6533          * @type String
6534          */
6535         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6536         /**
6537          * The keystroke filter mask to be applied on email input
6538          * @type RegExp
6539          */
6540         'emailMask' : /[a-z0-9_\.\-@]/i,
6541
6542         /**
6543          * The function used to validate URLs
6544          * @param {String} value The URL
6545          */
6546         'url' : function(v){
6547             return url.test(v);
6548         },
6549         /**
6550          * The error text to display when the url validation function returns false
6551          * @type String
6552          */
6553         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6554         
6555         /**
6556          * The function used to validate alpha values
6557          * @param {String} value The value
6558          */
6559         'alpha' : function(v){
6560             return alpha.test(v);
6561         },
6562         /**
6563          * The error text to display when the alpha validation function returns false
6564          * @type String
6565          */
6566         'alphaText' : 'This field should only contain letters and _',
6567         /**
6568          * The keystroke filter mask to be applied on alpha input
6569          * @type RegExp
6570          */
6571         'alphaMask' : /[a-z_]/i,
6572
6573         /**
6574          * The function used to validate alphanumeric values
6575          * @param {String} value The value
6576          */
6577         'alphanum' : function(v){
6578             return alphanum.test(v);
6579         },
6580         /**
6581          * The error text to display when the alphanumeric validation function returns false
6582          * @type String
6583          */
6584         'alphanumText' : 'This field should only contain letters, numbers and _',
6585         /**
6586          * The keystroke filter mask to be applied on alphanumeric input
6587          * @type RegExp
6588          */
6589         'alphanumMask' : /[a-z0-9_]/i
6590     };
6591 }();/*
6592  * - LGPL
6593  *
6594  * Input
6595  * 
6596  */
6597
6598 /**
6599  * @class Roo.bootstrap.Input
6600  * @extends Roo.bootstrap.Component
6601  * Bootstrap Input class
6602  * @cfg {Boolean} disabled is it disabled
6603  * @cfg {String} fieldLabel - the label associated
6604  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6605  * @cfg {String} name name of the input
6606  * @cfg {string} fieldLabel - the label associated
6607  * @cfg {string}  inputType - input / file submit ...
6608  * @cfg {string} placeholder - placeholder to put in text.
6609  * @cfg {string}  before - input group add on before
6610  * @cfg {string} after - input group add on after
6611  * @cfg {string} size - (lg|sm) or leave empty..
6612  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6613  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6614  * @cfg {Number} md colspan out of 12 for computer-sized screens
6615  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6616  * @cfg {string} value default value of the input
6617  * @cfg {Number} labelWidth set the width of label (0-12)
6618  * @cfg {String} labelAlign (top|left)
6619  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6620  * @cfg {String} align (left|center|right) Default left
6621  * 
6622  * 
6623  * @constructor
6624  * Create a new Input
6625  * @param {Object} config The config object
6626  */
6627
6628 Roo.bootstrap.Input = function(config){
6629     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6630    
6631         this.addEvents({
6632             /**
6633              * @event focus
6634              * Fires when this field receives input focus.
6635              * @param {Roo.form.Field} this
6636              */
6637             focus : true,
6638             /**
6639              * @event blur
6640              * Fires when this field loses input focus.
6641              * @param {Roo.form.Field} this
6642              */
6643             blur : true,
6644             /**
6645              * @event specialkey
6646              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6647              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6648              * @param {Roo.form.Field} this
6649              * @param {Roo.EventObject} e The event object
6650              */
6651             specialkey : true,
6652             /**
6653              * @event change
6654              * Fires just before the field blurs if the field value has changed.
6655              * @param {Roo.form.Field} this
6656              * @param {Mixed} newValue The new value
6657              * @param {Mixed} oldValue The original value
6658              */
6659             change : true,
6660             /**
6661              * @event invalid
6662              * Fires after the field has been marked as invalid.
6663              * @param {Roo.form.Field} this
6664              * @param {String} msg The validation message
6665              */
6666             invalid : true,
6667             /**
6668              * @event valid
6669              * Fires after the field has been validated with no errors.
6670              * @param {Roo.form.Field} this
6671              */
6672             valid : true,
6673              /**
6674              * @event keyup
6675              * Fires after the key up
6676              * @param {Roo.form.Field} this
6677              * @param {Roo.EventObject}  e The event Object
6678              */
6679             keyup : true
6680         });
6681 };
6682
6683 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6684      /**
6685      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6686       automatic validation (defaults to "keyup").
6687      */
6688     validationEvent : "keyup",
6689      /**
6690      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6691      */
6692     validateOnBlur : true,
6693     /**
6694      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6695      */
6696     validationDelay : 250,
6697      /**
6698      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6699      */
6700     focusClass : "x-form-focus",  // not needed???
6701     
6702        
6703     /**
6704      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6705      */
6706     invalidClass : "has-error",
6707     
6708     /**
6709      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6710      */
6711     selectOnFocus : false,
6712     
6713      /**
6714      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6715      */
6716     maskRe : null,
6717        /**
6718      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6719      */
6720     vtype : null,
6721     
6722       /**
6723      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6724      */
6725     disableKeyFilter : false,
6726     
6727        /**
6728      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6729      */
6730     disabled : false,
6731      /**
6732      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6733      */
6734     allowBlank : true,
6735     /**
6736      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6737      */
6738     blankText : "This field is required",
6739     
6740      /**
6741      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6742      */
6743     minLength : 0,
6744     /**
6745      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6746      */
6747     maxLength : Number.MAX_VALUE,
6748     /**
6749      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6750      */
6751     minLengthText : "The minimum length for this field is {0}",
6752     /**
6753      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6754      */
6755     maxLengthText : "The maximum length for this field is {0}",
6756   
6757     
6758     /**
6759      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6760      * If available, this function will be called only after the basic validators all return true, and will be passed the
6761      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6762      */
6763     validator : null,
6764     /**
6765      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6766      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6767      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6768      */
6769     regex : null,
6770     /**
6771      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6772      */
6773     regexText : "",
6774     
6775     
6776     
6777     fieldLabel : '',
6778     inputType : 'text',
6779     
6780     name : false,
6781     placeholder: false,
6782     before : false,
6783     after : false,
6784     size : false,
6785     // private
6786     hasFocus : false,
6787     preventMark: false,
6788     isFormField : true,
6789     value : '',
6790     labelWidth : 2,
6791     labelAlign : false,
6792     readOnly : false,
6793     align : false,
6794     formatedValue : false,
6795     
6796     parentLabelAlign : function()
6797     {
6798         var parent = this;
6799         while (parent.parent()) {
6800             parent = parent.parent();
6801             if (typeof(parent.labelAlign) !='undefined') {
6802                 return parent.labelAlign;
6803             }
6804         }
6805         return 'left';
6806         
6807     },
6808     
6809     getAutoCreate : function(){
6810         
6811         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6812         
6813         var id = Roo.id();
6814         
6815         var cfg = {};
6816         
6817         if(this.inputType != 'hidden'){
6818             cfg.cls = 'form-group' //input-group
6819         }
6820         
6821         var input =  {
6822             tag: 'input',
6823             id : id,
6824             type : this.inputType,
6825             value : this.value,
6826             cls : 'form-control',
6827             placeholder : this.placeholder || ''
6828             
6829         };
6830         
6831         if(this.align){
6832             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6833         }
6834         
6835         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6836             input.maxLength = this.maxLength;
6837         }
6838         
6839         if (this.disabled) {
6840             input.disabled=true;
6841         }
6842         
6843         if (this.readOnly) {
6844             input.readonly=true;
6845         }
6846         
6847         if (this.name) {
6848             input.name = this.name;
6849         }
6850         if (this.size) {
6851             input.cls += ' input-' + this.size;
6852         }
6853         var settings=this;
6854         ['xs','sm','md','lg'].map(function(size){
6855             if (settings[size]) {
6856                 cfg.cls += ' col-' + size + '-' + settings[size];
6857             }
6858         });
6859         
6860         var inputblock = input;
6861         
6862         if (this.before || this.after) {
6863             
6864             inputblock = {
6865                 cls : 'input-group',
6866                 cn :  [] 
6867             };
6868             if (this.before && typeof(this.before) == 'string') {
6869                 
6870                 inputblock.cn.push({
6871                     tag :'span',
6872                     cls : 'roo-input-before input-group-addon',
6873                     html : this.before
6874                 });
6875             }
6876             if (this.before && typeof(this.before) == 'object') {
6877                 this.before = Roo.factory(this.before);
6878                 Roo.log(this.before);
6879                 inputblock.cn.push({
6880                     tag :'span',
6881                     cls : 'roo-input-before input-group-' +
6882                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6883                 });
6884             }
6885             
6886             inputblock.cn.push(input);
6887             
6888             if (this.after && typeof(this.after) == 'string') {
6889                 inputblock.cn.push({
6890                     tag :'span',
6891                     cls : 'roo-input-after input-group-addon',
6892                     html : this.after
6893                 });
6894             }
6895             if (this.after && typeof(this.after) == 'object') {
6896                 this.after = Roo.factory(this.after);
6897                 Roo.log(this.after);
6898                 inputblock.cn.push({
6899                     tag :'span',
6900                     cls : 'roo-input-after input-group-' +
6901                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6902                 });
6903             }
6904         };
6905         
6906         if (align ==='left' && this.fieldLabel.length) {
6907                 Roo.log("left and has label");
6908                 cfg.cn = [
6909                     
6910                     {
6911                         tag: 'label',
6912                         'for' :  id,
6913                         cls : 'control-label col-sm-' + this.labelWidth,
6914                         html : this.fieldLabel
6915                         
6916                     },
6917                     {
6918                         cls : "col-sm-" + (12 - this.labelWidth), 
6919                         cn: [
6920                             inputblock
6921                         ]
6922                     }
6923                     
6924                 ];
6925         } else if ( this.fieldLabel.length) {
6926                 Roo.log(" label");
6927                  cfg.cn = [
6928                    
6929                     {
6930                         tag: 'label',
6931                         //cls : 'input-group-addon',
6932                         html : this.fieldLabel
6933                         
6934                     },
6935                     
6936                     inputblock
6937                     
6938                 ];
6939
6940         } else {
6941             
6942                 Roo.log(" no label && no align");
6943                 cfg.cn = [
6944                     
6945                         inputblock
6946                     
6947                 ];
6948                 
6949                 
6950         };
6951         Roo.log('input-parentType: ' + this.parentType);
6952         
6953         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6954            cfg.cls += ' navbar-form';
6955            Roo.log(cfg);
6956         }
6957         
6958         return cfg;
6959         
6960     },
6961     /**
6962      * return the real input element.
6963      */
6964     inputEl: function ()
6965     {
6966         return this.el.select('input.form-control',true).first();
6967     },
6968     setDisabled : function(v)
6969     {
6970         var i  = this.inputEl().dom;
6971         if (!v) {
6972             i.removeAttribute('disabled');
6973             return;
6974             
6975         }
6976         i.setAttribute('disabled','true');
6977     },
6978     initEvents : function()
6979     {
6980         
6981         this.inputEl().on("keydown" , this.fireKey,  this);
6982         this.inputEl().on("focus", this.onFocus,  this);
6983         this.inputEl().on("blur", this.onBlur,  this);
6984         
6985         this.inputEl().relayEvent('keyup', this);
6986
6987         // reference to original value for reset
6988         this.originalValue = this.getValue();
6989         //Roo.form.TextField.superclass.initEvents.call(this);
6990         if(this.validationEvent == 'keyup'){
6991             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6992             this.inputEl().on('keyup', this.filterValidation, this);
6993         }
6994         else if(this.validationEvent !== false){
6995             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6996         }
6997         
6998         if(this.selectOnFocus){
6999             this.on("focus", this.preFocus, this);
7000             
7001         }
7002         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7003             this.inputEl().on("keypress", this.filterKeys, this);
7004         }
7005        /* if(this.grow){
7006             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7007             this.el.on("click", this.autoSize,  this);
7008         }
7009         */
7010         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7011             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7012         }
7013         
7014         if (typeof(this.before) == 'object') {
7015             this.before.render(this.el.select('.roo-input-before',true).first());
7016         }
7017         if (typeof(this.after) == 'object') {
7018             this.after.render(this.el.select('.roo-input-after',true).first());
7019         }
7020         
7021         
7022     },
7023     filterValidation : function(e){
7024         if(!e.isNavKeyPress()){
7025             this.validationTask.delay(this.validationDelay);
7026         }
7027     },
7028      /**
7029      * Validates the field value
7030      * @return {Boolean} True if the value is valid, else false
7031      */
7032     validate : function(){
7033         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7034         if(this.disabled || this.validateValue(this.getRawValue())){
7035             this.clearInvalid();
7036             return true;
7037         }
7038         return false;
7039     },
7040     
7041     
7042     /**
7043      * Validates a value according to the field's validation rules and marks the field as invalid
7044      * if the validation fails
7045      * @param {Mixed} value The value to validate
7046      * @return {Boolean} True if the value is valid, else false
7047      */
7048     validateValue : function(value){
7049         if(value.length < 1)  { // if it's blank
7050              if(this.allowBlank){
7051                 this.clearInvalid();
7052                 return true;
7053              }else{
7054                 this.markInvalid(this.blankText);
7055                 return false;
7056              }
7057         }
7058         if(value.length < this.minLength){
7059             this.markInvalid(String.format(this.minLengthText, this.minLength));
7060             return false;
7061         }
7062         if(value.length > this.maxLength){
7063             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7064             return false;
7065         }
7066         if(this.vtype){
7067             var vt = Roo.form.VTypes;
7068             if(!vt[this.vtype](value, this)){
7069                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7070                 return false;
7071             }
7072         }
7073         if(typeof this.validator == "function"){
7074             var msg = this.validator(value);
7075             if(msg !== true){
7076                 this.markInvalid(msg);
7077                 return false;
7078             }
7079         }
7080         if(this.regex && !this.regex.test(value)){
7081             this.markInvalid(this.regexText);
7082             return false;
7083         }
7084         return true;
7085     },
7086
7087     
7088     
7089      // private
7090     fireKey : function(e){
7091         //Roo.log('field ' + e.getKey());
7092         if(e.isNavKeyPress()){
7093             this.fireEvent("specialkey", this, e);
7094         }
7095     },
7096     focus : function (selectText){
7097         if(this.rendered){
7098             this.inputEl().focus();
7099             if(selectText === true){
7100                 this.inputEl().dom.select();
7101             }
7102         }
7103         return this;
7104     } ,
7105     
7106     onFocus : function(){
7107         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7108            // this.el.addClass(this.focusClass);
7109         }
7110         if(!this.hasFocus){
7111             this.hasFocus = true;
7112             this.startValue = this.getValue();
7113             this.fireEvent("focus", this);
7114         }
7115     },
7116     
7117     beforeBlur : Roo.emptyFn,
7118
7119     
7120     // private
7121     onBlur : function(){
7122         this.beforeBlur();
7123         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7124             //this.el.removeClass(this.focusClass);
7125         }
7126         this.hasFocus = false;
7127         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7128             this.validate();
7129         }
7130         var v = this.getValue();
7131         if(String(v) !== String(this.startValue)){
7132             this.fireEvent('change', this, v, this.startValue);
7133         }
7134         this.fireEvent("blur", this);
7135     },
7136     
7137     /**
7138      * Resets the current field value to the originally loaded value and clears any validation messages
7139      */
7140     reset : function(){
7141         this.setValue(this.originalValue);
7142         this.clearInvalid();
7143     },
7144      /**
7145      * Returns the name of the field
7146      * @return {Mixed} name The name field
7147      */
7148     getName: function(){
7149         return this.name;
7150     },
7151      /**
7152      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7153      * @return {Mixed} value The field value
7154      */
7155     getValue : function(){
7156         
7157         var v = this.inputEl().getValue();
7158         
7159         return v;
7160     },
7161     /**
7162      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7163      * @return {Mixed} value The field value
7164      */
7165     getRawValue : function(){
7166         var v = this.inputEl().getValue();
7167         
7168         return v;
7169     },
7170     
7171     /**
7172      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7173      * @param {Mixed} value The value to set
7174      */
7175     setRawValue : function(v){
7176         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7177     },
7178     
7179     selectText : function(start, end){
7180         var v = this.getRawValue();
7181         if(v.length > 0){
7182             start = start === undefined ? 0 : start;
7183             end = end === undefined ? v.length : end;
7184             var d = this.inputEl().dom;
7185             if(d.setSelectionRange){
7186                 d.setSelectionRange(start, end);
7187             }else if(d.createTextRange){
7188                 var range = d.createTextRange();
7189                 range.moveStart("character", start);
7190                 range.moveEnd("character", v.length-end);
7191                 range.select();
7192             }
7193         }
7194     },
7195     
7196     /**
7197      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7198      * @param {Mixed} value The value to set
7199      */
7200     setValue : function(v){
7201         this.value = v;
7202         if(this.rendered){
7203             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7204             this.validate();
7205         }
7206     },
7207     
7208     /*
7209     processValue : function(value){
7210         if(this.stripCharsRe){
7211             var newValue = value.replace(this.stripCharsRe, '');
7212             if(newValue !== value){
7213                 this.setRawValue(newValue);
7214                 return newValue;
7215             }
7216         }
7217         return value;
7218     },
7219   */
7220     preFocus : function(){
7221         
7222         if(this.selectOnFocus){
7223             this.inputEl().dom.select();
7224         }
7225     },
7226     filterKeys : function(e){
7227         var k = e.getKey();
7228         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7229             return;
7230         }
7231         var c = e.getCharCode(), cc = String.fromCharCode(c);
7232         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7233             return;
7234         }
7235         if(!this.maskRe.test(cc)){
7236             e.stopEvent();
7237         }
7238     },
7239      /**
7240      * Clear any invalid styles/messages for this field
7241      */
7242     clearInvalid : function(){
7243         
7244         if(!this.el || this.preventMark){ // not rendered
7245             return;
7246         }
7247         this.el.removeClass(this.invalidClass);
7248         /*
7249         switch(this.msgTarget){
7250             case 'qtip':
7251                 this.el.dom.qtip = '';
7252                 break;
7253             case 'title':
7254                 this.el.dom.title = '';
7255                 break;
7256             case 'under':
7257                 if(this.errorEl){
7258                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7259                 }
7260                 break;
7261             case 'side':
7262                 if(this.errorIcon){
7263                     this.errorIcon.dom.qtip = '';
7264                     this.errorIcon.hide();
7265                     this.un('resize', this.alignErrorIcon, this);
7266                 }
7267                 break;
7268             default:
7269                 var t = Roo.getDom(this.msgTarget);
7270                 t.innerHTML = '';
7271                 t.style.display = 'none';
7272                 break;
7273         }
7274         */
7275         this.fireEvent('valid', this);
7276     },
7277      /**
7278      * Mark this field as invalid
7279      * @param {String} msg The validation message
7280      */
7281     markInvalid : function(msg){
7282         if(!this.el  || this.preventMark){ // not rendered
7283             return;
7284         }
7285         this.el.addClass(this.invalidClass);
7286         /*
7287         msg = msg || this.invalidText;
7288         switch(this.msgTarget){
7289             case 'qtip':
7290                 this.el.dom.qtip = msg;
7291                 this.el.dom.qclass = 'x-form-invalid-tip';
7292                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7293                     Roo.QuickTips.enable();
7294                 }
7295                 break;
7296             case 'title':
7297                 this.el.dom.title = msg;
7298                 break;
7299             case 'under':
7300                 if(!this.errorEl){
7301                     var elp = this.el.findParent('.x-form-element', 5, true);
7302                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7303                     this.errorEl.setWidth(elp.getWidth(true)-20);
7304                 }
7305                 this.errorEl.update(msg);
7306                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7307                 break;
7308             case 'side':
7309                 if(!this.errorIcon){
7310                     var elp = this.el.findParent('.x-form-element', 5, true);
7311                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7312                 }
7313                 this.alignErrorIcon();
7314                 this.errorIcon.dom.qtip = msg;
7315                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7316                 this.errorIcon.show();
7317                 this.on('resize', this.alignErrorIcon, this);
7318                 break;
7319             default:
7320                 var t = Roo.getDom(this.msgTarget);
7321                 t.innerHTML = msg;
7322                 t.style.display = this.msgDisplay;
7323                 break;
7324         }
7325         */
7326         this.fireEvent('invalid', this, msg);
7327     },
7328     // private
7329     SafariOnKeyDown : function(event)
7330     {
7331         // this is a workaround for a password hang bug on chrome/ webkit.
7332         
7333         var isSelectAll = false;
7334         
7335         if(this.inputEl().dom.selectionEnd > 0){
7336             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7337         }
7338         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7339             event.preventDefault();
7340             this.setValue('');
7341             return;
7342         }
7343         
7344         if(isSelectAll){ // backspace and delete key
7345             
7346             event.preventDefault();
7347             // this is very hacky as keydown always get's upper case.
7348             //
7349             var cc = String.fromCharCode(event.getCharCode());
7350             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7351             
7352         }
7353     },
7354     adjustWidth : function(tag, w){
7355         tag = tag.toLowerCase();
7356         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7357             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7358                 if(tag == 'input'){
7359                     return w + 2;
7360                 }
7361                 if(tag == 'textarea'){
7362                     return w-2;
7363                 }
7364             }else if(Roo.isOpera){
7365                 if(tag == 'input'){
7366                     return w + 2;
7367                 }
7368                 if(tag == 'textarea'){
7369                     return w-2;
7370                 }
7371             }
7372         }
7373         return w;
7374     }
7375     
7376 });
7377
7378  
7379 /*
7380  * - LGPL
7381  *
7382  * Input
7383  * 
7384  */
7385
7386 /**
7387  * @class Roo.bootstrap.TextArea
7388  * @extends Roo.bootstrap.Input
7389  * Bootstrap TextArea class
7390  * @cfg {Number} cols Specifies the visible width of a text area
7391  * @cfg {Number} rows Specifies the visible number of lines in a text area
7392  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7393  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7394  * @cfg {string} html text
7395  * 
7396  * @constructor
7397  * Create a new TextArea
7398  * @param {Object} config The config object
7399  */
7400
7401 Roo.bootstrap.TextArea = function(config){
7402     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7403    
7404 };
7405
7406 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7407      
7408     cols : false,
7409     rows : 5,
7410     readOnly : false,
7411     warp : 'soft',
7412     resize : false,
7413     value: false,
7414     html: false,
7415     
7416     getAutoCreate : function(){
7417         
7418         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7419         
7420         var id = Roo.id();
7421         
7422         var cfg = {};
7423         
7424         var input =  {
7425             tag: 'textarea',
7426             id : id,
7427             warp : this.warp,
7428             rows : this.rows,
7429             value : this.value || '',
7430             html: this.html || '',
7431             cls : 'form-control',
7432             placeholder : this.placeholder || '' 
7433             
7434         };
7435         
7436         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7437             input.maxLength = this.maxLength;
7438         }
7439         
7440         if(this.resize){
7441             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7442         }
7443         
7444         if(this.cols){
7445             input.cols = this.cols;
7446         }
7447         
7448         if (this.readOnly) {
7449             input.readonly = true;
7450         }
7451         
7452         if (this.name) {
7453             input.name = this.name;
7454         }
7455         
7456         if (this.size) {
7457             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7458         }
7459         
7460         var settings=this;
7461         ['xs','sm','md','lg'].map(function(size){
7462             if (settings[size]) {
7463                 cfg.cls += ' col-' + size + '-' + settings[size];
7464             }
7465         });
7466         
7467         var inputblock = input;
7468         
7469         if (this.before || this.after) {
7470             
7471             inputblock = {
7472                 cls : 'input-group',
7473                 cn :  [] 
7474             };
7475             if (this.before) {
7476                 inputblock.cn.push({
7477                     tag :'span',
7478                     cls : 'input-group-addon',
7479                     html : this.before
7480                 });
7481             }
7482             inputblock.cn.push(input);
7483             if (this.after) {
7484                 inputblock.cn.push({
7485                     tag :'span',
7486                     cls : 'input-group-addon',
7487                     html : this.after
7488                 });
7489             }
7490             
7491         }
7492         
7493         if (align ==='left' && this.fieldLabel.length) {
7494                 Roo.log("left and has label");
7495                 cfg.cn = [
7496                     
7497                     {
7498                         tag: 'label',
7499                         'for' :  id,
7500                         cls : 'control-label col-sm-' + this.labelWidth,
7501                         html : this.fieldLabel
7502                         
7503                     },
7504                     {
7505                         cls : "col-sm-" + (12 - this.labelWidth), 
7506                         cn: [
7507                             inputblock
7508                         ]
7509                     }
7510                     
7511                 ];
7512         } else if ( this.fieldLabel.length) {
7513                 Roo.log(" label");
7514                  cfg.cn = [
7515                    
7516                     {
7517                         tag: 'label',
7518                         //cls : 'input-group-addon',
7519                         html : this.fieldLabel
7520                         
7521                     },
7522                     
7523                     inputblock
7524                     
7525                 ];
7526
7527         } else {
7528             
7529                    Roo.log(" no label && no align");
7530                 cfg.cn = [
7531                     
7532                         inputblock
7533                     
7534                 ];
7535                 
7536                 
7537         }
7538         
7539         if (this.disabled) {
7540             input.disabled=true;
7541         }
7542         
7543         return cfg;
7544         
7545     },
7546     /**
7547      * return the real textarea element.
7548      */
7549     inputEl: function ()
7550     {
7551         return this.el.select('textarea.form-control',true).first();
7552     }
7553 });
7554
7555  
7556 /*
7557  * - LGPL
7558  *
7559  * trigger field - base class for combo..
7560  * 
7561  */
7562  
7563 /**
7564  * @class Roo.bootstrap.TriggerField
7565  * @extends Roo.bootstrap.Input
7566  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7567  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7568  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7569  * for which you can provide a custom implementation.  For example:
7570  * <pre><code>
7571 var trigger = new Roo.bootstrap.TriggerField();
7572 trigger.onTriggerClick = myTriggerFn;
7573 trigger.applyTo('my-field');
7574 </code></pre>
7575  *
7576  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7577  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7578  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7579  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7580  * @constructor
7581  * Create a new TriggerField.
7582  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7583  * to the base TextField)
7584  */
7585 Roo.bootstrap.TriggerField = function(config){
7586     this.mimicing = false;
7587     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7588 };
7589
7590 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7591     /**
7592      * @cfg {String} triggerClass A CSS class to apply to the trigger
7593      */
7594      /**
7595      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7596      */
7597     hideTrigger:false,
7598
7599     /** @cfg {Boolean} grow @hide */
7600     /** @cfg {Number} growMin @hide */
7601     /** @cfg {Number} growMax @hide */
7602
7603     /**
7604      * @hide 
7605      * @method
7606      */
7607     autoSize: Roo.emptyFn,
7608     // private
7609     monitorTab : true,
7610     // private
7611     deferHeight : true,
7612
7613     
7614     actionMode : 'wrap',
7615     
7616     
7617     
7618     getAutoCreate : function(){
7619        
7620         var parent = this.parent();
7621         
7622         var align = this.labelAlign || this.parentLabelAlign();
7623         
7624         var id = Roo.id();
7625         
7626         var cfg = {
7627             cls: 'form-group' //input-group
7628         };
7629         
7630         
7631         var input =  {
7632             tag: 'input',
7633             id : id,
7634             type : this.inputType,
7635             cls : 'form-control',
7636             autocomplete: 'off',
7637             placeholder : this.placeholder || '' 
7638             
7639         };
7640         if (this.name) {
7641             input.name = this.name;
7642         }
7643         if (this.size) {
7644             input.cls += ' input-' + this.size;
7645         }
7646         
7647         if (this.disabled) {
7648             input.disabled=true;
7649         }
7650         
7651         var inputblock = input;
7652         
7653         if (this.before || this.after) {
7654             
7655             inputblock = {
7656                 cls : 'input-group',
7657                 cn :  [] 
7658             };
7659             if (this.before) {
7660                 inputblock.cn.push({
7661                     tag :'span',
7662                     cls : 'input-group-addon',
7663                     html : this.before
7664                 });
7665             }
7666             inputblock.cn.push(input);
7667             if (this.after) {
7668                 inputblock.cn.push({
7669                     tag :'span',
7670                     cls : 'input-group-addon',
7671                     html : this.after
7672                 });
7673             }
7674             
7675         };
7676         
7677         var box = {
7678             tag: 'div',
7679             cn: [
7680                 {
7681                     tag: 'input',
7682                     type : 'hidden',
7683                     cls: 'form-hidden-field'
7684                 },
7685                 inputblock
7686             ]
7687             
7688         };
7689         
7690         if(this.multiple){
7691             Roo.log('multiple');
7692             
7693             box = {
7694                 tag: 'div',
7695                 cn: [
7696                     {
7697                         tag: 'input',
7698                         type : 'hidden',
7699                         cls: 'form-hidden-field'
7700                     },
7701                     {
7702                         tag: 'ul',
7703                         cls: 'select2-choices',
7704                         cn:[
7705                             {
7706                                 tag: 'li',
7707                                 cls: 'select2-search-field',
7708                                 cn: [
7709
7710                                     inputblock
7711                                 ]
7712                             }
7713                         ]
7714                     }
7715                 ]
7716             }
7717         };
7718         
7719         var combobox = {
7720             cls: 'select2-container input-group',
7721             cn: [
7722                 box,
7723                 {
7724                     tag: 'ul',
7725                     cls: 'typeahead typeahead-long dropdown-menu',
7726                     style: 'display:none'
7727                 }
7728             ]
7729         };
7730         
7731         if(!this.multiple){
7732             combobox.cn.push({
7733                 tag :'span',
7734                 cls : 'input-group-addon btn dropdown-toggle',
7735                 cn : [
7736                     {
7737                         tag: 'span',
7738                         cls: 'caret'
7739                     },
7740                     {
7741                         tag: 'span',
7742                         cls: 'combobox-clear',
7743                         cn  : [
7744                             {
7745                                 tag : 'i',
7746                                 cls: 'icon-remove'
7747                             }
7748                         ]
7749                     }
7750                 ]
7751
7752             })
7753         }
7754         
7755         if(this.multiple){
7756             combobox.cls += ' select2-container-multi';
7757         }
7758         
7759         if (align ==='left' && this.fieldLabel.length) {
7760             
7761                 Roo.log("left and has label");
7762                 cfg.cn = [
7763                     
7764                     {
7765                         tag: 'label',
7766                         'for' :  id,
7767                         cls : 'control-label col-sm-' + this.labelWidth,
7768                         html : this.fieldLabel
7769                         
7770                     },
7771                     {
7772                         cls : "col-sm-" + (12 - this.labelWidth), 
7773                         cn: [
7774                             combobox
7775                         ]
7776                     }
7777                     
7778                 ];
7779         } else if ( this.fieldLabel.length) {
7780                 Roo.log(" label");
7781                  cfg.cn = [
7782                    
7783                     {
7784                         tag: 'label',
7785                         //cls : 'input-group-addon',
7786                         html : this.fieldLabel
7787                         
7788                     },
7789                     
7790                     combobox
7791                     
7792                 ];
7793
7794         } else {
7795             
7796                 Roo.log(" no label && no align");
7797                 cfg = combobox
7798                      
7799                 
7800         }
7801          
7802         var settings=this;
7803         ['xs','sm','md','lg'].map(function(size){
7804             if (settings[size]) {
7805                 cfg.cls += ' col-' + size + '-' + settings[size];
7806             }
7807         });
7808         
7809         return cfg;
7810         
7811     },
7812     
7813     
7814     
7815     // private
7816     onResize : function(w, h){
7817 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7818 //        if(typeof w == 'number'){
7819 //            var x = w - this.trigger.getWidth();
7820 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7821 //            this.trigger.setStyle('left', x+'px');
7822 //        }
7823     },
7824
7825     // private
7826     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7827
7828     // private
7829     getResizeEl : function(){
7830         return this.inputEl();
7831     },
7832
7833     // private
7834     getPositionEl : function(){
7835         return this.inputEl();
7836     },
7837
7838     // private
7839     alignErrorIcon : function(){
7840         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7841     },
7842
7843     // private
7844     initEvents : function(){
7845         
7846         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7847         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7848         if(!this.multiple){
7849             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7850             if(this.hideTrigger){
7851                 this.trigger.setDisplayed(false);
7852             }
7853             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7854         }
7855         
7856         if(this.multiple){
7857             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7858         }
7859         
7860         //this.trigger.addClassOnOver('x-form-trigger-over');
7861         //this.trigger.addClassOnClick('x-form-trigger-click');
7862         
7863         //if(!this.width){
7864         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7865         //}
7866     },
7867
7868     // private
7869     initTrigger : function(){
7870        
7871     },
7872
7873     // private
7874     onDestroy : function(){
7875         if(this.trigger){
7876             this.trigger.removeAllListeners();
7877           //  this.trigger.remove();
7878         }
7879         //if(this.wrap){
7880         //    this.wrap.remove();
7881         //}
7882         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7883     },
7884
7885     // private
7886     onFocus : function(){
7887         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7888         /*
7889         if(!this.mimicing){
7890             this.wrap.addClass('x-trigger-wrap-focus');
7891             this.mimicing = true;
7892             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7893             if(this.monitorTab){
7894                 this.el.on("keydown", this.checkTab, this);
7895             }
7896         }
7897         */
7898     },
7899
7900     // private
7901     checkTab : function(e){
7902         if(e.getKey() == e.TAB){
7903             this.triggerBlur();
7904         }
7905     },
7906
7907     // private
7908     onBlur : function(){
7909         // do nothing
7910     },
7911
7912     // private
7913     mimicBlur : function(e, t){
7914         /*
7915         if(!this.wrap.contains(t) && this.validateBlur()){
7916             this.triggerBlur();
7917         }
7918         */
7919     },
7920
7921     // private
7922     triggerBlur : function(){
7923         this.mimicing = false;
7924         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7925         if(this.monitorTab){
7926             this.el.un("keydown", this.checkTab, this);
7927         }
7928         //this.wrap.removeClass('x-trigger-wrap-focus');
7929         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7930     },
7931
7932     // private
7933     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7934     validateBlur : function(e, t){
7935         return true;
7936     },
7937
7938     // private
7939     onDisable : function(){
7940         this.inputEl().dom.disabled = true;
7941         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7942         //if(this.wrap){
7943         //    this.wrap.addClass('x-item-disabled');
7944         //}
7945     },
7946
7947     // private
7948     onEnable : function(){
7949         this.inputEl().dom.disabled = false;
7950         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7951         //if(this.wrap){
7952         //    this.el.removeClass('x-item-disabled');
7953         //}
7954     },
7955
7956     // private
7957     onShow : function(){
7958         var ae = this.getActionEl();
7959         
7960         if(ae){
7961             ae.dom.style.display = '';
7962             ae.dom.style.visibility = 'visible';
7963         }
7964     },
7965
7966     // private
7967     
7968     onHide : function(){
7969         var ae = this.getActionEl();
7970         ae.dom.style.display = 'none';
7971     },
7972
7973     /**
7974      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7975      * by an implementing function.
7976      * @method
7977      * @param {EventObject} e
7978      */
7979     onTriggerClick : Roo.emptyFn
7980 });
7981  /*
7982  * Based on:
7983  * Ext JS Library 1.1.1
7984  * Copyright(c) 2006-2007, Ext JS, LLC.
7985  *
7986  * Originally Released Under LGPL - original licence link has changed is not relivant.
7987  *
7988  * Fork - LGPL
7989  * <script type="text/javascript">
7990  */
7991
7992
7993 /**
7994  * @class Roo.data.SortTypes
7995  * @singleton
7996  * Defines the default sorting (casting?) comparison functions used when sorting data.
7997  */
7998 Roo.data.SortTypes = {
7999     /**
8000      * Default sort that does nothing
8001      * @param {Mixed} s The value being converted
8002      * @return {Mixed} The comparison value
8003      */
8004     none : function(s){
8005         return s;
8006     },
8007     
8008     /**
8009      * The regular expression used to strip tags
8010      * @type {RegExp}
8011      * @property
8012      */
8013     stripTagsRE : /<\/?[^>]+>/gi,
8014     
8015     /**
8016      * Strips all HTML tags to sort on text only
8017      * @param {Mixed} s The value being converted
8018      * @return {String} The comparison value
8019      */
8020     asText : function(s){
8021         return String(s).replace(this.stripTagsRE, "");
8022     },
8023     
8024     /**
8025      * Strips all HTML tags to sort on text only - Case insensitive
8026      * @param {Mixed} s The value being converted
8027      * @return {String} The comparison value
8028      */
8029     asUCText : function(s){
8030         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8031     },
8032     
8033     /**
8034      * Case insensitive string
8035      * @param {Mixed} s The value being converted
8036      * @return {String} The comparison value
8037      */
8038     asUCString : function(s) {
8039         return String(s).toUpperCase();
8040     },
8041     
8042     /**
8043      * Date sorting
8044      * @param {Mixed} s The value being converted
8045      * @return {Number} The comparison value
8046      */
8047     asDate : function(s) {
8048         if(!s){
8049             return 0;
8050         }
8051         if(s instanceof Date){
8052             return s.getTime();
8053         }
8054         return Date.parse(String(s));
8055     },
8056     
8057     /**
8058      * Float sorting
8059      * @param {Mixed} s The value being converted
8060      * @return {Float} The comparison value
8061      */
8062     asFloat : function(s) {
8063         var val = parseFloat(String(s).replace(/,/g, ""));
8064         if(isNaN(val)) val = 0;
8065         return val;
8066     },
8067     
8068     /**
8069      * Integer sorting
8070      * @param {Mixed} s The value being converted
8071      * @return {Number} The comparison value
8072      */
8073     asInt : function(s) {
8074         var val = parseInt(String(s).replace(/,/g, ""));
8075         if(isNaN(val)) val = 0;
8076         return val;
8077     }
8078 };/*
8079  * Based on:
8080  * Ext JS Library 1.1.1
8081  * Copyright(c) 2006-2007, Ext JS, LLC.
8082  *
8083  * Originally Released Under LGPL - original licence link has changed is not relivant.
8084  *
8085  * Fork - LGPL
8086  * <script type="text/javascript">
8087  */
8088
8089 /**
8090 * @class Roo.data.Record
8091  * Instances of this class encapsulate both record <em>definition</em> information, and record
8092  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8093  * to access Records cached in an {@link Roo.data.Store} object.<br>
8094  * <p>
8095  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8096  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8097  * objects.<br>
8098  * <p>
8099  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8100  * @constructor
8101  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8102  * {@link #create}. The parameters are the same.
8103  * @param {Array} data An associative Array of data values keyed by the field name.
8104  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8105  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8106  * not specified an integer id is generated.
8107  */
8108 Roo.data.Record = function(data, id){
8109     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8110     this.data = data;
8111 };
8112
8113 /**
8114  * Generate a constructor for a specific record layout.
8115  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8116  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8117  * Each field definition object may contain the following properties: <ul>
8118  * <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,
8119  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8120  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8121  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8122  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8123  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8124  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8125  * this may be omitted.</p></li>
8126  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8127  * <ul><li>auto (Default, implies no conversion)</li>
8128  * <li>string</li>
8129  * <li>int</li>
8130  * <li>float</li>
8131  * <li>boolean</li>
8132  * <li>date</li></ul></p></li>
8133  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8134  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8135  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8136  * by the Reader into an object that will be stored in the Record. It is passed the
8137  * following parameters:<ul>
8138  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8139  * </ul></p></li>
8140  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8141  * </ul>
8142  * <br>usage:<br><pre><code>
8143 var TopicRecord = Roo.data.Record.create(
8144     {name: 'title', mapping: 'topic_title'},
8145     {name: 'author', mapping: 'username'},
8146     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8147     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8148     {name: 'lastPoster', mapping: 'user2'},
8149     {name: 'excerpt', mapping: 'post_text'}
8150 );
8151
8152 var myNewRecord = new TopicRecord({
8153     title: 'Do my job please',
8154     author: 'noobie',
8155     totalPosts: 1,
8156     lastPost: new Date(),
8157     lastPoster: 'Animal',
8158     excerpt: 'No way dude!'
8159 });
8160 myStore.add(myNewRecord);
8161 </code></pre>
8162  * @method create
8163  * @static
8164  */
8165 Roo.data.Record.create = function(o){
8166     var f = function(){
8167         f.superclass.constructor.apply(this, arguments);
8168     };
8169     Roo.extend(f, Roo.data.Record);
8170     var p = f.prototype;
8171     p.fields = new Roo.util.MixedCollection(false, function(field){
8172         return field.name;
8173     });
8174     for(var i = 0, len = o.length; i < len; i++){
8175         p.fields.add(new Roo.data.Field(o[i]));
8176     }
8177     f.getField = function(name){
8178         return p.fields.get(name);  
8179     };
8180     return f;
8181 };
8182
8183 Roo.data.Record.AUTO_ID = 1000;
8184 Roo.data.Record.EDIT = 'edit';
8185 Roo.data.Record.REJECT = 'reject';
8186 Roo.data.Record.COMMIT = 'commit';
8187
8188 Roo.data.Record.prototype = {
8189     /**
8190      * Readonly flag - true if this record has been modified.
8191      * @type Boolean
8192      */
8193     dirty : false,
8194     editing : false,
8195     error: null,
8196     modified: null,
8197
8198     // private
8199     join : function(store){
8200         this.store = store;
8201     },
8202
8203     /**
8204      * Set the named field to the specified value.
8205      * @param {String} name The name of the field to set.
8206      * @param {Object} value The value to set the field to.
8207      */
8208     set : function(name, value){
8209         if(this.data[name] == value){
8210             return;
8211         }
8212         this.dirty = true;
8213         if(!this.modified){
8214             this.modified = {};
8215         }
8216         if(typeof this.modified[name] == 'undefined'){
8217             this.modified[name] = this.data[name];
8218         }
8219         this.data[name] = value;
8220         if(!this.editing && this.store){
8221             this.store.afterEdit(this);
8222         }       
8223     },
8224
8225     /**
8226      * Get the value of the named field.
8227      * @param {String} name The name of the field to get the value of.
8228      * @return {Object} The value of the field.
8229      */
8230     get : function(name){
8231         return this.data[name]; 
8232     },
8233
8234     // private
8235     beginEdit : function(){
8236         this.editing = true;
8237         this.modified = {}; 
8238     },
8239
8240     // private
8241     cancelEdit : function(){
8242         this.editing = false;
8243         delete this.modified;
8244     },
8245
8246     // private
8247     endEdit : function(){
8248         this.editing = false;
8249         if(this.dirty && this.store){
8250             this.store.afterEdit(this);
8251         }
8252     },
8253
8254     /**
8255      * Usually called by the {@link Roo.data.Store} which owns the Record.
8256      * Rejects all changes made to the Record since either creation, or the last commit operation.
8257      * Modified fields are reverted to their original values.
8258      * <p>
8259      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8260      * of reject operations.
8261      */
8262     reject : function(){
8263         var m = this.modified;
8264         for(var n in m){
8265             if(typeof m[n] != "function"){
8266                 this.data[n] = m[n];
8267             }
8268         }
8269         this.dirty = false;
8270         delete this.modified;
8271         this.editing = false;
8272         if(this.store){
8273             this.store.afterReject(this);
8274         }
8275     },
8276
8277     /**
8278      * Usually called by the {@link Roo.data.Store} which owns the Record.
8279      * Commits all changes made to the Record since either creation, or the last commit operation.
8280      * <p>
8281      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8282      * of commit operations.
8283      */
8284     commit : function(){
8285         this.dirty = false;
8286         delete this.modified;
8287         this.editing = false;
8288         if(this.store){
8289             this.store.afterCommit(this);
8290         }
8291     },
8292
8293     // private
8294     hasError : function(){
8295         return this.error != null;
8296     },
8297
8298     // private
8299     clearError : function(){
8300         this.error = null;
8301     },
8302
8303     /**
8304      * Creates a copy of this record.
8305      * @param {String} id (optional) A new record id if you don't want to use this record's id
8306      * @return {Record}
8307      */
8308     copy : function(newId) {
8309         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8310     }
8311 };/*
8312  * Based on:
8313  * Ext JS Library 1.1.1
8314  * Copyright(c) 2006-2007, Ext JS, LLC.
8315  *
8316  * Originally Released Under LGPL - original licence link has changed is not relivant.
8317  *
8318  * Fork - LGPL
8319  * <script type="text/javascript">
8320  */
8321
8322
8323
8324 /**
8325  * @class Roo.data.Store
8326  * @extends Roo.util.Observable
8327  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8328  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8329  * <p>
8330  * 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
8331  * has no knowledge of the format of the data returned by the Proxy.<br>
8332  * <p>
8333  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8334  * instances from the data object. These records are cached and made available through accessor functions.
8335  * @constructor
8336  * Creates a new Store.
8337  * @param {Object} config A config object containing the objects needed for the Store to access data,
8338  * and read the data into Records.
8339  */
8340 Roo.data.Store = function(config){
8341     this.data = new Roo.util.MixedCollection(false);
8342     this.data.getKey = function(o){
8343         return o.id;
8344     };
8345     this.baseParams = {};
8346     // private
8347     this.paramNames = {
8348         "start" : "start",
8349         "limit" : "limit",
8350         "sort" : "sort",
8351         "dir" : "dir",
8352         "multisort" : "_multisort"
8353     };
8354
8355     if(config && config.data){
8356         this.inlineData = config.data;
8357         delete config.data;
8358     }
8359
8360     Roo.apply(this, config);
8361     
8362     if(this.reader){ // reader passed
8363         this.reader = Roo.factory(this.reader, Roo.data);
8364         this.reader.xmodule = this.xmodule || false;
8365         if(!this.recordType){
8366             this.recordType = this.reader.recordType;
8367         }
8368         if(this.reader.onMetaChange){
8369             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8370         }
8371     }
8372
8373     if(this.recordType){
8374         this.fields = this.recordType.prototype.fields;
8375     }
8376     this.modified = [];
8377
8378     this.addEvents({
8379         /**
8380          * @event datachanged
8381          * Fires when the data cache has changed, and a widget which is using this Store
8382          * as a Record cache should refresh its view.
8383          * @param {Store} this
8384          */
8385         datachanged : true,
8386         /**
8387          * @event metachange
8388          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8389          * @param {Store} this
8390          * @param {Object} meta The JSON metadata
8391          */
8392         metachange : true,
8393         /**
8394          * @event add
8395          * Fires when Records have been added to the Store
8396          * @param {Store} this
8397          * @param {Roo.data.Record[]} records The array of Records added
8398          * @param {Number} index The index at which the record(s) were added
8399          */
8400         add : true,
8401         /**
8402          * @event remove
8403          * Fires when a Record has been removed from the Store
8404          * @param {Store} this
8405          * @param {Roo.data.Record} record The Record that was removed
8406          * @param {Number} index The index at which the record was removed
8407          */
8408         remove : true,
8409         /**
8410          * @event update
8411          * Fires when a Record has been updated
8412          * @param {Store} this
8413          * @param {Roo.data.Record} record The Record that was updated
8414          * @param {String} operation The update operation being performed.  Value may be one of:
8415          * <pre><code>
8416  Roo.data.Record.EDIT
8417  Roo.data.Record.REJECT
8418  Roo.data.Record.COMMIT
8419          * </code></pre>
8420          */
8421         update : true,
8422         /**
8423          * @event clear
8424          * Fires when the data cache has been cleared.
8425          * @param {Store} this
8426          */
8427         clear : true,
8428         /**
8429          * @event beforeload
8430          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8431          * the load action will be canceled.
8432          * @param {Store} this
8433          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8434          */
8435         beforeload : true,
8436         /**
8437          * @event beforeloadadd
8438          * Fires after a new set of Records has been loaded.
8439          * @param {Store} this
8440          * @param {Roo.data.Record[]} records The Records that were loaded
8441          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8442          */
8443         beforeloadadd : true,
8444         /**
8445          * @event load
8446          * Fires after a new set of Records has been loaded, before they are added to the store.
8447          * @param {Store} this
8448          * @param {Roo.data.Record[]} records The Records that were loaded
8449          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8450          * @params {Object} return from reader
8451          */
8452         load : true,
8453         /**
8454          * @event loadexception
8455          * Fires if an exception occurs in the Proxy during loading.
8456          * Called with the signature of the Proxy's "loadexception" event.
8457          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8458          * 
8459          * @param {Proxy} 
8460          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8461          * @param {Object} load options 
8462          * @param {Object} jsonData from your request (normally this contains the Exception)
8463          */
8464         loadexception : true
8465     });
8466     
8467     if(this.proxy){
8468         this.proxy = Roo.factory(this.proxy, Roo.data);
8469         this.proxy.xmodule = this.xmodule || false;
8470         this.relayEvents(this.proxy,  ["loadexception"]);
8471     }
8472     this.sortToggle = {};
8473     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8474
8475     Roo.data.Store.superclass.constructor.call(this);
8476
8477     if(this.inlineData){
8478         this.loadData(this.inlineData);
8479         delete this.inlineData;
8480     }
8481 };
8482
8483 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8484      /**
8485     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8486     * without a remote query - used by combo/forms at present.
8487     */
8488     
8489     /**
8490     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8491     */
8492     /**
8493     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8494     */
8495     /**
8496     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8497     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8498     */
8499     /**
8500     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8501     * on any HTTP request
8502     */
8503     /**
8504     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8505     */
8506     /**
8507     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8508     */
8509     multiSort: false,
8510     /**
8511     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8512     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8513     */
8514     remoteSort : false,
8515
8516     /**
8517     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8518      * loaded or when a record is removed. (defaults to false).
8519     */
8520     pruneModifiedRecords : false,
8521
8522     // private
8523     lastOptions : null,
8524
8525     /**
8526      * Add Records to the Store and fires the add event.
8527      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8528      */
8529     add : function(records){
8530         records = [].concat(records);
8531         for(var i = 0, len = records.length; i < len; i++){
8532             records[i].join(this);
8533         }
8534         var index = this.data.length;
8535         this.data.addAll(records);
8536         this.fireEvent("add", this, records, index);
8537     },
8538
8539     /**
8540      * Remove a Record from the Store and fires the remove event.
8541      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8542      */
8543     remove : function(record){
8544         var index = this.data.indexOf(record);
8545         this.data.removeAt(index);
8546         if(this.pruneModifiedRecords){
8547             this.modified.remove(record);
8548         }
8549         this.fireEvent("remove", this, record, index);
8550     },
8551
8552     /**
8553      * Remove all Records from the Store and fires the clear event.
8554      */
8555     removeAll : function(){
8556         this.data.clear();
8557         if(this.pruneModifiedRecords){
8558             this.modified = [];
8559         }
8560         this.fireEvent("clear", this);
8561     },
8562
8563     /**
8564      * Inserts Records to the Store at the given index and fires the add event.
8565      * @param {Number} index The start index at which to insert the passed Records.
8566      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8567      */
8568     insert : function(index, records){
8569         records = [].concat(records);
8570         for(var i = 0, len = records.length; i < len; i++){
8571             this.data.insert(index, records[i]);
8572             records[i].join(this);
8573         }
8574         this.fireEvent("add", this, records, index);
8575     },
8576
8577     /**
8578      * Get the index within the cache of the passed Record.
8579      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8580      * @return {Number} The index of the passed Record. Returns -1 if not found.
8581      */
8582     indexOf : function(record){
8583         return this.data.indexOf(record);
8584     },
8585
8586     /**
8587      * Get the index within the cache of the Record with the passed id.
8588      * @param {String} id The id of the Record to find.
8589      * @return {Number} The index of the Record. Returns -1 if not found.
8590      */
8591     indexOfId : function(id){
8592         return this.data.indexOfKey(id);
8593     },
8594
8595     /**
8596      * Get the Record with the specified id.
8597      * @param {String} id The id of the Record to find.
8598      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8599      */
8600     getById : function(id){
8601         return this.data.key(id);
8602     },
8603
8604     /**
8605      * Get the Record at the specified index.
8606      * @param {Number} index The index of the Record to find.
8607      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8608      */
8609     getAt : function(index){
8610         return this.data.itemAt(index);
8611     },
8612
8613     /**
8614      * Returns a range of Records between specified indices.
8615      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8616      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8617      * @return {Roo.data.Record[]} An array of Records
8618      */
8619     getRange : function(start, end){
8620         return this.data.getRange(start, end);
8621     },
8622
8623     // private
8624     storeOptions : function(o){
8625         o = Roo.apply({}, o);
8626         delete o.callback;
8627         delete o.scope;
8628         this.lastOptions = o;
8629     },
8630
8631     /**
8632      * Loads the Record cache from the configured Proxy using the configured Reader.
8633      * <p>
8634      * If using remote paging, then the first load call must specify the <em>start</em>
8635      * and <em>limit</em> properties in the options.params property to establish the initial
8636      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8637      * <p>
8638      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8639      * and this call will return before the new data has been loaded. Perform any post-processing
8640      * in a callback function, or in a "load" event handler.</strong>
8641      * <p>
8642      * @param {Object} options An object containing properties which control loading options:<ul>
8643      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8644      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8645      * passed the following arguments:<ul>
8646      * <li>r : Roo.data.Record[]</li>
8647      * <li>options: Options object from the load call</li>
8648      * <li>success: Boolean success indicator</li></ul></li>
8649      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8650      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8651      * </ul>
8652      */
8653     load : function(options){
8654         options = options || {};
8655         if(this.fireEvent("beforeload", this, options) !== false){
8656             this.storeOptions(options);
8657             var p = Roo.apply(options.params || {}, this.baseParams);
8658             // if meta was not loaded from remote source.. try requesting it.
8659             if (!this.reader.metaFromRemote) {
8660                 p._requestMeta = 1;
8661             }
8662             if(this.sortInfo && this.remoteSort){
8663                 var pn = this.paramNames;
8664                 p[pn["sort"]] = this.sortInfo.field;
8665                 p[pn["dir"]] = this.sortInfo.direction;
8666             }
8667             if (this.multiSort) {
8668                 var pn = this.paramNames;
8669                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8670             }
8671             
8672             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8673         }
8674     },
8675
8676     /**
8677      * Reloads the Record cache from the configured Proxy using the configured Reader and
8678      * the options from the last load operation performed.
8679      * @param {Object} options (optional) An object containing properties which may override the options
8680      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8681      * the most recently used options are reused).
8682      */
8683     reload : function(options){
8684         this.load(Roo.applyIf(options||{}, this.lastOptions));
8685     },
8686
8687     // private
8688     // Called as a callback by the Reader during a load operation.
8689     loadRecords : function(o, options, success){
8690         if(!o || success === false){
8691             if(success !== false){
8692                 this.fireEvent("load", this, [], options, o);
8693             }
8694             if(options.callback){
8695                 options.callback.call(options.scope || this, [], options, false);
8696             }
8697             return;
8698         }
8699         // if data returned failure - throw an exception.
8700         if (o.success === false) {
8701             // show a message if no listener is registered.
8702             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8703                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8704             }
8705             // loadmask wil be hooked into this..
8706             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8707             return;
8708         }
8709         var r = o.records, t = o.totalRecords || r.length;
8710         
8711         this.fireEvent("beforeloadadd", this, r, options, o);
8712         
8713         if(!options || options.add !== true){
8714             if(this.pruneModifiedRecords){
8715                 this.modified = [];
8716             }
8717             for(var i = 0, len = r.length; i < len; i++){
8718                 r[i].join(this);
8719             }
8720             if(this.snapshot){
8721                 this.data = this.snapshot;
8722                 delete this.snapshot;
8723             }
8724             this.data.clear();
8725             this.data.addAll(r);
8726             this.totalLength = t;
8727             this.applySort();
8728             this.fireEvent("datachanged", this);
8729         }else{
8730             this.totalLength = Math.max(t, this.data.length+r.length);
8731             this.add(r);
8732         }
8733         this.fireEvent("load", this, r, options, o);
8734         if(options.callback){
8735             options.callback.call(options.scope || this, r, options, true);
8736         }
8737     },
8738
8739
8740     /**
8741      * Loads data from a passed data block. A Reader which understands the format of the data
8742      * must have been configured in the constructor.
8743      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8744      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8745      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8746      */
8747     loadData : function(o, append){
8748         var r = this.reader.readRecords(o);
8749         this.loadRecords(r, {add: append}, true);
8750     },
8751
8752     /**
8753      * Gets the number of cached records.
8754      * <p>
8755      * <em>If using paging, this may not be the total size of the dataset. If the data object
8756      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8757      * the data set size</em>
8758      */
8759     getCount : function(){
8760         return this.data.length || 0;
8761     },
8762
8763     /**
8764      * Gets the total number of records in the dataset as returned by the server.
8765      * <p>
8766      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8767      * the dataset size</em>
8768      */
8769     getTotalCount : function(){
8770         return this.totalLength || 0;
8771     },
8772
8773     /**
8774      * Returns the sort state of the Store as an object with two properties:
8775      * <pre><code>
8776  field {String} The name of the field by which the Records are sorted
8777  direction {String} The sort order, "ASC" or "DESC"
8778      * </code></pre>
8779      */
8780     getSortState : function(){
8781         return this.sortInfo;
8782     },
8783
8784     // private
8785     applySort : function(){
8786         if(this.sortInfo && !this.remoteSort){
8787             var s = this.sortInfo, f = s.field;
8788             var st = this.fields.get(f).sortType;
8789             var fn = function(r1, r2){
8790                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8791                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8792             };
8793             this.data.sort(s.direction, fn);
8794             if(this.snapshot && this.snapshot != this.data){
8795                 this.snapshot.sort(s.direction, fn);
8796             }
8797         }
8798     },
8799
8800     /**
8801      * Sets the default sort column and order to be used by the next load operation.
8802      * @param {String} fieldName The name of the field to sort by.
8803      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8804      */
8805     setDefaultSort : function(field, dir){
8806         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8807     },
8808
8809     /**
8810      * Sort the Records.
8811      * If remote sorting is used, the sort is performed on the server, and the cache is
8812      * reloaded. If local sorting is used, the cache is sorted internally.
8813      * @param {String} fieldName The name of the field to sort by.
8814      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8815      */
8816     sort : function(fieldName, dir){
8817         var f = this.fields.get(fieldName);
8818         if(!dir){
8819             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8820             
8821             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8822                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8823             }else{
8824                 dir = f.sortDir;
8825             }
8826         }
8827         this.sortToggle[f.name] = dir;
8828         this.sortInfo = {field: f.name, direction: dir};
8829         if(!this.remoteSort){
8830             this.applySort();
8831             this.fireEvent("datachanged", this);
8832         }else{
8833             this.load(this.lastOptions);
8834         }
8835     },
8836
8837     /**
8838      * Calls the specified function for each of the Records in the cache.
8839      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8840      * Returning <em>false</em> aborts and exits the iteration.
8841      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8842      */
8843     each : function(fn, scope){
8844         this.data.each(fn, scope);
8845     },
8846
8847     /**
8848      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8849      * (e.g., during paging).
8850      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8851      */
8852     getModifiedRecords : function(){
8853         return this.modified;
8854     },
8855
8856     // private
8857     createFilterFn : function(property, value, anyMatch){
8858         if(!value.exec){ // not a regex
8859             value = String(value);
8860             if(value.length == 0){
8861                 return false;
8862             }
8863             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8864         }
8865         return function(r){
8866             return value.test(r.data[property]);
8867         };
8868     },
8869
8870     /**
8871      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8872      * @param {String} property A field on your records
8873      * @param {Number} start The record index to start at (defaults to 0)
8874      * @param {Number} end The last record index to include (defaults to length - 1)
8875      * @return {Number} The sum
8876      */
8877     sum : function(property, start, end){
8878         var rs = this.data.items, v = 0;
8879         start = start || 0;
8880         end = (end || end === 0) ? end : rs.length-1;
8881
8882         for(var i = start; i <= end; i++){
8883             v += (rs[i].data[property] || 0);
8884         }
8885         return v;
8886     },
8887
8888     /**
8889      * Filter the records by a specified property.
8890      * @param {String} field A field on your records
8891      * @param {String/RegExp} value Either a string that the field
8892      * should start with or a RegExp to test against the field
8893      * @param {Boolean} anyMatch True to match any part not just the beginning
8894      */
8895     filter : function(property, value, anyMatch){
8896         var fn = this.createFilterFn(property, value, anyMatch);
8897         return fn ? this.filterBy(fn) : this.clearFilter();
8898     },
8899
8900     /**
8901      * Filter by a function. The specified function will be called with each
8902      * record in this data source. If the function returns true the record is included,
8903      * otherwise it is filtered.
8904      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8905      * @param {Object} scope (optional) The scope of the function (defaults to this)
8906      */
8907     filterBy : function(fn, scope){
8908         this.snapshot = this.snapshot || this.data;
8909         this.data = this.queryBy(fn, scope||this);
8910         this.fireEvent("datachanged", this);
8911     },
8912
8913     /**
8914      * Query the records by a specified property.
8915      * @param {String} field A field on your records
8916      * @param {String/RegExp} value Either a string that the field
8917      * should start with or a RegExp to test against the field
8918      * @param {Boolean} anyMatch True to match any part not just the beginning
8919      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8920      */
8921     query : function(property, value, anyMatch){
8922         var fn = this.createFilterFn(property, value, anyMatch);
8923         return fn ? this.queryBy(fn) : this.data.clone();
8924     },
8925
8926     /**
8927      * Query by a function. The specified function will be called with each
8928      * record in this data source. If the function returns true the record is included
8929      * in the results.
8930      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8931      * @param {Object} scope (optional) The scope of the function (defaults to this)
8932       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8933      **/
8934     queryBy : function(fn, scope){
8935         var data = this.snapshot || this.data;
8936         return data.filterBy(fn, scope||this);
8937     },
8938
8939     /**
8940      * Collects unique values for a particular dataIndex from this store.
8941      * @param {String} dataIndex The property to collect
8942      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8943      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8944      * @return {Array} An array of the unique values
8945      **/
8946     collect : function(dataIndex, allowNull, bypassFilter){
8947         var d = (bypassFilter === true && this.snapshot) ?
8948                 this.snapshot.items : this.data.items;
8949         var v, sv, r = [], l = {};
8950         for(var i = 0, len = d.length; i < len; i++){
8951             v = d[i].data[dataIndex];
8952             sv = String(v);
8953             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8954                 l[sv] = true;
8955                 r[r.length] = v;
8956             }
8957         }
8958         return r;
8959     },
8960
8961     /**
8962      * Revert to a view of the Record cache with no filtering applied.
8963      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
8964      */
8965     clearFilter : function(suppressEvent){
8966         if(this.snapshot && this.snapshot != this.data){
8967             this.data = this.snapshot;
8968             delete this.snapshot;
8969             if(suppressEvent !== true){
8970                 this.fireEvent("datachanged", this);
8971             }
8972         }
8973     },
8974
8975     // private
8976     afterEdit : function(record){
8977         if(this.modified.indexOf(record) == -1){
8978             this.modified.push(record);
8979         }
8980         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8981     },
8982     
8983     // private
8984     afterReject : function(record){
8985         this.modified.remove(record);
8986         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8987     },
8988
8989     // private
8990     afterCommit : function(record){
8991         this.modified.remove(record);
8992         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8993     },
8994
8995     /**
8996      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8997      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8998      */
8999     commitChanges : function(){
9000         var m = this.modified.slice(0);
9001         this.modified = [];
9002         for(var i = 0, len = m.length; i < len; i++){
9003             m[i].commit();
9004         }
9005     },
9006
9007     /**
9008      * Cancel outstanding changes on all changed records.
9009      */
9010     rejectChanges : function(){
9011         var m = this.modified.slice(0);
9012         this.modified = [];
9013         for(var i = 0, len = m.length; i < len; i++){
9014             m[i].reject();
9015         }
9016     },
9017
9018     onMetaChange : function(meta, rtype, o){
9019         this.recordType = rtype;
9020         this.fields = rtype.prototype.fields;
9021         delete this.snapshot;
9022         this.sortInfo = meta.sortInfo || this.sortInfo;
9023         this.modified = [];
9024         this.fireEvent('metachange', this, this.reader.meta);
9025     },
9026     
9027     moveIndex : function(data, type)
9028     {
9029         var index = this.indexOf(data);
9030         
9031         var newIndex = index + type;
9032         
9033         this.remove(data);
9034         
9035         this.insert(newIndex, data);
9036         
9037     }
9038 });/*
9039  * Based on:
9040  * Ext JS Library 1.1.1
9041  * Copyright(c) 2006-2007, Ext JS, LLC.
9042  *
9043  * Originally Released Under LGPL - original licence link has changed is not relivant.
9044  *
9045  * Fork - LGPL
9046  * <script type="text/javascript">
9047  */
9048
9049 /**
9050  * @class Roo.data.SimpleStore
9051  * @extends Roo.data.Store
9052  * Small helper class to make creating Stores from Array data easier.
9053  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9054  * @cfg {Array} fields An array of field definition objects, or field name strings.
9055  * @cfg {Array} data The multi-dimensional array of data
9056  * @constructor
9057  * @param {Object} config
9058  */
9059 Roo.data.SimpleStore = function(config){
9060     Roo.data.SimpleStore.superclass.constructor.call(this, {
9061         isLocal : true,
9062         reader: new Roo.data.ArrayReader({
9063                 id: config.id
9064             },
9065             Roo.data.Record.create(config.fields)
9066         ),
9067         proxy : new Roo.data.MemoryProxy(config.data)
9068     });
9069     this.load();
9070 };
9071 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9072  * Based on:
9073  * Ext JS Library 1.1.1
9074  * Copyright(c) 2006-2007, Ext JS, LLC.
9075  *
9076  * Originally Released Under LGPL - original licence link has changed is not relivant.
9077  *
9078  * Fork - LGPL
9079  * <script type="text/javascript">
9080  */
9081
9082 /**
9083 /**
9084  * @extends Roo.data.Store
9085  * @class Roo.data.JsonStore
9086  * Small helper class to make creating Stores for JSON data easier. <br/>
9087 <pre><code>
9088 var store = new Roo.data.JsonStore({
9089     url: 'get-images.php',
9090     root: 'images',
9091     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9092 });
9093 </code></pre>
9094  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9095  * JsonReader and HttpProxy (unless inline data is provided).</b>
9096  * @cfg {Array} fields An array of field definition objects, or field name strings.
9097  * @constructor
9098  * @param {Object} config
9099  */
9100 Roo.data.JsonStore = function(c){
9101     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9102         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9103         reader: new Roo.data.JsonReader(c, c.fields)
9104     }));
9105 };
9106 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9107  * Based on:
9108  * Ext JS Library 1.1.1
9109  * Copyright(c) 2006-2007, Ext JS, LLC.
9110  *
9111  * Originally Released Under LGPL - original licence link has changed is not relivant.
9112  *
9113  * Fork - LGPL
9114  * <script type="text/javascript">
9115  */
9116
9117  
9118 Roo.data.Field = function(config){
9119     if(typeof config == "string"){
9120         config = {name: config};
9121     }
9122     Roo.apply(this, config);
9123     
9124     if(!this.type){
9125         this.type = "auto";
9126     }
9127     
9128     var st = Roo.data.SortTypes;
9129     // named sortTypes are supported, here we look them up
9130     if(typeof this.sortType == "string"){
9131         this.sortType = st[this.sortType];
9132     }
9133     
9134     // set default sortType for strings and dates
9135     if(!this.sortType){
9136         switch(this.type){
9137             case "string":
9138                 this.sortType = st.asUCString;
9139                 break;
9140             case "date":
9141                 this.sortType = st.asDate;
9142                 break;
9143             default:
9144                 this.sortType = st.none;
9145         }
9146     }
9147
9148     // define once
9149     var stripRe = /[\$,%]/g;
9150
9151     // prebuilt conversion function for this field, instead of
9152     // switching every time we're reading a value
9153     if(!this.convert){
9154         var cv, dateFormat = this.dateFormat;
9155         switch(this.type){
9156             case "":
9157             case "auto":
9158             case undefined:
9159                 cv = function(v){ return v; };
9160                 break;
9161             case "string":
9162                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9163                 break;
9164             case "int":
9165                 cv = function(v){
9166                     return v !== undefined && v !== null && v !== '' ?
9167                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9168                     };
9169                 break;
9170             case "float":
9171                 cv = function(v){
9172                     return v !== undefined && v !== null && v !== '' ?
9173                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9174                     };
9175                 break;
9176             case "bool":
9177             case "boolean":
9178                 cv = function(v){ return v === true || v === "true" || v == 1; };
9179                 break;
9180             case "date":
9181                 cv = function(v){
9182                     if(!v){
9183                         return '';
9184                     }
9185                     if(v instanceof Date){
9186                         return v;
9187                     }
9188                     if(dateFormat){
9189                         if(dateFormat == "timestamp"){
9190                             return new Date(v*1000);
9191                         }
9192                         return Date.parseDate(v, dateFormat);
9193                     }
9194                     var parsed = Date.parse(v);
9195                     return parsed ? new Date(parsed) : null;
9196                 };
9197              break;
9198             
9199         }
9200         this.convert = cv;
9201     }
9202 };
9203
9204 Roo.data.Field.prototype = {
9205     dateFormat: null,
9206     defaultValue: "",
9207     mapping: null,
9208     sortType : null,
9209     sortDir : "ASC"
9210 };/*
9211  * Based on:
9212  * Ext JS Library 1.1.1
9213  * Copyright(c) 2006-2007, Ext JS, LLC.
9214  *
9215  * Originally Released Under LGPL - original licence link has changed is not relivant.
9216  *
9217  * Fork - LGPL
9218  * <script type="text/javascript">
9219  */
9220  
9221 // Base class for reading structured data from a data source.  This class is intended to be
9222 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9223
9224 /**
9225  * @class Roo.data.DataReader
9226  * Base class for reading structured data from a data source.  This class is intended to be
9227  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9228  */
9229
9230 Roo.data.DataReader = function(meta, recordType){
9231     
9232     this.meta = meta;
9233     
9234     this.recordType = recordType instanceof Array ? 
9235         Roo.data.Record.create(recordType) : recordType;
9236 };
9237
9238 Roo.data.DataReader.prototype = {
9239      /**
9240      * Create an empty record
9241      * @param {Object} data (optional) - overlay some values
9242      * @return {Roo.data.Record} record created.
9243      */
9244     newRow :  function(d) {
9245         var da =  {};
9246         this.recordType.prototype.fields.each(function(c) {
9247             switch( c.type) {
9248                 case 'int' : da[c.name] = 0; break;
9249                 case 'date' : da[c.name] = new Date(); break;
9250                 case 'float' : da[c.name] = 0.0; break;
9251                 case 'boolean' : da[c.name] = false; break;
9252                 default : da[c.name] = ""; break;
9253             }
9254             
9255         });
9256         return new this.recordType(Roo.apply(da, d));
9257     }
9258     
9259 };/*
9260  * Based on:
9261  * Ext JS Library 1.1.1
9262  * Copyright(c) 2006-2007, Ext JS, LLC.
9263  *
9264  * Originally Released Under LGPL - original licence link has changed is not relivant.
9265  *
9266  * Fork - LGPL
9267  * <script type="text/javascript">
9268  */
9269
9270 /**
9271  * @class Roo.data.DataProxy
9272  * @extends Roo.data.Observable
9273  * This class is an abstract base class for implementations which provide retrieval of
9274  * unformatted data objects.<br>
9275  * <p>
9276  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9277  * (of the appropriate type which knows how to parse the data object) to provide a block of
9278  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9279  * <p>
9280  * Custom implementations must implement the load method as described in
9281  * {@link Roo.data.HttpProxy#load}.
9282  */
9283 Roo.data.DataProxy = function(){
9284     this.addEvents({
9285         /**
9286          * @event beforeload
9287          * Fires before a network request is made to retrieve a data object.
9288          * @param {Object} This DataProxy object.
9289          * @param {Object} params The params parameter to the load function.
9290          */
9291         beforeload : true,
9292         /**
9293          * @event load
9294          * Fires before the load method's callback is called.
9295          * @param {Object} This DataProxy object.
9296          * @param {Object} o The data object.
9297          * @param {Object} arg The callback argument object passed to the load function.
9298          */
9299         load : true,
9300         /**
9301          * @event loadexception
9302          * Fires if an Exception occurs during data retrieval.
9303          * @param {Object} This DataProxy object.
9304          * @param {Object} o The data object.
9305          * @param {Object} arg The callback argument object passed to the load function.
9306          * @param {Object} e The Exception.
9307          */
9308         loadexception : true
9309     });
9310     Roo.data.DataProxy.superclass.constructor.call(this);
9311 };
9312
9313 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9314
9315     /**
9316      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9317      */
9318 /*
9319  * Based on:
9320  * Ext JS Library 1.1.1
9321  * Copyright(c) 2006-2007, Ext JS, LLC.
9322  *
9323  * Originally Released Under LGPL - original licence link has changed is not relivant.
9324  *
9325  * Fork - LGPL
9326  * <script type="text/javascript">
9327  */
9328 /**
9329  * @class Roo.data.MemoryProxy
9330  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9331  * to the Reader when its load method is called.
9332  * @constructor
9333  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9334  */
9335 Roo.data.MemoryProxy = function(data){
9336     if (data.data) {
9337         data = data.data;
9338     }
9339     Roo.data.MemoryProxy.superclass.constructor.call(this);
9340     this.data = data;
9341 };
9342
9343 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9344     /**
9345      * Load data from the requested source (in this case an in-memory
9346      * data object passed to the constructor), read the data object into
9347      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9348      * process that block using the passed callback.
9349      * @param {Object} params This parameter is not used by the MemoryProxy class.
9350      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9351      * object into a block of Roo.data.Records.
9352      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9353      * The function must be passed <ul>
9354      * <li>The Record block object</li>
9355      * <li>The "arg" argument from the load function</li>
9356      * <li>A boolean success indicator</li>
9357      * </ul>
9358      * @param {Object} scope The scope in which to call the callback
9359      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9360      */
9361     load : function(params, reader, callback, scope, arg){
9362         params = params || {};
9363         var result;
9364         try {
9365             result = reader.readRecords(this.data);
9366         }catch(e){
9367             this.fireEvent("loadexception", this, arg, null, e);
9368             callback.call(scope, null, arg, false);
9369             return;
9370         }
9371         callback.call(scope, result, arg, true);
9372     },
9373     
9374     // private
9375     update : function(params, records){
9376         
9377     }
9378 });/*
9379  * Based on:
9380  * Ext JS Library 1.1.1
9381  * Copyright(c) 2006-2007, Ext JS, LLC.
9382  *
9383  * Originally Released Under LGPL - original licence link has changed is not relivant.
9384  *
9385  * Fork - LGPL
9386  * <script type="text/javascript">
9387  */
9388 /**
9389  * @class Roo.data.HttpProxy
9390  * @extends Roo.data.DataProxy
9391  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9392  * configured to reference a certain URL.<br><br>
9393  * <p>
9394  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9395  * from which the running page was served.<br><br>
9396  * <p>
9397  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9398  * <p>
9399  * Be aware that to enable the browser to parse an XML document, the server must set
9400  * the Content-Type header in the HTTP response to "text/xml".
9401  * @constructor
9402  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9403  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9404  * will be used to make the request.
9405  */
9406 Roo.data.HttpProxy = function(conn){
9407     Roo.data.HttpProxy.superclass.constructor.call(this);
9408     // is conn a conn config or a real conn?
9409     this.conn = conn;
9410     this.useAjax = !conn || !conn.events;
9411   
9412 };
9413
9414 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9415     // thse are take from connection...
9416     
9417     /**
9418      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9419      */
9420     /**
9421      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9422      * extra parameters to each request made by this object. (defaults to undefined)
9423      */
9424     /**
9425      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9426      *  to each request made by this object. (defaults to undefined)
9427      */
9428     /**
9429      * @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)
9430      */
9431     /**
9432      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9433      */
9434      /**
9435      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9436      * @type Boolean
9437      */
9438   
9439
9440     /**
9441      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9442      * @type Boolean
9443      */
9444     /**
9445      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9446      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9447      * a finer-grained basis than the DataProxy events.
9448      */
9449     getConnection : function(){
9450         return this.useAjax ? Roo.Ajax : this.conn;
9451     },
9452
9453     /**
9454      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9455      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9456      * process that block using the passed callback.
9457      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9458      * for the request to the remote server.
9459      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9460      * object into a block of Roo.data.Records.
9461      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9462      * The function must be passed <ul>
9463      * <li>The Record block object</li>
9464      * <li>The "arg" argument from the load function</li>
9465      * <li>A boolean success indicator</li>
9466      * </ul>
9467      * @param {Object} scope The scope in which to call the callback
9468      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9469      */
9470     load : function(params, reader, callback, scope, arg){
9471         if(this.fireEvent("beforeload", this, params) !== false){
9472             var  o = {
9473                 params : params || {},
9474                 request: {
9475                     callback : callback,
9476                     scope : scope,
9477                     arg : arg
9478                 },
9479                 reader: reader,
9480                 callback : this.loadResponse,
9481                 scope: this
9482             };
9483             if(this.useAjax){
9484                 Roo.applyIf(o, this.conn);
9485                 if(this.activeRequest){
9486                     Roo.Ajax.abort(this.activeRequest);
9487                 }
9488                 this.activeRequest = Roo.Ajax.request(o);
9489             }else{
9490                 this.conn.request(o);
9491             }
9492         }else{
9493             callback.call(scope||this, null, arg, false);
9494         }
9495     },
9496
9497     // private
9498     loadResponse : function(o, success, response){
9499         delete this.activeRequest;
9500         if(!success){
9501             this.fireEvent("loadexception", this, o, response);
9502             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9503             return;
9504         }
9505         var result;
9506         try {
9507             result = o.reader.read(response);
9508         }catch(e){
9509             this.fireEvent("loadexception", this, o, response, e);
9510             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9511             return;
9512         }
9513         
9514         this.fireEvent("load", this, o, o.request.arg);
9515         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9516     },
9517
9518     // private
9519     update : function(dataSet){
9520
9521     },
9522
9523     // private
9524     updateResponse : function(dataSet){
9525
9526     }
9527 });/*
9528  * Based on:
9529  * Ext JS Library 1.1.1
9530  * Copyright(c) 2006-2007, Ext JS, LLC.
9531  *
9532  * Originally Released Under LGPL - original licence link has changed is not relivant.
9533  *
9534  * Fork - LGPL
9535  * <script type="text/javascript">
9536  */
9537
9538 /**
9539  * @class Roo.data.ScriptTagProxy
9540  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9541  * other than the originating domain of the running page.<br><br>
9542  * <p>
9543  * <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
9544  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9545  * <p>
9546  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9547  * source code that is used as the source inside a &lt;script> tag.<br><br>
9548  * <p>
9549  * In order for the browser to process the returned data, the server must wrap the data object
9550  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9551  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9552  * depending on whether the callback name was passed:
9553  * <p>
9554  * <pre><code>
9555 boolean scriptTag = false;
9556 String cb = request.getParameter("callback");
9557 if (cb != null) {
9558     scriptTag = true;
9559     response.setContentType("text/javascript");
9560 } else {
9561     response.setContentType("application/x-json");
9562 }
9563 Writer out = response.getWriter();
9564 if (scriptTag) {
9565     out.write(cb + "(");
9566 }
9567 out.print(dataBlock.toJsonString());
9568 if (scriptTag) {
9569     out.write(");");
9570 }
9571 </pre></code>
9572  *
9573  * @constructor
9574  * @param {Object} config A configuration object.
9575  */
9576 Roo.data.ScriptTagProxy = function(config){
9577     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9578     Roo.apply(this, config);
9579     this.head = document.getElementsByTagName("head")[0];
9580 };
9581
9582 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9583
9584 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9585     /**
9586      * @cfg {String} url The URL from which to request the data object.
9587      */
9588     /**
9589      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9590      */
9591     timeout : 30000,
9592     /**
9593      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9594      * the server the name of the callback function set up by the load call to process the returned data object.
9595      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9596      * javascript output which calls this named function passing the data object as its only parameter.
9597      */
9598     callbackParam : "callback",
9599     /**
9600      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9601      * name to the request.
9602      */
9603     nocache : true,
9604
9605     /**
9606      * Load data from the configured URL, read the data object into
9607      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9608      * process that block using the passed callback.
9609      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9610      * for the request to the remote server.
9611      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9612      * object into a block of Roo.data.Records.
9613      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9614      * The function must be passed <ul>
9615      * <li>The Record block object</li>
9616      * <li>The "arg" argument from the load function</li>
9617      * <li>A boolean success indicator</li>
9618      * </ul>
9619      * @param {Object} scope The scope in which to call the callback
9620      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9621      */
9622     load : function(params, reader, callback, scope, arg){
9623         if(this.fireEvent("beforeload", this, params) !== false){
9624
9625             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9626
9627             var url = this.url;
9628             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9629             if(this.nocache){
9630                 url += "&_dc=" + (new Date().getTime());
9631             }
9632             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9633             var trans = {
9634                 id : transId,
9635                 cb : "stcCallback"+transId,
9636                 scriptId : "stcScript"+transId,
9637                 params : params,
9638                 arg : arg,
9639                 url : url,
9640                 callback : callback,
9641                 scope : scope,
9642                 reader : reader
9643             };
9644             var conn = this;
9645
9646             window[trans.cb] = function(o){
9647                 conn.handleResponse(o, trans);
9648             };
9649
9650             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9651
9652             if(this.autoAbort !== false){
9653                 this.abort();
9654             }
9655
9656             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9657
9658             var script = document.createElement("script");
9659             script.setAttribute("src", url);
9660             script.setAttribute("type", "text/javascript");
9661             script.setAttribute("id", trans.scriptId);
9662             this.head.appendChild(script);
9663
9664             this.trans = trans;
9665         }else{
9666             callback.call(scope||this, null, arg, false);
9667         }
9668     },
9669
9670     // private
9671     isLoading : function(){
9672         return this.trans ? true : false;
9673     },
9674
9675     /**
9676      * Abort the current server request.
9677      */
9678     abort : function(){
9679         if(this.isLoading()){
9680             this.destroyTrans(this.trans);
9681         }
9682     },
9683
9684     // private
9685     destroyTrans : function(trans, isLoaded){
9686         this.head.removeChild(document.getElementById(trans.scriptId));
9687         clearTimeout(trans.timeoutId);
9688         if(isLoaded){
9689             window[trans.cb] = undefined;
9690             try{
9691                 delete window[trans.cb];
9692             }catch(e){}
9693         }else{
9694             // if hasn't been loaded, wait for load to remove it to prevent script error
9695             window[trans.cb] = function(){
9696                 window[trans.cb] = undefined;
9697                 try{
9698                     delete window[trans.cb];
9699                 }catch(e){}
9700             };
9701         }
9702     },
9703
9704     // private
9705     handleResponse : function(o, trans){
9706         this.trans = false;
9707         this.destroyTrans(trans, true);
9708         var result;
9709         try {
9710             result = trans.reader.readRecords(o);
9711         }catch(e){
9712             this.fireEvent("loadexception", this, o, trans.arg, e);
9713             trans.callback.call(trans.scope||window, null, trans.arg, false);
9714             return;
9715         }
9716         this.fireEvent("load", this, o, trans.arg);
9717         trans.callback.call(trans.scope||window, result, trans.arg, true);
9718     },
9719
9720     // private
9721     handleFailure : function(trans){
9722         this.trans = false;
9723         this.destroyTrans(trans, false);
9724         this.fireEvent("loadexception", this, null, trans.arg);
9725         trans.callback.call(trans.scope||window, null, trans.arg, false);
9726     }
9727 });/*
9728  * Based on:
9729  * Ext JS Library 1.1.1
9730  * Copyright(c) 2006-2007, Ext JS, LLC.
9731  *
9732  * Originally Released Under LGPL - original licence link has changed is not relivant.
9733  *
9734  * Fork - LGPL
9735  * <script type="text/javascript">
9736  */
9737
9738 /**
9739  * @class Roo.data.JsonReader
9740  * @extends Roo.data.DataReader
9741  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9742  * based on mappings in a provided Roo.data.Record constructor.
9743  * 
9744  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9745  * in the reply previously. 
9746  * 
9747  * <p>
9748  * Example code:
9749  * <pre><code>
9750 var RecordDef = Roo.data.Record.create([
9751     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9752     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9753 ]);
9754 var myReader = new Roo.data.JsonReader({
9755     totalProperty: "results",    // The property which contains the total dataset size (optional)
9756     root: "rows",                // The property which contains an Array of row objects
9757     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9758 }, RecordDef);
9759 </code></pre>
9760  * <p>
9761  * This would consume a JSON file like this:
9762  * <pre><code>
9763 { 'results': 2, 'rows': [
9764     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9765     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9766 }
9767 </code></pre>
9768  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9769  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9770  * paged from the remote server.
9771  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9772  * @cfg {String} root name of the property which contains the Array of row objects.
9773  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9774  * @constructor
9775  * Create a new JsonReader
9776  * @param {Object} meta Metadata configuration options
9777  * @param {Object} recordType Either an Array of field definition objects,
9778  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9779  */
9780 Roo.data.JsonReader = function(meta, recordType){
9781     
9782     meta = meta || {};
9783     // set some defaults:
9784     Roo.applyIf(meta, {
9785         totalProperty: 'total',
9786         successProperty : 'success',
9787         root : 'data',
9788         id : 'id'
9789     });
9790     
9791     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9792 };
9793 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9794     
9795     /**
9796      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9797      * Used by Store query builder to append _requestMeta to params.
9798      * 
9799      */
9800     metaFromRemote : false,
9801     /**
9802      * This method is only used by a DataProxy which has retrieved data from a remote server.
9803      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9804      * @return {Object} data A data block which is used by an Roo.data.Store object as
9805      * a cache of Roo.data.Records.
9806      */
9807     read : function(response){
9808         var json = response.responseText;
9809        
9810         var o = /* eval:var:o */ eval("("+json+")");
9811         if(!o) {
9812             throw {message: "JsonReader.read: Json object not found"};
9813         }
9814         
9815         if(o.metaData){
9816             
9817             delete this.ef;
9818             this.metaFromRemote = true;
9819             this.meta = o.metaData;
9820             this.recordType = Roo.data.Record.create(o.metaData.fields);
9821             this.onMetaChange(this.meta, this.recordType, o);
9822         }
9823         return this.readRecords(o);
9824     },
9825
9826     // private function a store will implement
9827     onMetaChange : function(meta, recordType, o){
9828
9829     },
9830
9831     /**
9832          * @ignore
9833          */
9834     simpleAccess: function(obj, subsc) {
9835         return obj[subsc];
9836     },
9837
9838         /**
9839          * @ignore
9840          */
9841     getJsonAccessor: function(){
9842         var re = /[\[\.]/;
9843         return function(expr) {
9844             try {
9845                 return(re.test(expr))
9846                     ? new Function("obj", "return obj." + expr)
9847                     : function(obj){
9848                         return obj[expr];
9849                     };
9850             } catch(e){}
9851             return Roo.emptyFn;
9852         };
9853     }(),
9854
9855     /**
9856      * Create a data block containing Roo.data.Records from an XML document.
9857      * @param {Object} o An object which contains an Array of row objects in the property specified
9858      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9859      * which contains the total size of the dataset.
9860      * @return {Object} data A data block which is used by an Roo.data.Store object as
9861      * a cache of Roo.data.Records.
9862      */
9863     readRecords : function(o){
9864         /**
9865          * After any data loads, the raw JSON data is available for further custom processing.
9866          * @type Object
9867          */
9868         this.o = o;
9869         var s = this.meta, Record = this.recordType,
9870             f = Record.prototype.fields, fi = f.items, fl = f.length;
9871
9872 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9873         if (!this.ef) {
9874             if(s.totalProperty) {
9875                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9876                 }
9877                 if(s.successProperty) {
9878                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9879                 }
9880                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9881                 if (s.id) {
9882                         var g = this.getJsonAccessor(s.id);
9883                         this.getId = function(rec) {
9884                                 var r = g(rec);
9885                                 return (r === undefined || r === "") ? null : r;
9886                         };
9887                 } else {
9888                         this.getId = function(){return null;};
9889                 }
9890             this.ef = [];
9891             for(var jj = 0; jj < fl; jj++){
9892                 f = fi[jj];
9893                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9894                 this.ef[jj] = this.getJsonAccessor(map);
9895             }
9896         }
9897
9898         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9899         if(s.totalProperty){
9900             var vt = parseInt(this.getTotal(o), 10);
9901             if(!isNaN(vt)){
9902                 totalRecords = vt;
9903             }
9904         }
9905         if(s.successProperty){
9906             var vs = this.getSuccess(o);
9907             if(vs === false || vs === 'false'){
9908                 success = false;
9909             }
9910         }
9911         var records = [];
9912             for(var i = 0; i < c; i++){
9913                     var n = root[i];
9914                 var values = {};
9915                 var id = this.getId(n);
9916                 for(var j = 0; j < fl; j++){
9917                     f = fi[j];
9918                 var v = this.ef[j](n);
9919                 if (!f.convert) {
9920                     Roo.log('missing convert for ' + f.name);
9921                     Roo.log(f);
9922                     continue;
9923                 }
9924                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9925                 }
9926                 var record = new Record(values, id);
9927                 record.json = n;
9928                 records[i] = record;
9929             }
9930             return {
9931             raw : o,
9932                 success : success,
9933                 records : records,
9934                 totalRecords : totalRecords
9935             };
9936     }
9937 });/*
9938  * Based on:
9939  * Ext JS Library 1.1.1
9940  * Copyright(c) 2006-2007, Ext JS, LLC.
9941  *
9942  * Originally Released Under LGPL - original licence link has changed is not relivant.
9943  *
9944  * Fork - LGPL
9945  * <script type="text/javascript">
9946  */
9947
9948 /**
9949  * @class Roo.data.ArrayReader
9950  * @extends Roo.data.DataReader
9951  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9952  * Each element of that Array represents a row of data fields. The
9953  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9954  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9955  * <p>
9956  * Example code:.
9957  * <pre><code>
9958 var RecordDef = Roo.data.Record.create([
9959     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
9960     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
9961 ]);
9962 var myReader = new Roo.data.ArrayReader({
9963     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
9964 }, RecordDef);
9965 </code></pre>
9966  * <p>
9967  * This would consume an Array like this:
9968  * <pre><code>
9969 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9970   </code></pre>
9971  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9972  * @constructor
9973  * Create a new JsonReader
9974  * @param {Object} meta Metadata configuration options.
9975  * @param {Object} recordType Either an Array of field definition objects
9976  * as specified to {@link Roo.data.Record#create},
9977  * or an {@link Roo.data.Record} object
9978  * created using {@link Roo.data.Record#create}.
9979  */
9980 Roo.data.ArrayReader = function(meta, recordType){
9981     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9982 };
9983
9984 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9985     /**
9986      * Create a data block containing Roo.data.Records from an XML document.
9987      * @param {Object} o An Array of row objects which represents the dataset.
9988      * @return {Object} data A data block which is used by an Roo.data.Store object as
9989      * a cache of Roo.data.Records.
9990      */
9991     readRecords : function(o){
9992         var sid = this.meta ? this.meta.id : null;
9993         var recordType = this.recordType, fields = recordType.prototype.fields;
9994         var records = [];
9995         var root = o;
9996             for(var i = 0; i < root.length; i++){
9997                     var n = root[i];
9998                 var values = {};
9999                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10000                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10001                 var f = fields.items[j];
10002                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10003                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10004                 v = f.convert(v);
10005                 values[f.name] = v;
10006             }
10007                 var record = new recordType(values, id);
10008                 record.json = n;
10009                 records[records.length] = record;
10010             }
10011             return {
10012                 records : records,
10013                 totalRecords : records.length
10014             };
10015     }
10016 });/*
10017  * - LGPL
10018  * * 
10019  */
10020
10021 /**
10022  * @class Roo.bootstrap.ComboBox
10023  * @extends Roo.bootstrap.TriggerField
10024  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10025  * @cfg {Boolean} append (true|false) default false
10026  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10027  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10028  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10029  * @constructor
10030  * Create a new ComboBox.
10031  * @param {Object} config Configuration options
10032  */
10033 Roo.bootstrap.ComboBox = function(config){
10034     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10035     this.addEvents({
10036         /**
10037          * @event expand
10038          * Fires when the dropdown list is expanded
10039              * @param {Roo.bootstrap.ComboBox} combo This combo box
10040              */
10041         'expand' : true,
10042         /**
10043          * @event collapse
10044          * Fires when the dropdown list is collapsed
10045              * @param {Roo.bootstrap.ComboBox} combo This combo box
10046              */
10047         'collapse' : true,
10048         /**
10049          * @event beforeselect
10050          * Fires before a list item is selected. Return false to cancel the selection.
10051              * @param {Roo.bootstrap.ComboBox} combo This combo box
10052              * @param {Roo.data.Record} record The data record returned from the underlying store
10053              * @param {Number} index The index of the selected item in the dropdown list
10054              */
10055         'beforeselect' : true,
10056         /**
10057          * @event select
10058          * Fires when a list item is selected
10059              * @param {Roo.bootstrap.ComboBox} combo This combo box
10060              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10061              * @param {Number} index The index of the selected item in the dropdown list
10062              */
10063         'select' : true,
10064         /**
10065          * @event beforequery
10066          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10067          * The event object passed has these properties:
10068              * @param {Roo.bootstrap.ComboBox} combo This combo box
10069              * @param {String} query The query
10070              * @param {Boolean} forceAll true to force "all" query
10071              * @param {Boolean} cancel true to cancel the query
10072              * @param {Object} e The query event object
10073              */
10074         'beforequery': true,
10075          /**
10076          * @event add
10077          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10078              * @param {Roo.bootstrap.ComboBox} combo This combo box
10079              */
10080         'add' : true,
10081         /**
10082          * @event edit
10083          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10084              * @param {Roo.bootstrap.ComboBox} combo This combo box
10085              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10086              */
10087         'edit' : true,
10088         /**
10089          * @event remove
10090          * Fires when the remove value from the combobox array
10091              * @param {Roo.bootstrap.ComboBox} combo This combo box
10092              */
10093         'remove' : true
10094         
10095     });
10096     
10097     this.item = [];
10098     this.tickItems = [];
10099     
10100     this.selectedIndex = -1;
10101     if(this.mode == 'local'){
10102         if(config.queryDelay === undefined){
10103             this.queryDelay = 10;
10104         }
10105         if(config.minChars === undefined){
10106             this.minChars = 0;
10107         }
10108     }
10109 };
10110
10111 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10112      
10113     /**
10114      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10115      * rendering into an Roo.Editor, defaults to false)
10116      */
10117     /**
10118      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10119      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10120      */
10121     /**
10122      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10123      */
10124     /**
10125      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10126      * the dropdown list (defaults to undefined, with no header element)
10127      */
10128
10129      /**
10130      * @cfg {String/Roo.Template} tpl The template to use to render the output
10131      */
10132      
10133      /**
10134      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10135      */
10136     listWidth: undefined,
10137     /**
10138      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10139      * mode = 'remote' or 'text' if mode = 'local')
10140      */
10141     displayField: undefined,
10142     /**
10143      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10144      * mode = 'remote' or 'value' if mode = 'local'). 
10145      * Note: use of a valueField requires the user make a selection
10146      * in order for a value to be mapped.
10147      */
10148     valueField: undefined,
10149     
10150     
10151     /**
10152      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10153      * field's data value (defaults to the underlying DOM element's name)
10154      */
10155     hiddenName: undefined,
10156     /**
10157      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10158      */
10159     listClass: '',
10160     /**
10161      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10162      */
10163     selectedClass: 'active',
10164     
10165     /**
10166      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10167      */
10168     shadow:'sides',
10169     /**
10170      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10171      * anchor positions (defaults to 'tl-bl')
10172      */
10173     listAlign: 'tl-bl?',
10174     /**
10175      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10176      */
10177     maxHeight: 300,
10178     /**
10179      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10180      * query specified by the allQuery config option (defaults to 'query')
10181      */
10182     triggerAction: 'query',
10183     /**
10184      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10185      * (defaults to 4, does not apply if editable = false)
10186      */
10187     minChars : 4,
10188     /**
10189      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10190      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10191      */
10192     typeAhead: false,
10193     /**
10194      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10195      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10196      */
10197     queryDelay: 500,
10198     /**
10199      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10200      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10201      */
10202     pageSize: 0,
10203     /**
10204      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10205      * when editable = true (defaults to false)
10206      */
10207     selectOnFocus:false,
10208     /**
10209      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10210      */
10211     queryParam: 'query',
10212     /**
10213      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10214      * when mode = 'remote' (defaults to 'Loading...')
10215      */
10216     loadingText: 'Loading...',
10217     /**
10218      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10219      */
10220     resizable: false,
10221     /**
10222      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10223      */
10224     handleHeight : 8,
10225     /**
10226      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10227      * traditional select (defaults to true)
10228      */
10229     editable: true,
10230     /**
10231      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10232      */
10233     allQuery: '',
10234     /**
10235      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10236      */
10237     mode: 'remote',
10238     /**
10239      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10240      * listWidth has a higher value)
10241      */
10242     minListWidth : 70,
10243     /**
10244      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10245      * allow the user to set arbitrary text into the field (defaults to false)
10246      */
10247     forceSelection:false,
10248     /**
10249      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10250      * if typeAhead = true (defaults to 250)
10251      */
10252     typeAheadDelay : 250,
10253     /**
10254      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10255      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10256      */
10257     valueNotFoundText : undefined,
10258     /**
10259      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10260      */
10261     blockFocus : false,
10262     
10263     /**
10264      * @cfg {Boolean} disableClear Disable showing of clear button.
10265      */
10266     disableClear : false,
10267     /**
10268      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10269      */
10270     alwaysQuery : false,
10271     
10272     /**
10273      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10274      */
10275     multiple : false,
10276     
10277     //private
10278     addicon : false,
10279     editicon: false,
10280     
10281     page: 0,
10282     hasQuery: false,
10283     append: false,
10284     loadNext: false,
10285     autoFocus : true,
10286     tickable : false,
10287     btnPosition : 'right',
10288     
10289     // element that contains real text value.. (when hidden is used..)
10290     
10291     getAutoCreate : function()
10292     {
10293         var cfg = false;
10294         
10295         /*
10296          *  Normal ComboBox
10297          */
10298         if(!this.tickable){
10299             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10300             return cfg;
10301         }
10302         
10303         /*
10304          *  ComboBox with tickable selections
10305          */
10306              
10307         var align = this.labelAlign || this.parentLabelAlign();
10308         
10309         cfg = {
10310             cls : 'form-group roo-combobox-tickable' //input-group
10311         };
10312         
10313         
10314         var buttons = {
10315             tag : 'div',
10316             cls : 'tickable-buttons',
10317             cn : [
10318                 {
10319                     tag : 'button',
10320                     type : 'button',
10321                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10322                     html : 'Edit'
10323                 },
10324                 {
10325                     tag : 'button',
10326                     type : 'button',
10327                     name : 'ok',
10328                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10329                     html : 'Done'
10330                 },
10331                 {
10332                     tag : 'button',
10333                     type : 'button',
10334                     name : 'cancel',
10335                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10336                     html : 'Cancel'
10337                 }
10338             ]
10339         };
10340         
10341         var _this = this;
10342         Roo.each(buttons.cn, function(c){
10343             if (_this.size) {
10344                 c.cls += ' btn-' + _this.size;
10345             }
10346
10347             if (_this.disabled) {
10348                 c.disabled = true;
10349             }
10350         });
10351         
10352         var box = {
10353             tag: 'div',
10354             cn: [
10355                 {
10356                     tag: 'input',
10357                     type : 'hidden',
10358                     cls: 'form-hidden-field'
10359                 },
10360                 {
10361                     tag: 'ul',
10362                     cls: 'select2-choices',
10363                     cn:[
10364                         {
10365                             tag: 'li',
10366                             cls: 'select2-search-field',
10367                             cn: [
10368
10369                                 buttons
10370                             ]
10371                         }
10372                     ]
10373                 }
10374             ]
10375         }
10376         
10377         var combobox = {
10378             cls: 'select2-container input-group select2-container-multi',
10379             cn: [
10380                 box,
10381                 {
10382                     tag: 'ul',
10383                     cls: 'typeahead typeahead-long dropdown-menu',
10384                     style: 'display:none; max-height:' + this.maxHeight + 'px;'
10385                 }
10386             ]
10387         };
10388         
10389         if (align ==='left' && this.fieldLabel.length) {
10390             
10391                 Roo.log("left and has label");
10392                 cfg.cn = [
10393                     
10394                     {
10395                         tag: 'label',
10396                         'for' :  id,
10397                         cls : 'control-label col-sm-' + this.labelWidth,
10398                         html : this.fieldLabel
10399                         
10400                     },
10401                     {
10402                         cls : "col-sm-" + (12 - this.labelWidth), 
10403                         cn: [
10404                             combobox
10405                         ]
10406                     }
10407                     
10408                 ];
10409         } else if ( this.fieldLabel.length) {
10410                 Roo.log(" label");
10411                  cfg.cn = [
10412                    
10413                     {
10414                         tag: 'label',
10415                         //cls : 'input-group-addon',
10416                         html : this.fieldLabel
10417                         
10418                     },
10419                     
10420                     combobox
10421                     
10422                 ];
10423
10424         } else {
10425             
10426                 Roo.log(" no label && no align");
10427                 cfg = combobox
10428                      
10429                 
10430         }
10431          
10432         var settings=this;
10433         ['xs','sm','md','lg'].map(function(size){
10434             if (settings[size]) {
10435                 cfg.cls += ' col-' + size + '-' + settings[size];
10436             }
10437         });
10438         
10439         return cfg;
10440         
10441     },
10442     
10443     // private
10444     initEvents: function()
10445     {
10446         
10447         if (!this.store) {
10448             throw "can not find store for combo";
10449         }
10450         this.store = Roo.factory(this.store, Roo.data);
10451         
10452         if(this.tickable){
10453             this.initTickableEvnets();
10454             return;
10455         }
10456         
10457         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10458         
10459         
10460         if(this.hiddenName){
10461             
10462             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10463             
10464             this.hiddenField.dom.value =
10465                 this.hiddenValue !== undefined ? this.hiddenValue :
10466                 this.value !== undefined ? this.value : '';
10467
10468             // prevent input submission
10469             this.el.dom.removeAttribute('name');
10470             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10471              
10472              
10473         }
10474         //if(Roo.isGecko){
10475         //    this.el.dom.setAttribute('autocomplete', 'off');
10476         //}
10477
10478         var cls = 'x-combo-list';
10479         this.list = this.el.select('ul.dropdown-menu',true).first();
10480
10481         //this.list = new Roo.Layer({
10482         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10483         //});
10484         
10485         var _this = this;
10486         
10487         (function(){
10488             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10489             _this.list.setWidth(lw);
10490         }).defer(100);
10491         
10492         this.list.on('mouseover', this.onViewOver, this);
10493         this.list.on('mousemove', this.onViewMove, this);
10494         
10495         this.list.on('scroll', this.onViewScroll, this);
10496         
10497         /*
10498         this.list.swallowEvent('mousewheel');
10499         this.assetHeight = 0;
10500
10501         if(this.title){
10502             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10503             this.assetHeight += this.header.getHeight();
10504         }
10505
10506         this.innerList = this.list.createChild({cls:cls+'-inner'});
10507         this.innerList.on('mouseover', this.onViewOver, this);
10508         this.innerList.on('mousemove', this.onViewMove, this);
10509         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10510         
10511         if(this.allowBlank && !this.pageSize && !this.disableClear){
10512             this.footer = this.list.createChild({cls:cls+'-ft'});
10513             this.pageTb = new Roo.Toolbar(this.footer);
10514            
10515         }
10516         if(this.pageSize){
10517             this.footer = this.list.createChild({cls:cls+'-ft'});
10518             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10519                     {pageSize: this.pageSize});
10520             
10521         }
10522         
10523         if (this.pageTb && this.allowBlank && !this.disableClear) {
10524             var _this = this;
10525             this.pageTb.add(new Roo.Toolbar.Fill(), {
10526                 cls: 'x-btn-icon x-btn-clear',
10527                 text: '&#160;',
10528                 handler: function()
10529                 {
10530                     _this.collapse();
10531                     _this.clearValue();
10532                     _this.onSelect(false, -1);
10533                 }
10534             });
10535         }
10536         if (this.footer) {
10537             this.assetHeight += this.footer.getHeight();
10538         }
10539         */
10540             
10541         if(!this.tpl){
10542             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10543         }
10544
10545         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10546             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10547         });
10548         //this.view.wrapEl.setDisplayed(false);
10549         this.view.on('click', this.onViewClick, this);
10550         
10551         
10552         
10553         this.store.on('beforeload', this.onBeforeLoad, this);
10554         this.store.on('load', this.onLoad, this);
10555         this.store.on('loadexception', this.onLoadException, this);
10556         /*
10557         if(this.resizable){
10558             this.resizer = new Roo.Resizable(this.list,  {
10559                pinned:true, handles:'se'
10560             });
10561             this.resizer.on('resize', function(r, w, h){
10562                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10563                 this.listWidth = w;
10564                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10565                 this.restrictHeight();
10566             }, this);
10567             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10568         }
10569         */
10570         if(!this.editable){
10571             this.editable = true;
10572             this.setEditable(false);
10573         }
10574         
10575         /*
10576         
10577         if (typeof(this.events.add.listeners) != 'undefined') {
10578             
10579             this.addicon = this.wrap.createChild(
10580                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10581        
10582             this.addicon.on('click', function(e) {
10583                 this.fireEvent('add', this);
10584             }, this);
10585         }
10586         if (typeof(this.events.edit.listeners) != 'undefined') {
10587             
10588             this.editicon = this.wrap.createChild(
10589                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10590             if (this.addicon) {
10591                 this.editicon.setStyle('margin-left', '40px');
10592             }
10593             this.editicon.on('click', function(e) {
10594                 
10595                 // we fire even  if inothing is selected..
10596                 this.fireEvent('edit', this, this.lastData );
10597                 
10598             }, this);
10599         }
10600         */
10601         
10602         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10603             "up" : function(e){
10604                 this.inKeyMode = true;
10605                 this.selectPrev();
10606             },
10607
10608             "down" : function(e){
10609                 if(!this.isExpanded()){
10610                     this.onTriggerClick();
10611                 }else{
10612                     this.inKeyMode = true;
10613                     this.selectNext();
10614                 }
10615             },
10616
10617             "enter" : function(e){
10618 //                this.onViewClick();
10619                 //return true;
10620                 this.collapse();
10621                 
10622                 if(this.fireEvent("specialkey", this, e)){
10623                     this.onViewClick(false);
10624                 }
10625                 
10626                 return true;
10627             },
10628
10629             "esc" : function(e){
10630                 this.collapse();
10631             },
10632
10633             "tab" : function(e){
10634                 this.collapse();
10635                 
10636                 if(this.fireEvent("specialkey", this, e)){
10637                     this.onViewClick(false);
10638                 }
10639                 
10640                 return true;
10641             },
10642
10643             scope : this,
10644
10645             doRelay : function(foo, bar, hname){
10646                 if(hname == 'down' || this.scope.isExpanded()){
10647                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10648                 }
10649                 return true;
10650             },
10651
10652             forceKeyDown: true
10653         });
10654         
10655         
10656         this.queryDelay = Math.max(this.queryDelay || 10,
10657                 this.mode == 'local' ? 10 : 250);
10658         
10659         
10660         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10661         
10662         if(this.typeAhead){
10663             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10664         }
10665         if(this.editable !== false){
10666             this.inputEl().on("keyup", this.onKeyUp, this);
10667         }
10668         if(this.forceSelection){
10669             this.inputEl().on('blur', this.doForce, this);
10670         }
10671         
10672         if(this.multiple){
10673             this.choices = this.el.select('ul.select2-choices', true).first();
10674             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10675         }
10676     },
10677     
10678     initTickableEvnets: function()
10679     {   
10680         if(this.hiddenName){
10681             
10682             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10683             
10684             this.hiddenField.dom.value =
10685                 this.hiddenValue !== undefined ? this.hiddenValue :
10686                 this.value !== undefined ? this.value : '';
10687
10688             // prevent input submission
10689             this.el.dom.removeAttribute('name');
10690             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10691              
10692              
10693         }
10694         
10695         this.list = this.el.select('ul.dropdown-menu',true).first();
10696         
10697         this.choices = this.el.select('ul.select2-choices', true).first();
10698         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10699         
10700         this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10701          
10702         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10703         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10704         
10705         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10706         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10707         
10708         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10709         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10710         
10711         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10712         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10713         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10714         
10715         this.okBtn.hide();
10716         this.cancelBtn.hide();
10717         
10718         var _this = this;
10719         
10720         (function(){
10721             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10722             _this.list.setWidth(lw);
10723         }).defer(100);
10724         
10725         this.list.on('mouseover', this.onViewOver, this);
10726         this.list.on('mousemove', this.onViewMove, this);
10727         
10728         this.list.on('scroll', this.onViewScroll, this);
10729         
10730         if(!this.tpl){
10731             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>';
10732         }
10733
10734         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10735             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10736         });
10737         
10738         //this.view.wrapEl.setDisplayed(false);
10739         this.view.on('click', this.onViewClick, this);
10740         
10741         
10742         
10743         this.store.on('beforeload', this.onBeforeLoad, this);
10744         this.store.on('load', this.onLoad, this);
10745         this.store.on('loadexception', this.onLoadException, this);
10746         
10747 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10748 //            "up" : function(e){
10749 //                this.inKeyMode = true;
10750 //                this.selectPrev();
10751 //            },
10752 //
10753 //            "down" : function(e){
10754 //                if(!this.isExpanded()){
10755 //                    this.onTriggerClick();
10756 //                }else{
10757 //                    this.inKeyMode = true;
10758 //                    this.selectNext();
10759 //                }
10760 //            },
10761 //
10762 //            "enter" : function(e){
10763 ////                this.onViewClick();
10764 //                //return true;
10765 //                this.collapse();
10766 //                
10767 //                if(this.fireEvent("specialkey", this, e)){
10768 //                    this.onViewClick(false);
10769 //                }
10770 //                
10771 //                return true;
10772 //            },
10773 //
10774 //            "esc" : function(e){
10775 //                this.collapse();
10776 //            },
10777 //
10778 //            "tab" : function(e){
10779 //                this.collapse();
10780 //                
10781 //                if(this.fireEvent("specialkey", this, e)){
10782 //                    this.onViewClick(false);
10783 //                }
10784 //                
10785 //                return true;
10786 //            },
10787 //
10788 //            scope : this,
10789 //
10790 //            doRelay : function(foo, bar, hname){
10791 //                if(hname == 'down' || this.scope.isExpanded()){
10792 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10793 //                }
10794 //                return true;
10795 //            },
10796 //
10797 //            forceKeyDown: true
10798 //        });
10799         
10800         
10801         this.queryDelay = Math.max(this.queryDelay || 10,
10802                 this.mode == 'local' ? 10 : 250);
10803         
10804         
10805         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10806         
10807         if(this.typeAhead){
10808             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10809         }
10810     },
10811
10812     onDestroy : function(){
10813         if(this.view){
10814             this.view.setStore(null);
10815             this.view.el.removeAllListeners();
10816             this.view.el.remove();
10817             this.view.purgeListeners();
10818         }
10819         if(this.list){
10820             this.list.dom.innerHTML  = '';
10821         }
10822         
10823         if(this.store){
10824             this.store.un('beforeload', this.onBeforeLoad, this);
10825             this.store.un('load', this.onLoad, this);
10826             this.store.un('loadexception', this.onLoadException, this);
10827         }
10828         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10829     },
10830
10831     // private
10832     fireKey : function(e){
10833         if(e.isNavKeyPress() && !this.list.isVisible()){
10834             this.fireEvent("specialkey", this, e);
10835         }
10836     },
10837
10838     // private
10839     onResize: function(w, h){
10840 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10841 //        
10842 //        if(typeof w != 'number'){
10843 //            // we do not handle it!?!?
10844 //            return;
10845 //        }
10846 //        var tw = this.trigger.getWidth();
10847 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10848 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10849 //        var x = w - tw;
10850 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10851 //            
10852 //        //this.trigger.setStyle('left', x+'px');
10853 //        
10854 //        if(this.list && this.listWidth === undefined){
10855 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10856 //            this.list.setWidth(lw);
10857 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10858 //        }
10859         
10860     
10861         
10862     },
10863
10864     /**
10865      * Allow or prevent the user from directly editing the field text.  If false is passed,
10866      * the user will only be able to select from the items defined in the dropdown list.  This method
10867      * is the runtime equivalent of setting the 'editable' config option at config time.
10868      * @param {Boolean} value True to allow the user to directly edit the field text
10869      */
10870     setEditable : function(value){
10871         if(value == this.editable){
10872             return;
10873         }
10874         this.editable = value;
10875         if(!value){
10876             this.inputEl().dom.setAttribute('readOnly', true);
10877             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10878             this.inputEl().addClass('x-combo-noedit');
10879         }else{
10880             this.inputEl().dom.setAttribute('readOnly', false);
10881             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10882             this.inputEl().removeClass('x-combo-noedit');
10883         }
10884     },
10885
10886     // private
10887     
10888     onBeforeLoad : function(combo,opts){
10889         if(!this.hasFocus){
10890             return;
10891         }
10892          if (!opts.add) {
10893             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10894          }
10895         this.restrictHeight();
10896         this.selectedIndex = -1;
10897     },
10898
10899     // private
10900     onLoad : function(){
10901         
10902         this.hasQuery = false;
10903         
10904         if(!this.hasFocus){
10905             return;
10906         }
10907         
10908         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10909             this.loading.hide();
10910         }
10911         
10912         if(this.store.getCount() > 0){
10913             this.expand();
10914             this.restrictHeight();
10915             if(this.lastQuery == this.allQuery){
10916                 if(this.editable && !this.tickable){
10917                     this.inputEl().dom.select();
10918                 }
10919                 if(!this.selectByValue(this.value, true) && this.autoFocus){
10920                     this.select(0, true);
10921                 }
10922             }else{
10923                 if(this.autoFocus){
10924                     this.selectNext();
10925                 }
10926                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10927                     this.taTask.delay(this.typeAheadDelay);
10928                 }
10929             }
10930         }else{
10931             this.onEmptyResults();
10932         }
10933         
10934         //this.el.focus();
10935     },
10936     // private
10937     onLoadException : function()
10938     {
10939         this.hasQuery = false;
10940         
10941         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10942             this.loading.hide();
10943         }
10944         
10945         this.collapse();
10946         Roo.log(this.store.reader.jsonData);
10947         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
10948             // fixme
10949             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
10950         }
10951         
10952         
10953     },
10954     // private
10955     onTypeAhead : function(){
10956         if(this.store.getCount() > 0){
10957             var r = this.store.getAt(0);
10958             var newValue = r.data[this.displayField];
10959             var len = newValue.length;
10960             var selStart = this.getRawValue().length;
10961             
10962             if(selStart != len){
10963                 this.setRawValue(newValue);
10964                 this.selectText(selStart, newValue.length);
10965             }
10966         }
10967     },
10968
10969     // private
10970     onSelect : function(record, index){
10971         
10972         if(this.fireEvent('beforeselect', this, record, index) !== false){
10973         
10974             this.setFromData(index > -1 ? record.data : false);
10975             
10976             this.collapse();
10977             this.fireEvent('select', this, record, index);
10978         }
10979     },
10980
10981     /**
10982      * Returns the currently selected field value or empty string if no value is set.
10983      * @return {String} value The selected value
10984      */
10985     getValue : function(){
10986         
10987         if(this.multiple){
10988             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
10989         }
10990         
10991         if(this.valueField){
10992             return typeof this.value != 'undefined' ? this.value : '';
10993         }else{
10994             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
10995         }
10996     },
10997
10998     /**
10999      * Clears any text/value currently set in the field
11000      */
11001     clearValue : function(){
11002         if(this.hiddenField){
11003             this.hiddenField.dom.value = '';
11004         }
11005         this.value = '';
11006         this.setRawValue('');
11007         this.lastSelectionText = '';
11008         
11009     },
11010
11011     /**
11012      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11013      * will be displayed in the field.  If the value does not match the data value of an existing item,
11014      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11015      * Otherwise the field will be blank (although the value will still be set).
11016      * @param {String} value The value to match
11017      */
11018     setValue : function(v){
11019         if(this.multiple){
11020             this.syncValue();
11021             return;
11022         }
11023         
11024         var text = v;
11025         if(this.valueField){
11026             var r = this.findRecord(this.valueField, v);
11027             if(r){
11028                 text = r.data[this.displayField];
11029             }else if(this.valueNotFoundText !== undefined){
11030                 text = this.valueNotFoundText;
11031             }
11032         }
11033         this.lastSelectionText = text;
11034         if(this.hiddenField){
11035             this.hiddenField.dom.value = v;
11036         }
11037         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11038         this.value = v;
11039     },
11040     /**
11041      * @property {Object} the last set data for the element
11042      */
11043     
11044     lastData : false,
11045     /**
11046      * Sets the value of the field based on a object which is related to the record format for the store.
11047      * @param {Object} value the value to set as. or false on reset?
11048      */
11049     setFromData : function(o){
11050         
11051         if(this.multiple){
11052             this.addItem(o);
11053             return;
11054         }
11055             
11056         var dv = ''; // display value
11057         var vv = ''; // value value..
11058         this.lastData = o;
11059         if (this.displayField) {
11060             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11061         } else {
11062             // this is an error condition!!!
11063             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11064         }
11065         
11066         if(this.valueField){
11067             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11068         }
11069         
11070         if(this.hiddenField){
11071             this.hiddenField.dom.value = vv;
11072             
11073             this.lastSelectionText = dv;
11074             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11075             this.value = vv;
11076             return;
11077         }
11078         // no hidden field.. - we store the value in 'value', but still display
11079         // display field!!!!
11080         this.lastSelectionText = dv;
11081         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11082         this.value = vv;
11083         
11084         
11085     },
11086     // private
11087     reset : function(){
11088         // overridden so that last data is reset..
11089         this.setValue(this.originalValue);
11090         this.clearInvalid();
11091         this.lastData = false;
11092         if (this.view) {
11093             this.view.clearSelections();
11094         }
11095     },
11096     // private
11097     findRecord : function(prop, value){
11098         var record;
11099         if(this.store.getCount() > 0){
11100             this.store.each(function(r){
11101                 if(r.data[prop] == value){
11102                     record = r;
11103                     return false;
11104                 }
11105                 return true;
11106             });
11107         }
11108         return record;
11109     },
11110     
11111     getName: function()
11112     {
11113         // returns hidden if it's set..
11114         if (!this.rendered) {return ''};
11115         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11116         
11117     },
11118     // private
11119     onViewMove : function(e, t){
11120         this.inKeyMode = false;
11121     },
11122
11123     // private
11124     onViewOver : function(e, t){
11125         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11126             return;
11127         }
11128         var item = this.view.findItemFromChild(t);
11129         
11130         if(item){
11131             var index = this.view.indexOf(item);
11132             this.select(index, false);
11133         }
11134     },
11135
11136     // private
11137     onViewClick : function(view, doFocus, el, e)
11138     {
11139         var index = this.view.getSelectedIndexes()[0];
11140         
11141         var r = this.store.getAt(index);
11142         
11143         if(this.tickable){
11144             
11145             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11146                 return;
11147             }
11148             
11149             var rm = false;
11150             var _this = this;
11151             
11152             Roo.each(this.tickItems, function(v,k){
11153                 
11154                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11155                     _this.tickItems.splice(k, 1);
11156                     rm = true;
11157                     return;
11158                 }
11159             })
11160             
11161             if(rm){
11162                 return;
11163             }
11164             
11165             this.tickItems.push(r.data);
11166             return;
11167         }
11168         
11169         if(r){
11170             this.onSelect(r, index);
11171         }
11172         if(doFocus !== false && !this.blockFocus){
11173             this.inputEl().focus();
11174         }
11175     },
11176
11177     // private
11178     restrictHeight : function(){
11179         //this.innerList.dom.style.height = '';
11180         //var inner = this.innerList.dom;
11181         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11182         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11183         //this.list.beginUpdate();
11184         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11185         this.list.alignTo(this.inputEl(), this.listAlign);
11186         //this.list.endUpdate();
11187     },
11188
11189     // private
11190     onEmptyResults : function(){
11191         this.collapse();
11192     },
11193
11194     /**
11195      * Returns true if the dropdown list is expanded, else false.
11196      */
11197     isExpanded : function(){
11198         return this.list.isVisible();
11199     },
11200
11201     /**
11202      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11203      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11204      * @param {String} value The data value of the item to select
11205      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11206      * selected item if it is not currently in view (defaults to true)
11207      * @return {Boolean} True if the value matched an item in the list, else false
11208      */
11209     selectByValue : function(v, scrollIntoView){
11210         if(v !== undefined && v !== null){
11211             var r = this.findRecord(this.valueField || this.displayField, v);
11212             if(r){
11213                 this.select(this.store.indexOf(r), scrollIntoView);
11214                 return true;
11215             }
11216         }
11217         return false;
11218     },
11219
11220     /**
11221      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11222      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11223      * @param {Number} index The zero-based index of the list item to select
11224      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11225      * selected item if it is not currently in view (defaults to true)
11226      */
11227     select : function(index, scrollIntoView){
11228         this.selectedIndex = index;
11229         this.view.select(index);
11230         if(scrollIntoView !== false){
11231             var el = this.view.getNode(index);
11232             if(el){
11233                 //this.innerList.scrollChildIntoView(el, false);
11234                 
11235             }
11236         }
11237     },
11238
11239     // private
11240     selectNext : function(){
11241         var ct = this.store.getCount();
11242         if(ct > 0){
11243             if(this.selectedIndex == -1){
11244                 this.select(0);
11245             }else if(this.selectedIndex < ct-1){
11246                 this.select(this.selectedIndex+1);
11247             }
11248         }
11249     },
11250
11251     // private
11252     selectPrev : function(){
11253         var ct = this.store.getCount();
11254         if(ct > 0){
11255             if(this.selectedIndex == -1){
11256                 this.select(0);
11257             }else if(this.selectedIndex != 0){
11258                 this.select(this.selectedIndex-1);
11259             }
11260         }
11261     },
11262
11263     // private
11264     onKeyUp : function(e){
11265         if(this.editable !== false && !e.isSpecialKey()){
11266             this.lastKey = e.getKey();
11267             this.dqTask.delay(this.queryDelay);
11268         }
11269     },
11270
11271     // private
11272     validateBlur : function(){
11273         return !this.list || !this.list.isVisible();   
11274     },
11275
11276     // private
11277     initQuery : function(){
11278         this.doQuery(this.getRawValue());
11279     },
11280
11281     // private
11282     doForce : function(){
11283         if(this.inputEl().dom.value.length > 0){
11284             this.inputEl().dom.value =
11285                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11286              
11287         }
11288     },
11289
11290     /**
11291      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11292      * query allowing the query action to be canceled if needed.
11293      * @param {String} query The SQL query to execute
11294      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11295      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11296      * saved in the current store (defaults to false)
11297      */
11298     doQuery : function(q, forceAll){
11299         
11300         if(q === undefined || q === null){
11301             q = '';
11302         }
11303         var qe = {
11304             query: q,
11305             forceAll: forceAll,
11306             combo: this,
11307             cancel:false
11308         };
11309         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11310             return false;
11311         }
11312         q = qe.query;
11313         
11314         forceAll = qe.forceAll;
11315         if(forceAll === true || (q.length >= this.minChars)){
11316             
11317             this.hasQuery = true;
11318             
11319             if(this.lastQuery != q || this.alwaysQuery){
11320                 this.lastQuery = q;
11321                 if(this.mode == 'local'){
11322                     this.selectedIndex = -1;
11323                     if(forceAll){
11324                         this.store.clearFilter();
11325                     }else{
11326                         this.store.filter(this.displayField, q);
11327                     }
11328                     this.onLoad();
11329                 }else{
11330                     this.store.baseParams[this.queryParam] = q;
11331                     
11332                     var options = {params : this.getParams(q)};
11333                     
11334                     if(this.loadNext){
11335                         options.add = true;
11336                         options.params.start = this.page * this.pageSize;
11337                     }
11338                     
11339                     this.store.load(options);
11340                     /*
11341                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11342                      *  we should expand the list on onLoad
11343                      *  so command out it
11344                      */
11345 //                    this.expand();
11346                 }
11347             }else{
11348                 this.selectedIndex = -1;
11349                 this.onLoad();   
11350             }
11351         }
11352         
11353         this.loadNext = false;
11354     },
11355
11356     // private
11357     getParams : function(q){
11358         var p = {};
11359         //p[this.queryParam] = q;
11360         
11361         if(this.pageSize){
11362             p.start = 0;
11363             p.limit = this.pageSize;
11364         }
11365         return p;
11366     },
11367
11368     /**
11369      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11370      */
11371     collapse : function(){
11372         if(!this.isExpanded()){
11373             return;
11374         }
11375         
11376         this.hasFocus = false;
11377         
11378         this.list.hide();
11379         
11380         if(this.tickable){
11381             this.okBtn.hide();
11382             this.cancelBtn.hide();
11383             this.trigger.show();
11384         }
11385         
11386         Roo.get(document).un('mousedown', this.collapseIf, this);
11387         Roo.get(document).un('mousewheel', this.collapseIf, this);
11388         if (!this.editable) {
11389             Roo.get(document).un('keydown', this.listKeyPress, this);
11390         }
11391         this.fireEvent('collapse', this);
11392     },
11393
11394     // private
11395     collapseIf : function(e){
11396         var in_combo  = e.within(this.el);
11397         var in_list =  e.within(this.list);
11398         
11399         if (in_combo || in_list) {
11400             //e.stopPropagation();
11401             return;
11402         }
11403         
11404         if(this.tickable){
11405             this.onTickableFooterButtonClick(e, false, false);
11406         }
11407
11408         this.collapse();
11409         
11410     },
11411
11412     /**
11413      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11414      */
11415     expand : function(){
11416        
11417         if(this.isExpanded() || !this.hasFocus){
11418             return;
11419         }
11420          Roo.log('expand');
11421         this.list.alignTo(this.inputEl(), this.listAlign);
11422         this.list.show();
11423         
11424         if(this.tickable){
11425             
11426             this.tickItems = Roo.apply([], this.item);
11427             
11428             this.okBtn.show();
11429             this.cancelBtn.show();
11430             this.trigger.hide();
11431             
11432         }
11433         
11434         Roo.get(document).on('mousedown', this.collapseIf, this);
11435         Roo.get(document).on('mousewheel', this.collapseIf, this);
11436         if (!this.editable) {
11437             Roo.get(document).on('keydown', this.listKeyPress, this);
11438         }
11439         
11440         this.fireEvent('expand', this);
11441     },
11442
11443     // private
11444     // Implements the default empty TriggerField.onTriggerClick function
11445     onTriggerClick : function(e)
11446     {
11447         Roo.log('trigger click');
11448         
11449         if(this.disabled){
11450             return;
11451         }
11452         
11453         this.page = 0;
11454         this.loadNext = false;
11455         
11456         if(this.isExpanded()){
11457             this.collapse();
11458             if (!this.blockFocus) {
11459                 this.inputEl().focus();
11460             }
11461             
11462         }else {
11463             this.hasFocus = true;
11464             if(this.triggerAction == 'all') {
11465                 this.doQuery(this.allQuery, true);
11466             } else {
11467                 this.doQuery(this.getRawValue());
11468             }
11469             if (!this.blockFocus) {
11470                 this.inputEl().focus();
11471             }
11472         }
11473     },
11474     
11475     onTickableTriggerClick : function(e)
11476     {
11477         if(this.disabled){
11478             return;
11479         }
11480         
11481         this.page = 0;
11482         this.loadNext = false;
11483         this.hasFocus = true;
11484         
11485         if(this.triggerAction == 'all') {
11486             this.doQuery(this.allQuery, true);
11487         } else {
11488             this.doQuery(this.getRawValue());
11489         }
11490     },
11491     
11492     onSearchFieldClick : function(e)
11493     {
11494         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11495             return;
11496         }
11497         
11498         this.page = 0;
11499         this.loadNext = false;
11500         this.hasFocus = true;
11501         
11502         if(this.triggerAction == 'all') {
11503             this.doQuery(this.allQuery, true);
11504         } else {
11505             this.doQuery(this.getRawValue());
11506         }
11507     },
11508     
11509     listKeyPress : function(e)
11510     {
11511         //Roo.log('listkeypress');
11512         // scroll to first matching element based on key pres..
11513         if (e.isSpecialKey()) {
11514             return false;
11515         }
11516         var k = String.fromCharCode(e.getKey()).toUpperCase();
11517         //Roo.log(k);
11518         var match  = false;
11519         var csel = this.view.getSelectedNodes();
11520         var cselitem = false;
11521         if (csel.length) {
11522             var ix = this.view.indexOf(csel[0]);
11523             cselitem  = this.store.getAt(ix);
11524             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11525                 cselitem = false;
11526             }
11527             
11528         }
11529         
11530         this.store.each(function(v) { 
11531             if (cselitem) {
11532                 // start at existing selection.
11533                 if (cselitem.id == v.id) {
11534                     cselitem = false;
11535                 }
11536                 return true;
11537             }
11538                 
11539             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11540                 match = this.store.indexOf(v);
11541                 return false;
11542             }
11543             return true;
11544         }, this);
11545         
11546         if (match === false) {
11547             return true; // no more action?
11548         }
11549         // scroll to?
11550         this.view.select(match);
11551         var sn = Roo.get(this.view.getSelectedNodes()[0])
11552         //sn.scrollIntoView(sn.dom.parentNode, false);
11553     },
11554     
11555     onViewScroll : function(e, t){
11556         
11557         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11558             return;
11559         }
11560         
11561         this.hasQuery = true;
11562         
11563         this.loading = this.list.select('.loading', true).first();
11564         
11565         if(this.loading === null){
11566             this.list.createChild({
11567                 tag: 'div',
11568                 cls: 'loading select2-more-results select2-active',
11569                 html: 'Loading more results...'
11570             })
11571             
11572             this.loading = this.list.select('.loading', true).first();
11573             
11574             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11575             
11576             this.loading.hide();
11577         }
11578         
11579         this.loading.show();
11580         
11581         var _combo = this;
11582         
11583         this.page++;
11584         this.loadNext = true;
11585         
11586         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11587         
11588         return;
11589     },
11590     
11591     addItem : function(o)
11592     {   
11593         var dv = ''; // display value
11594         
11595         if (this.displayField) {
11596             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11597         } else {
11598             // this is an error condition!!!
11599             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11600         }
11601         
11602         if(!dv.length){
11603             return;
11604         }
11605         
11606         var choice = this.choices.createChild({
11607             tag: 'li',
11608             cls: 'select2-search-choice',
11609             cn: [
11610                 {
11611                     tag: 'div',
11612                     html: dv
11613                 },
11614                 {
11615                     tag: 'a',
11616                     href: '#',
11617                     cls: 'select2-search-choice-close',
11618                     tabindex: '-1'
11619                 }
11620             ]
11621             
11622         }, this.searchField);
11623         
11624         var close = choice.select('a.select2-search-choice-close', true).first()
11625         
11626         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11627         
11628         this.item.push(o);
11629         
11630         this.lastData = o;
11631         
11632         this.syncValue();
11633         
11634         this.inputEl().dom.value = '';
11635         
11636     },
11637     
11638     onRemoveItem : function(e, _self, o)
11639     {
11640         e.preventDefault();
11641         var index = this.item.indexOf(o.data) * 1;
11642         
11643         if( index < 0){
11644             Roo.log('not this item?!');
11645             return;
11646         }
11647         
11648         this.item.splice(index, 1);
11649         o.item.remove();
11650         
11651         this.syncValue();
11652         
11653         this.fireEvent('remove', this, e);
11654         
11655     },
11656     
11657     syncValue : function()
11658     {
11659         if(!this.item.length){
11660             this.clearValue();
11661             return;
11662         }
11663             
11664         var value = [];
11665         var _this = this;
11666         Roo.each(this.item, function(i){
11667             if(_this.valueField){
11668                 value.push(i[_this.valueField]);
11669                 return;
11670             }
11671
11672             value.push(i);
11673         });
11674
11675         this.value = value.join(',');
11676
11677         if(this.hiddenField){
11678             this.hiddenField.dom.value = this.value;
11679         }
11680     },
11681     
11682     clearItem : function()
11683     {
11684         if(!this.multiple){
11685             return;
11686         }
11687         
11688         this.item = [];
11689         
11690         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11691            c.remove();
11692         });
11693         
11694         this.syncValue();
11695     },
11696     
11697     inputEl: function ()
11698     {
11699         if(this.tickable){
11700             return this.searchField;
11701         }
11702         return this.el.select('input.form-control',true).first();
11703     },
11704     
11705     
11706     onTickableFooterButtonClick : function(e, btn, el)
11707     {
11708         e.preventDefault();
11709         
11710         if(btn && btn.name == 'cancel'){
11711             this.tickItems = Roo.apply([], this.item);
11712             this.collapse();
11713             return;
11714         }
11715         
11716         this.clearItem();
11717         
11718         var _this = this;
11719         
11720         Roo.each(this.tickItems, function(o){
11721             _this.addItem(o);
11722         });
11723         
11724         this.collapse();
11725         
11726     }
11727     
11728     
11729
11730     /** 
11731     * @cfg {Boolean} grow 
11732     * @hide 
11733     */
11734     /** 
11735     * @cfg {Number} growMin 
11736     * @hide 
11737     */
11738     /** 
11739     * @cfg {Number} growMax 
11740     * @hide 
11741     */
11742     /**
11743      * @hide
11744      * @method autoSize
11745      */
11746 });
11747 /*
11748  * Based on:
11749  * Ext JS Library 1.1.1
11750  * Copyright(c) 2006-2007, Ext JS, LLC.
11751  *
11752  * Originally Released Under LGPL - original licence link has changed is not relivant.
11753  *
11754  * Fork - LGPL
11755  * <script type="text/javascript">
11756  */
11757
11758 /**
11759  * @class Roo.View
11760  * @extends Roo.util.Observable
11761  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11762  * This class also supports single and multi selection modes. <br>
11763  * Create a data model bound view:
11764  <pre><code>
11765  var store = new Roo.data.Store(...);
11766
11767  var view = new Roo.View({
11768     el : "my-element",
11769     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11770  
11771     singleSelect: true,
11772     selectedClass: "ydataview-selected",
11773     store: store
11774  });
11775
11776  // listen for node click?
11777  view.on("click", function(vw, index, node, e){
11778  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11779  });
11780
11781  // load XML data
11782  dataModel.load("foobar.xml");
11783  </code></pre>
11784  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11785  * <br><br>
11786  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11787  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11788  * 
11789  * Note: old style constructor is still suported (container, template, config)
11790  * 
11791  * @constructor
11792  * Create a new View
11793  * @param {Object} config The config object
11794  * 
11795  */
11796 Roo.View = function(config, depreciated_tpl, depreciated_config){
11797     
11798     this.parent = false;
11799     
11800     if (typeof(depreciated_tpl) == 'undefined') {
11801         // new way.. - universal constructor.
11802         Roo.apply(this, config);
11803         this.el  = Roo.get(this.el);
11804     } else {
11805         // old format..
11806         this.el  = Roo.get(config);
11807         this.tpl = depreciated_tpl;
11808         Roo.apply(this, depreciated_config);
11809     }
11810     this.wrapEl  = this.el.wrap().wrap();
11811     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11812     
11813     
11814     if(typeof(this.tpl) == "string"){
11815         this.tpl = new Roo.Template(this.tpl);
11816     } else {
11817         // support xtype ctors..
11818         this.tpl = new Roo.factory(this.tpl, Roo);
11819     }
11820     
11821     
11822     this.tpl.compile();
11823     
11824     /** @private */
11825     this.addEvents({
11826         /**
11827          * @event beforeclick
11828          * Fires before a click is processed. Returns false to cancel the default action.
11829          * @param {Roo.View} this
11830          * @param {Number} index The index of the target node
11831          * @param {HTMLElement} node The target node
11832          * @param {Roo.EventObject} e The raw event object
11833          */
11834             "beforeclick" : true,
11835         /**
11836          * @event click
11837          * Fires when a template node is clicked.
11838          * @param {Roo.View} this
11839          * @param {Number} index The index of the target node
11840          * @param {HTMLElement} node The target node
11841          * @param {Roo.EventObject} e The raw event object
11842          */
11843             "click" : true,
11844         /**
11845          * @event dblclick
11846          * Fires when a template node is double clicked.
11847          * @param {Roo.View} this
11848          * @param {Number} index The index of the target node
11849          * @param {HTMLElement} node The target node
11850          * @param {Roo.EventObject} e The raw event object
11851          */
11852             "dblclick" : true,
11853         /**
11854          * @event contextmenu
11855          * Fires when a template node is right clicked.
11856          * @param {Roo.View} this
11857          * @param {Number} index The index of the target node
11858          * @param {HTMLElement} node The target node
11859          * @param {Roo.EventObject} e The raw event object
11860          */
11861             "contextmenu" : true,
11862         /**
11863          * @event selectionchange
11864          * Fires when the selected nodes change.
11865          * @param {Roo.View} this
11866          * @param {Array} selections Array of the selected nodes
11867          */
11868             "selectionchange" : true,
11869     
11870         /**
11871          * @event beforeselect
11872          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11873          * @param {Roo.View} this
11874          * @param {HTMLElement} node The node to be selected
11875          * @param {Array} selections Array of currently selected nodes
11876          */
11877             "beforeselect" : true,
11878         /**
11879          * @event preparedata
11880          * Fires on every row to render, to allow you to change the data.
11881          * @param {Roo.View} this
11882          * @param {Object} data to be rendered (change this)
11883          */
11884           "preparedata" : true
11885           
11886           
11887         });
11888
11889
11890
11891     this.el.on({
11892         "click": this.onClick,
11893         "dblclick": this.onDblClick,
11894         "contextmenu": this.onContextMenu,
11895         scope:this
11896     });
11897
11898     this.selections = [];
11899     this.nodes = [];
11900     this.cmp = new Roo.CompositeElementLite([]);
11901     if(this.store){
11902         this.store = Roo.factory(this.store, Roo.data);
11903         this.setStore(this.store, true);
11904     }
11905     
11906     if ( this.footer && this.footer.xtype) {
11907            
11908          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11909         
11910         this.footer.dataSource = this.store
11911         this.footer.container = fctr;
11912         this.footer = Roo.factory(this.footer, Roo);
11913         fctr.insertFirst(this.el);
11914         
11915         // this is a bit insane - as the paging toolbar seems to detach the el..
11916 //        dom.parentNode.parentNode.parentNode
11917          // they get detached?
11918     }
11919     
11920     
11921     Roo.View.superclass.constructor.call(this);
11922     
11923     
11924 };
11925
11926 Roo.extend(Roo.View, Roo.util.Observable, {
11927     
11928      /**
11929      * @cfg {Roo.data.Store} store Data store to load data from.
11930      */
11931     store : false,
11932     
11933     /**
11934      * @cfg {String|Roo.Element} el The container element.
11935      */
11936     el : '',
11937     
11938     /**
11939      * @cfg {String|Roo.Template} tpl The template used by this View 
11940      */
11941     tpl : false,
11942     /**
11943      * @cfg {String} dataName the named area of the template to use as the data area
11944      *                          Works with domtemplates roo-name="name"
11945      */
11946     dataName: false,
11947     /**
11948      * @cfg {String} selectedClass The css class to add to selected nodes
11949      */
11950     selectedClass : "x-view-selected",
11951      /**
11952      * @cfg {String} emptyText The empty text to show when nothing is loaded.
11953      */
11954     emptyText : "",
11955     
11956     /**
11957      * @cfg {String} text to display on mask (default Loading)
11958      */
11959     mask : false,
11960     /**
11961      * @cfg {Boolean} multiSelect Allow multiple selection
11962      */
11963     multiSelect : false,
11964     /**
11965      * @cfg {Boolean} singleSelect Allow single selection
11966      */
11967     singleSelect:  false,
11968     
11969     /**
11970      * @cfg {Boolean} toggleSelect - selecting 
11971      */
11972     toggleSelect : false,
11973     
11974     /**
11975      * @cfg {Boolean} tickable - selecting 
11976      */
11977     tickable : false,
11978     
11979     /**
11980      * Returns the element this view is bound to.
11981      * @return {Roo.Element}
11982      */
11983     getEl : function(){
11984         return this.wrapEl;
11985     },
11986     
11987     
11988
11989     /**
11990      * Refreshes the view. - called by datachanged on the store. - do not call directly.
11991      */
11992     refresh : function(){
11993         Roo.log('refresh');
11994         var t = this.tpl;
11995         
11996         // if we are using something like 'domtemplate', then
11997         // the what gets used is:
11998         // t.applySubtemplate(NAME, data, wrapping data..)
11999         // the outer template then get' applied with
12000         //     the store 'extra data'
12001         // and the body get's added to the
12002         //      roo-name="data" node?
12003         //      <span class='roo-tpl-{name}'></span> ?????
12004         
12005         
12006         
12007         this.clearSelections();
12008         this.el.update("");
12009         var html = [];
12010         var records = this.store.getRange();
12011         if(records.length < 1) {
12012             
12013             // is this valid??  = should it render a template??
12014             
12015             this.el.update(this.emptyText);
12016             return;
12017         }
12018         var el = this.el;
12019         if (this.dataName) {
12020             this.el.update(t.apply(this.store.meta)); //????
12021             el = this.el.child('.roo-tpl-' + this.dataName);
12022         }
12023         
12024         for(var i = 0, len = records.length; i < len; i++){
12025             var data = this.prepareData(records[i].data, i, records[i]);
12026             this.fireEvent("preparedata", this, data, i, records[i]);
12027             
12028             var d = Roo.apply({}, data);
12029             
12030             if(this.tickable){
12031                 Roo.apply(d, {'roo-id' : Roo.id()});
12032                 
12033                 var _this = this;
12034             
12035                 Roo.each(this.parent.item, function(item){
12036                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12037                         return;
12038                     }
12039                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12040                 });
12041             }
12042             
12043             html[html.length] = Roo.util.Format.trim(
12044                 this.dataName ?
12045                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12046                     t.apply(d)
12047             );
12048         }
12049         
12050         
12051         
12052         el.update(html.join(""));
12053         this.nodes = el.dom.childNodes;
12054         this.updateIndexes(0);
12055     },
12056     
12057
12058     /**
12059      * Function to override to reformat the data that is sent to
12060      * the template for each node.
12061      * DEPRICATED - use the preparedata event handler.
12062      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12063      * a JSON object for an UpdateManager bound view).
12064      */
12065     prepareData : function(data, index, record)
12066     {
12067         this.fireEvent("preparedata", this, data, index, record);
12068         return data;
12069     },
12070
12071     onUpdate : function(ds, record){
12072          Roo.log('on update');   
12073         this.clearSelections();
12074         var index = this.store.indexOf(record);
12075         var n = this.nodes[index];
12076         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12077         n.parentNode.removeChild(n);
12078         this.updateIndexes(index, index);
12079     },
12080
12081     
12082     
12083 // --------- FIXME     
12084     onAdd : function(ds, records, index)
12085     {
12086         Roo.log(['on Add', ds, records, index] );        
12087         this.clearSelections();
12088         if(this.nodes.length == 0){
12089             this.refresh();
12090             return;
12091         }
12092         var n = this.nodes[index];
12093         for(var i = 0, len = records.length; i < len; i++){
12094             var d = this.prepareData(records[i].data, i, records[i]);
12095             if(n){
12096                 this.tpl.insertBefore(n, d);
12097             }else{
12098                 
12099                 this.tpl.append(this.el, d);
12100             }
12101         }
12102         this.updateIndexes(index);
12103     },
12104
12105     onRemove : function(ds, record, index){
12106         Roo.log('onRemove');
12107         this.clearSelections();
12108         var el = this.dataName  ?
12109             this.el.child('.roo-tpl-' + this.dataName) :
12110             this.el; 
12111         
12112         el.dom.removeChild(this.nodes[index]);
12113         this.updateIndexes(index);
12114     },
12115
12116     /**
12117      * Refresh an individual node.
12118      * @param {Number} index
12119      */
12120     refreshNode : function(index){
12121         this.onUpdate(this.store, this.store.getAt(index));
12122     },
12123
12124     updateIndexes : function(startIndex, endIndex){
12125         var ns = this.nodes;
12126         startIndex = startIndex || 0;
12127         endIndex = endIndex || ns.length - 1;
12128         for(var i = startIndex; i <= endIndex; i++){
12129             ns[i].nodeIndex = i;
12130         }
12131     },
12132
12133     /**
12134      * Changes the data store this view uses and refresh the view.
12135      * @param {Store} store
12136      */
12137     setStore : function(store, initial){
12138         if(!initial && this.store){
12139             this.store.un("datachanged", this.refresh);
12140             this.store.un("add", this.onAdd);
12141             this.store.un("remove", this.onRemove);
12142             this.store.un("update", this.onUpdate);
12143             this.store.un("clear", this.refresh);
12144             this.store.un("beforeload", this.onBeforeLoad);
12145             this.store.un("load", this.onLoad);
12146             this.store.un("loadexception", this.onLoad);
12147         }
12148         if(store){
12149           
12150             store.on("datachanged", this.refresh, this);
12151             store.on("add", this.onAdd, this);
12152             store.on("remove", this.onRemove, this);
12153             store.on("update", this.onUpdate, this);
12154             store.on("clear", this.refresh, this);
12155             store.on("beforeload", this.onBeforeLoad, this);
12156             store.on("load", this.onLoad, this);
12157             store.on("loadexception", this.onLoad, this);
12158         }
12159         
12160         if(store){
12161             this.refresh();
12162         }
12163     },
12164     /**
12165      * onbeforeLoad - masks the loading area.
12166      *
12167      */
12168     onBeforeLoad : function(store,opts)
12169     {
12170          Roo.log('onBeforeLoad');   
12171         if (!opts.add) {
12172             this.el.update("");
12173         }
12174         this.el.mask(this.mask ? this.mask : "Loading" ); 
12175     },
12176     onLoad : function ()
12177     {
12178         this.el.unmask();
12179     },
12180     
12181
12182     /**
12183      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12184      * @param {HTMLElement} node
12185      * @return {HTMLElement} The template node
12186      */
12187     findItemFromChild : function(node){
12188         var el = this.dataName  ?
12189             this.el.child('.roo-tpl-' + this.dataName,true) :
12190             this.el.dom; 
12191         
12192         if(!node || node.parentNode == el){
12193                     return node;
12194             }
12195             var p = node.parentNode;
12196             while(p && p != el){
12197             if(p.parentNode == el){
12198                 return p;
12199             }
12200             p = p.parentNode;
12201         }
12202             return null;
12203     },
12204
12205     /** @ignore */
12206     onClick : function(e){
12207         var item = this.findItemFromChild(e.getTarget());
12208         if(item){
12209             var index = this.indexOf(item);
12210             if(this.onItemClick(item, index, e) !== false){
12211                 this.fireEvent("click", this, index, item, e);
12212             }
12213         }else{
12214             this.clearSelections();
12215         }
12216     },
12217
12218     /** @ignore */
12219     onContextMenu : function(e){
12220         var item = this.findItemFromChild(e.getTarget());
12221         if(item){
12222             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12223         }
12224     },
12225
12226     /** @ignore */
12227     onDblClick : function(e){
12228         var item = this.findItemFromChild(e.getTarget());
12229         if(item){
12230             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12231         }
12232     },
12233
12234     onItemClick : function(item, index, e)
12235     {
12236         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12237             return false;
12238         }
12239         if (this.toggleSelect) {
12240             var m = this.isSelected(item) ? 'unselect' : 'select';
12241             Roo.log(m);
12242             var _t = this;
12243             _t[m](item, true, false);
12244             return true;
12245         }
12246         if(this.multiSelect || this.singleSelect){
12247             if(this.multiSelect && e.shiftKey && this.lastSelection){
12248                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12249             }else{
12250                 this.select(item, this.multiSelect && e.ctrlKey);
12251                 this.lastSelection = item;
12252             }
12253             
12254             if(!this.tickable){
12255                 e.preventDefault();
12256             }
12257             
12258         }
12259         return true;
12260     },
12261
12262     /**
12263      * Get the number of selected nodes.
12264      * @return {Number}
12265      */
12266     getSelectionCount : function(){
12267         return this.selections.length;
12268     },
12269
12270     /**
12271      * Get the currently selected nodes.
12272      * @return {Array} An array of HTMLElements
12273      */
12274     getSelectedNodes : function(){
12275         return this.selections;
12276     },
12277
12278     /**
12279      * Get the indexes of the selected nodes.
12280      * @return {Array}
12281      */
12282     getSelectedIndexes : function(){
12283         var indexes = [], s = this.selections;
12284         for(var i = 0, len = s.length; i < len; i++){
12285             indexes.push(s[i].nodeIndex);
12286         }
12287         return indexes;
12288     },
12289
12290     /**
12291      * Clear all selections
12292      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12293      */
12294     clearSelections : function(suppressEvent){
12295         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12296             this.cmp.elements = this.selections;
12297             this.cmp.removeClass(this.selectedClass);
12298             this.selections = [];
12299             if(!suppressEvent){
12300                 this.fireEvent("selectionchange", this, this.selections);
12301             }
12302         }
12303     },
12304
12305     /**
12306      * Returns true if the passed node is selected
12307      * @param {HTMLElement/Number} node The node or node index
12308      * @return {Boolean}
12309      */
12310     isSelected : function(node){
12311         var s = this.selections;
12312         if(s.length < 1){
12313             return false;
12314         }
12315         node = this.getNode(node);
12316         return s.indexOf(node) !== -1;
12317     },
12318
12319     /**
12320      * Selects nodes.
12321      * @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
12322      * @param {Boolean} keepExisting (optional) true to keep existing selections
12323      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12324      */
12325     select : function(nodeInfo, keepExisting, suppressEvent){
12326         if(nodeInfo instanceof Array){
12327             if(!keepExisting){
12328                 this.clearSelections(true);
12329             }
12330             for(var i = 0, len = nodeInfo.length; i < len; i++){
12331                 this.select(nodeInfo[i], true, true);
12332             }
12333             return;
12334         } 
12335         var node = this.getNode(nodeInfo);
12336         if(!node || this.isSelected(node)){
12337             return; // already selected.
12338         }
12339         if(!keepExisting){
12340             this.clearSelections(true);
12341         }
12342         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12343             Roo.fly(node).addClass(this.selectedClass);
12344             this.selections.push(node);
12345             if(!suppressEvent){
12346                 this.fireEvent("selectionchange", this, this.selections);
12347             }
12348         }
12349         
12350         
12351     },
12352       /**
12353      * Unselects nodes.
12354      * @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
12355      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12356      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12357      */
12358     unselect : function(nodeInfo, keepExisting, suppressEvent)
12359     {
12360         if(nodeInfo instanceof Array){
12361             Roo.each(this.selections, function(s) {
12362                 this.unselect(s, nodeInfo);
12363             }, this);
12364             return;
12365         }
12366         var node = this.getNode(nodeInfo);
12367         if(!node || !this.isSelected(node)){
12368             Roo.log("not selected");
12369             return; // not selected.
12370         }
12371         // fireevent???
12372         var ns = [];
12373         Roo.each(this.selections, function(s) {
12374             if (s == node ) {
12375                 Roo.fly(node).removeClass(this.selectedClass);
12376
12377                 return;
12378             }
12379             ns.push(s);
12380         },this);
12381         
12382         this.selections= ns;
12383         this.fireEvent("selectionchange", this, this.selections);
12384     },
12385
12386     /**
12387      * Gets a template node.
12388      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12389      * @return {HTMLElement} The node or null if it wasn't found
12390      */
12391     getNode : function(nodeInfo){
12392         if(typeof nodeInfo == "string"){
12393             return document.getElementById(nodeInfo);
12394         }else if(typeof nodeInfo == "number"){
12395             return this.nodes[nodeInfo];
12396         }
12397         return nodeInfo;
12398     },
12399
12400     /**
12401      * Gets a range template nodes.
12402      * @param {Number} startIndex
12403      * @param {Number} endIndex
12404      * @return {Array} An array of nodes
12405      */
12406     getNodes : function(start, end){
12407         var ns = this.nodes;
12408         start = start || 0;
12409         end = typeof end == "undefined" ? ns.length - 1 : end;
12410         var nodes = [];
12411         if(start <= end){
12412             for(var i = start; i <= end; i++){
12413                 nodes.push(ns[i]);
12414             }
12415         } else{
12416             for(var i = start; i >= end; i--){
12417                 nodes.push(ns[i]);
12418             }
12419         }
12420         return nodes;
12421     },
12422
12423     /**
12424      * Finds the index of the passed node
12425      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12426      * @return {Number} The index of the node or -1
12427      */
12428     indexOf : function(node){
12429         node = this.getNode(node);
12430         if(typeof node.nodeIndex == "number"){
12431             return node.nodeIndex;
12432         }
12433         var ns = this.nodes;
12434         for(var i = 0, len = ns.length; i < len; i++){
12435             if(ns[i] == node){
12436                 return i;
12437             }
12438         }
12439         return -1;
12440     }
12441 });
12442 /*
12443  * - LGPL
12444  *
12445  * based on jquery fullcalendar
12446  * 
12447  */
12448
12449 Roo.bootstrap = Roo.bootstrap || {};
12450 /**
12451  * @class Roo.bootstrap.Calendar
12452  * @extends Roo.bootstrap.Component
12453  * Bootstrap Calendar class
12454  * @cfg {Boolean} loadMask (true|false) default false
12455  * @cfg {Object} header generate the user specific header of the calendar, default false
12456
12457  * @constructor
12458  * Create a new Container
12459  * @param {Object} config The config object
12460  */
12461
12462
12463
12464 Roo.bootstrap.Calendar = function(config){
12465     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12466      this.addEvents({
12467         /**
12468              * @event select
12469              * Fires when a date is selected
12470              * @param {DatePicker} this
12471              * @param {Date} date The selected date
12472              */
12473         'select': true,
12474         /**
12475              * @event monthchange
12476              * Fires when the displayed month changes 
12477              * @param {DatePicker} this
12478              * @param {Date} date The selected month
12479              */
12480         'monthchange': true,
12481         /**
12482              * @event evententer
12483              * Fires when mouse over an event
12484              * @param {Calendar} this
12485              * @param {event} Event
12486              */
12487         'evententer': true,
12488         /**
12489              * @event eventleave
12490              * Fires when the mouse leaves an
12491              * @param {Calendar} this
12492              * @param {event}
12493              */
12494         'eventleave': true,
12495         /**
12496              * @event eventclick
12497              * Fires when the mouse click an
12498              * @param {Calendar} this
12499              * @param {event}
12500              */
12501         'eventclick': true
12502         
12503     });
12504
12505 };
12506
12507 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12508     
12509      /**
12510      * @cfg {Number} startDay
12511      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12512      */
12513     startDay : 0,
12514     
12515     loadMask : false,
12516     
12517     header : false,
12518       
12519     getAutoCreate : function(){
12520         
12521         
12522         var fc_button = function(name, corner, style, content ) {
12523             return Roo.apply({},{
12524                 tag : 'span',
12525                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12526                          (corner.length ?
12527                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12528                             ''
12529                         ),
12530                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12531                 unselectable: 'on'
12532             });
12533         };
12534         
12535         var header = {};
12536         
12537         if(!this.header){
12538             header = {
12539                 tag : 'table',
12540                 cls : 'fc-header',
12541                 style : 'width:100%',
12542                 cn : [
12543                     {
12544                         tag: 'tr',
12545                         cn : [
12546                             {
12547                                 tag : 'td',
12548                                 cls : 'fc-header-left',
12549                                 cn : [
12550                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12551                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12552                                     { tag: 'span', cls: 'fc-header-space' },
12553                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12554
12555
12556                                 ]
12557                             },
12558
12559                             {
12560                                 tag : 'td',
12561                                 cls : 'fc-header-center',
12562                                 cn : [
12563                                     {
12564                                         tag: 'span',
12565                                         cls: 'fc-header-title',
12566                                         cn : {
12567                                             tag: 'H2',
12568                                             html : 'month / year'
12569                                         }
12570                                     }
12571
12572                                 ]
12573                             },
12574                             {
12575                                 tag : 'td',
12576                                 cls : 'fc-header-right',
12577                                 cn : [
12578                               /*      fc_button('month', 'left', '', 'month' ),
12579                                     fc_button('week', '', '', 'week' ),
12580                                     fc_button('day', 'right', '', 'day' )
12581                                 */    
12582
12583                                 ]
12584                             }
12585
12586                         ]
12587                     }
12588                 ]
12589             };
12590         }
12591         
12592         header = this.header;
12593         
12594        
12595         var cal_heads = function() {
12596             var ret = [];
12597             // fixme - handle this.
12598             
12599             for (var i =0; i < Date.dayNames.length; i++) {
12600                 var d = Date.dayNames[i];
12601                 ret.push({
12602                     tag: 'th',
12603                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12604                     html : d.substring(0,3)
12605                 });
12606                 
12607             }
12608             ret[0].cls += ' fc-first';
12609             ret[6].cls += ' fc-last';
12610             return ret;
12611         };
12612         var cal_cell = function(n) {
12613             return  {
12614                 tag: 'td',
12615                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12616                 cn : [
12617                     {
12618                         cn : [
12619                             {
12620                                 cls: 'fc-day-number',
12621                                 html: 'D'
12622                             },
12623                             {
12624                                 cls: 'fc-day-content',
12625                              
12626                                 cn : [
12627                                      {
12628                                         style: 'position: relative;' // height: 17px;
12629                                     }
12630                                 ]
12631                             }
12632                             
12633                             
12634                         ]
12635                     }
12636                 ]
12637                 
12638             }
12639         };
12640         var cal_rows = function() {
12641             
12642             var ret = []
12643             for (var r = 0; r < 6; r++) {
12644                 var row= {
12645                     tag : 'tr',
12646                     cls : 'fc-week',
12647                     cn : []
12648                 };
12649                 
12650                 for (var i =0; i < Date.dayNames.length; i++) {
12651                     var d = Date.dayNames[i];
12652                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12653
12654                 }
12655                 row.cn[0].cls+=' fc-first';
12656                 row.cn[0].cn[0].style = 'min-height:90px';
12657                 row.cn[6].cls+=' fc-last';
12658                 ret.push(row);
12659                 
12660             }
12661             ret[0].cls += ' fc-first';
12662             ret[4].cls += ' fc-prev-last';
12663             ret[5].cls += ' fc-last';
12664             return ret;
12665             
12666         };
12667         
12668         var cal_table = {
12669             tag: 'table',
12670             cls: 'fc-border-separate',
12671             style : 'width:100%',
12672             cellspacing  : 0,
12673             cn : [
12674                 { 
12675                     tag: 'thead',
12676                     cn : [
12677                         { 
12678                             tag: 'tr',
12679                             cls : 'fc-first fc-last',
12680                             cn : cal_heads()
12681                         }
12682                     ]
12683                 },
12684                 { 
12685                     tag: 'tbody',
12686                     cn : cal_rows()
12687                 }
12688                   
12689             ]
12690         };
12691          
12692          var cfg = {
12693             cls : 'fc fc-ltr',
12694             cn : [
12695                 header,
12696                 {
12697                     cls : 'fc-content',
12698                     style : "position: relative;",
12699                     cn : [
12700                         {
12701                             cls : 'fc-view fc-view-month fc-grid',
12702                             style : 'position: relative',
12703                             unselectable : 'on',
12704                             cn : [
12705                                 {
12706                                     cls : 'fc-event-container',
12707                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12708                                 },
12709                                 cal_table
12710                             ]
12711                         }
12712                     ]
12713     
12714                 }
12715            ] 
12716             
12717         };
12718         
12719          
12720         
12721         return cfg;
12722     },
12723     
12724     
12725     initEvents : function()
12726     {
12727         if(!this.store){
12728             throw "can not find store for calendar";
12729         }
12730         
12731         var mark = {
12732             tag: "div",
12733             cls:"x-dlg-mask",
12734             style: "text-align:center",
12735             cn: [
12736                 {
12737                     tag: "div",
12738                     style: "background-color:white;width:50%;margin:250 auto",
12739                     cn: [
12740                         {
12741                             tag: "img",
12742                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12743                         },
12744                         {
12745                             tag: "span",
12746                             html: "Loading"
12747                         }
12748                         
12749                     ]
12750                 }
12751             ]
12752         }
12753         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12754         
12755         var size = this.el.select('.fc-content', true).first().getSize();
12756         this.maskEl.setSize(size.width, size.height);
12757         this.maskEl.enableDisplayMode("block");
12758         if(!this.loadMask){
12759             this.maskEl.hide();
12760         }
12761         
12762         this.store = Roo.factory(this.store, Roo.data);
12763         this.store.on('load', this.onLoad, this);
12764         this.store.on('beforeload', this.onBeforeLoad, this);
12765         
12766         this.resize();
12767         
12768         this.cells = this.el.select('.fc-day',true);
12769         //Roo.log(this.cells);
12770         this.textNodes = this.el.query('.fc-day-number');
12771         this.cells.addClassOnOver('fc-state-hover');
12772         
12773         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12774         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12775         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12776         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12777         
12778         this.on('monthchange', this.onMonthChange, this);
12779         
12780         this.update(new Date().clearTime());
12781     },
12782     
12783     resize : function() {
12784         var sz  = this.el.getSize();
12785         
12786         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12787         this.el.select('.fc-day-content div',true).setHeight(34);
12788     },
12789     
12790     
12791     // private
12792     showPrevMonth : function(e){
12793         this.update(this.activeDate.add("mo", -1));
12794     },
12795     showToday : function(e){
12796         this.update(new Date().clearTime());
12797     },
12798     // private
12799     showNextMonth : function(e){
12800         this.update(this.activeDate.add("mo", 1));
12801     },
12802
12803     // private
12804     showPrevYear : function(){
12805         this.update(this.activeDate.add("y", -1));
12806     },
12807
12808     // private
12809     showNextYear : function(){
12810         this.update(this.activeDate.add("y", 1));
12811     },
12812
12813     
12814    // private
12815     update : function(date)
12816     {
12817         var vd = this.activeDate;
12818         this.activeDate = date;
12819 //        if(vd && this.el){
12820 //            var t = date.getTime();
12821 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12822 //                Roo.log('using add remove');
12823 //                
12824 //                this.fireEvent('monthchange', this, date);
12825 //                
12826 //                this.cells.removeClass("fc-state-highlight");
12827 //                this.cells.each(function(c){
12828 //                   if(c.dateValue == t){
12829 //                       c.addClass("fc-state-highlight");
12830 //                       setTimeout(function(){
12831 //                            try{c.dom.firstChild.focus();}catch(e){}
12832 //                       }, 50);
12833 //                       return false;
12834 //                   }
12835 //                   return true;
12836 //                });
12837 //                return;
12838 //            }
12839 //        }
12840         
12841         var days = date.getDaysInMonth();
12842         
12843         var firstOfMonth = date.getFirstDateOfMonth();
12844         var startingPos = firstOfMonth.getDay()-this.startDay;
12845         
12846         if(startingPos < this.startDay){
12847             startingPos += 7;
12848         }
12849         
12850         var pm = date.add(Date.MONTH, -1);
12851         var prevStart = pm.getDaysInMonth()-startingPos;
12852 //        
12853         this.cells = this.el.select('.fc-day',true);
12854         this.textNodes = this.el.query('.fc-day-number');
12855         this.cells.addClassOnOver('fc-state-hover');
12856         
12857         var cells = this.cells.elements;
12858         var textEls = this.textNodes;
12859         
12860         Roo.each(cells, function(cell){
12861             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12862         });
12863         
12864         days += startingPos;
12865
12866         // convert everything to numbers so it's fast
12867         var day = 86400000;
12868         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12869         //Roo.log(d);
12870         //Roo.log(pm);
12871         //Roo.log(prevStart);
12872         
12873         var today = new Date().clearTime().getTime();
12874         var sel = date.clearTime().getTime();
12875         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12876         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12877         var ddMatch = this.disabledDatesRE;
12878         var ddText = this.disabledDatesText;
12879         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12880         var ddaysText = this.disabledDaysText;
12881         var format = this.format;
12882         
12883         var setCellClass = function(cal, cell){
12884             cell.row = 0;
12885             cell.events = [];
12886             cell.more = [];
12887             //Roo.log('set Cell Class');
12888             cell.title = "";
12889             var t = d.getTime();
12890             
12891             //Roo.log(d);
12892             
12893             cell.dateValue = t;
12894             if(t == today){
12895                 cell.className += " fc-today";
12896                 cell.className += " fc-state-highlight";
12897                 cell.title = cal.todayText;
12898             }
12899             if(t == sel){
12900                 // disable highlight in other month..
12901                 //cell.className += " fc-state-highlight";
12902                 
12903             }
12904             // disabling
12905             if(t < min) {
12906                 cell.className = " fc-state-disabled";
12907                 cell.title = cal.minText;
12908                 return;
12909             }
12910             if(t > max) {
12911                 cell.className = " fc-state-disabled";
12912                 cell.title = cal.maxText;
12913                 return;
12914             }
12915             if(ddays){
12916                 if(ddays.indexOf(d.getDay()) != -1){
12917                     cell.title = ddaysText;
12918                     cell.className = " fc-state-disabled";
12919                 }
12920             }
12921             if(ddMatch && format){
12922                 var fvalue = d.dateFormat(format);
12923                 if(ddMatch.test(fvalue)){
12924                     cell.title = ddText.replace("%0", fvalue);
12925                     cell.className = " fc-state-disabled";
12926                 }
12927             }
12928             
12929             if (!cell.initialClassName) {
12930                 cell.initialClassName = cell.dom.className;
12931             }
12932             
12933             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
12934         };
12935
12936         var i = 0;
12937         
12938         for(; i < startingPos; i++) {
12939             textEls[i].innerHTML = (++prevStart);
12940             d.setDate(d.getDate()+1);
12941             
12942             cells[i].className = "fc-past fc-other-month";
12943             setCellClass(this, cells[i]);
12944         }
12945         
12946         var intDay = 0;
12947         
12948         for(; i < days; i++){
12949             intDay = i - startingPos + 1;
12950             textEls[i].innerHTML = (intDay);
12951             d.setDate(d.getDate()+1);
12952             
12953             cells[i].className = ''; // "x-date-active";
12954             setCellClass(this, cells[i]);
12955         }
12956         var extraDays = 0;
12957         
12958         for(; i < 42; i++) {
12959             textEls[i].innerHTML = (++extraDays);
12960             d.setDate(d.getDate()+1);
12961             
12962             cells[i].className = "fc-future fc-other-month";
12963             setCellClass(this, cells[i]);
12964         }
12965         
12966         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
12967         
12968         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
12969         
12970         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
12971         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
12972         
12973         if(totalRows != 6){
12974             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
12975             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
12976         }
12977         
12978         this.fireEvent('monthchange', this, date);
12979         
12980         
12981         /*
12982         if(!this.internalRender){
12983             var main = this.el.dom.firstChild;
12984             var w = main.offsetWidth;
12985             this.el.setWidth(w + this.el.getBorderWidth("lr"));
12986             Roo.fly(main).setWidth(w);
12987             this.internalRender = true;
12988             // opera does not respect the auto grow header center column
12989             // then, after it gets a width opera refuses to recalculate
12990             // without a second pass
12991             if(Roo.isOpera && !this.secondPass){
12992                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
12993                 this.secondPass = true;
12994                 this.update.defer(10, this, [date]);
12995             }
12996         }
12997         */
12998         
12999     },
13000     
13001     findCell : function(dt) {
13002         dt = dt.clearTime().getTime();
13003         var ret = false;
13004         this.cells.each(function(c){
13005             //Roo.log("check " +c.dateValue + '?=' + dt);
13006             if(c.dateValue == dt){
13007                 ret = c;
13008                 return false;
13009             }
13010             return true;
13011         });
13012         
13013         return ret;
13014     },
13015     
13016     findCells : function(ev) {
13017         var s = ev.start.clone().clearTime().getTime();
13018        // Roo.log(s);
13019         var e= ev.end.clone().clearTime().getTime();
13020        // Roo.log(e);
13021         var ret = [];
13022         this.cells.each(function(c){
13023              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13024             
13025             if(c.dateValue > e){
13026                 return ;
13027             }
13028             if(c.dateValue < s){
13029                 return ;
13030             }
13031             ret.push(c);
13032         });
13033         
13034         return ret;    
13035     },
13036     
13037 //    findBestRow: function(cells)
13038 //    {
13039 //        var ret = 0;
13040 //        
13041 //        for (var i =0 ; i < cells.length;i++) {
13042 //            ret  = Math.max(cells[i].rows || 0,ret);
13043 //        }
13044 //        return ret;
13045 //        
13046 //    },
13047     
13048     
13049     addItem : function(ev)
13050     {
13051         // look for vertical location slot in
13052         var cells = this.findCells(ev);
13053         
13054 //        ev.row = this.findBestRow(cells);
13055         
13056         // work out the location.
13057         
13058         var crow = false;
13059         var rows = [];
13060         for(var i =0; i < cells.length; i++) {
13061             
13062             cells[i].row = cells[0].row;
13063             
13064             if(i == 0){
13065                 cells[i].row = cells[i].row + 1;
13066             }
13067             
13068             if (!crow) {
13069                 crow = {
13070                     start : cells[i],
13071                     end :  cells[i]
13072                 };
13073                 continue;
13074             }
13075             if (crow.start.getY() == cells[i].getY()) {
13076                 // on same row.
13077                 crow.end = cells[i];
13078                 continue;
13079             }
13080             // different row.
13081             rows.push(crow);
13082             crow = {
13083                 start: cells[i],
13084                 end : cells[i]
13085             };
13086             
13087         }
13088         
13089         rows.push(crow);
13090         ev.els = [];
13091         ev.rows = rows;
13092         ev.cells = cells;
13093         
13094         cells[0].events.push(ev);
13095         
13096         this.calevents.push(ev);
13097     },
13098     
13099     clearEvents: function() {
13100         
13101         if(!this.calevents){
13102             return;
13103         }
13104         
13105         Roo.each(this.cells.elements, function(c){
13106             c.row = 0;
13107             c.events = [];
13108             c.more = [];
13109         });
13110         
13111         Roo.each(this.calevents, function(e) {
13112             Roo.each(e.els, function(el) {
13113                 el.un('mouseenter' ,this.onEventEnter, this);
13114                 el.un('mouseleave' ,this.onEventLeave, this);
13115                 el.remove();
13116             },this);
13117         },this);
13118         
13119         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13120             e.remove();
13121         });
13122         
13123     },
13124     
13125     renderEvents: function()
13126     {   
13127         var _this = this;
13128         
13129         this.cells.each(function(c) {
13130             
13131             if(c.row < 5){
13132                 return;
13133             }
13134             
13135             var ev = c.events;
13136             
13137             var r = 4;
13138             if(c.row != c.events.length){
13139                 r = 4 - (4 - (c.row - c.events.length));
13140             }
13141             
13142             c.events = ev.slice(0, r);
13143             c.more = ev.slice(r);
13144             
13145             if(c.more.length && c.more.length == 1){
13146                 c.events.push(c.more.pop());
13147             }
13148             
13149             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13150             
13151         });
13152             
13153         this.cells.each(function(c) {
13154             
13155             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13156             
13157             
13158             for (var e = 0; e < c.events.length; e++){
13159                 var ev = c.events[e];
13160                 var rows = ev.rows;
13161                 
13162                 for(var i = 0; i < rows.length; i++) {
13163                 
13164                     // how many rows should it span..
13165
13166                     var  cfg = {
13167                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13168                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13169
13170                         unselectable : "on",
13171                         cn : [
13172                             {
13173                                 cls: 'fc-event-inner',
13174                                 cn : [
13175     //                                {
13176     //                                  tag:'span',
13177     //                                  cls: 'fc-event-time',
13178     //                                  html : cells.length > 1 ? '' : ev.time
13179     //                                },
13180                                     {
13181                                       tag:'span',
13182                                       cls: 'fc-event-title',
13183                                       html : String.format('{0}', ev.title)
13184                                     }
13185
13186
13187                                 ]
13188                             },
13189                             {
13190                                 cls: 'ui-resizable-handle ui-resizable-e',
13191                                 html : '&nbsp;&nbsp;&nbsp'
13192                             }
13193
13194                         ]
13195                     };
13196
13197                     if (i == 0) {
13198                         cfg.cls += ' fc-event-start';
13199                     }
13200                     if ((i+1) == rows.length) {
13201                         cfg.cls += ' fc-event-end';
13202                     }
13203
13204                     var ctr = _this.el.select('.fc-event-container',true).first();
13205                     var cg = ctr.createChild(cfg);
13206
13207                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13208                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13209
13210                     var r = (c.more.length) ? 1 : 0;
13211                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13212                     cg.setWidth(ebox.right - sbox.x -2);
13213
13214                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13215                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13216                     cg.on('click', _this.onEventClick, _this, ev);
13217
13218                     ev.els.push(cg);
13219                     
13220                 }
13221                 
13222             }
13223             
13224             
13225             if(c.more.length){
13226                 var  cfg = {
13227                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13228                     style : 'position: absolute',
13229                     unselectable : "on",
13230                     cn : [
13231                         {
13232                             cls: 'fc-event-inner',
13233                             cn : [
13234                                 {
13235                                   tag:'span',
13236                                   cls: 'fc-event-title',
13237                                   html : 'More'
13238                                 }
13239
13240
13241                             ]
13242                         },
13243                         {
13244                             cls: 'ui-resizable-handle ui-resizable-e',
13245                             html : '&nbsp;&nbsp;&nbsp'
13246                         }
13247
13248                     ]
13249                 };
13250
13251                 var ctr = _this.el.select('.fc-event-container',true).first();
13252                 var cg = ctr.createChild(cfg);
13253
13254                 var sbox = c.select('.fc-day-content',true).first().getBox();
13255                 var ebox = c.select('.fc-day-content',true).first().getBox();
13256                 //Roo.log(cg);
13257                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13258                 cg.setWidth(ebox.right - sbox.x -2);
13259
13260                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13261                 
13262             }
13263             
13264         });
13265         
13266         
13267         
13268     },
13269     
13270     onEventEnter: function (e, el,event,d) {
13271         this.fireEvent('evententer', this, el, event);
13272     },
13273     
13274     onEventLeave: function (e, el,event,d) {
13275         this.fireEvent('eventleave', this, el, event);
13276     },
13277     
13278     onEventClick: function (e, el,event,d) {
13279         this.fireEvent('eventclick', this, el, event);
13280     },
13281     
13282     onMonthChange: function () {
13283         this.store.load();
13284     },
13285     
13286     onMoreEventClick: function(e, el, more)
13287     {
13288         var _this = this;
13289         
13290         this.calpopover.placement = 'right';
13291         this.calpopover.setTitle('More');
13292         
13293         this.calpopover.setContent('');
13294         
13295         var ctr = this.calpopover.el.select('.popover-content', true).first();
13296         
13297         Roo.each(more, function(m){
13298             var cfg = {
13299                 cls : 'fc-event-hori fc-event-draggable',
13300                 html : m.title
13301             }
13302             var cg = ctr.createChild(cfg);
13303             
13304             cg.on('click', _this.onEventClick, _this, m);
13305         });
13306         
13307         this.calpopover.show(el);
13308         
13309         
13310     },
13311     
13312     onLoad: function () 
13313     {   
13314         this.calevents = [];
13315         var cal = this;
13316         
13317         if(this.store.getCount() > 0){
13318             this.store.data.each(function(d){
13319                cal.addItem({
13320                     id : d.data.id,
13321                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13322                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13323                     time : d.data.start_time,
13324                     title : d.data.title,
13325                     description : d.data.description,
13326                     venue : d.data.venue
13327                 });
13328             });
13329         }
13330         
13331         this.renderEvents();
13332         
13333         if(this.calevents.length && this.loadMask){
13334             this.maskEl.hide();
13335         }
13336     },
13337     
13338     onBeforeLoad: function()
13339     {
13340         this.clearEvents();
13341         if(this.loadMask){
13342             this.maskEl.show();
13343         }
13344     }
13345 });
13346
13347  
13348  /*
13349  * - LGPL
13350  *
13351  * element
13352  * 
13353  */
13354
13355 /**
13356  * @class Roo.bootstrap.Popover
13357  * @extends Roo.bootstrap.Component
13358  * Bootstrap Popover class
13359  * @cfg {String} html contents of the popover   (or false to use children..)
13360  * @cfg {String} title of popover (or false to hide)
13361  * @cfg {String} placement how it is placed
13362  * @cfg {String} trigger click || hover (or false to trigger manually)
13363  * @cfg {String} over what (parent or false to trigger manually.)
13364  * 
13365  * @constructor
13366  * Create a new Popover
13367  * @param {Object} config The config object
13368  */
13369
13370 Roo.bootstrap.Popover = function(config){
13371     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13372 };
13373
13374 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13375     
13376     title: 'Fill in a title',
13377     html: false,
13378     
13379     placement : 'right',
13380     trigger : 'hover', // hover
13381     
13382     over: 'parent',
13383     
13384     can_build_overlaid : false,
13385     
13386     getChildContainer : function()
13387     {
13388         return this.el.select('.popover-content',true).first();
13389     },
13390     
13391     getAutoCreate : function(){
13392          Roo.log('make popover?');
13393         var cfg = {
13394            cls : 'popover roo-dynamic',
13395            style: 'display:block',
13396            cn : [
13397                 {
13398                     cls : 'arrow'
13399                 },
13400                 {
13401                     cls : 'popover-inner',
13402                     cn : [
13403                         {
13404                             tag: 'h3',
13405                             cls: 'popover-title',
13406                             html : this.title
13407                         },
13408                         {
13409                             cls : 'popover-content',
13410                             html : this.html
13411                         }
13412                     ]
13413                     
13414                 }
13415            ]
13416         };
13417         
13418         return cfg;
13419     },
13420     setTitle: function(str)
13421     {
13422         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13423     },
13424     setContent: function(str)
13425     {
13426         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13427     },
13428     // as it get's added to the bottom of the page.
13429     onRender : function(ct, position)
13430     {
13431         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13432         if(!this.el){
13433             var cfg = Roo.apply({},  this.getAutoCreate());
13434             cfg.id = Roo.id();
13435             
13436             if (this.cls) {
13437                 cfg.cls += ' ' + this.cls;
13438             }
13439             if (this.style) {
13440                 cfg.style = this.style;
13441             }
13442             Roo.log("adding to ")
13443             this.el = Roo.get(document.body).createChild(cfg, position);
13444             Roo.log(this.el);
13445         }
13446         this.initEvents();
13447     },
13448     
13449     initEvents : function()
13450     {
13451         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13452         this.el.enableDisplayMode('block');
13453         this.el.hide();
13454         if (this.over === false) {
13455             return; 
13456         }
13457         if (this.triggers === false) {
13458             return;
13459         }
13460         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13461         var triggers = this.trigger ? this.trigger.split(' ') : [];
13462         Roo.each(triggers, function(trigger) {
13463         
13464             if (trigger == 'click') {
13465                 on_el.on('click', this.toggle, this);
13466             } else if (trigger != 'manual') {
13467                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13468                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13469       
13470                 on_el.on(eventIn  ,this.enter, this);
13471                 on_el.on(eventOut, this.leave, this);
13472             }
13473         }, this);
13474         
13475     },
13476     
13477     
13478     // private
13479     timeout : null,
13480     hoverState : null,
13481     
13482     toggle : function () {
13483         this.hoverState == 'in' ? this.leave() : this.enter();
13484     },
13485     
13486     enter : function () {
13487        
13488     
13489         clearTimeout(this.timeout);
13490     
13491         this.hoverState = 'in'
13492     
13493         if (!this.delay || !this.delay.show) {
13494             this.show();
13495             return 
13496         }
13497         var _t = this;
13498         this.timeout = setTimeout(function () {
13499             if (_t.hoverState == 'in') {
13500                 _t.show();
13501             }
13502         }, this.delay.show)
13503     },
13504     leave : function() {
13505         clearTimeout(this.timeout);
13506     
13507         this.hoverState = 'out'
13508     
13509         if (!this.delay || !this.delay.hide) {
13510             this.hide();
13511             return 
13512         }
13513         var _t = this;
13514         this.timeout = setTimeout(function () {
13515             if (_t.hoverState == 'out') {
13516                 _t.hide();
13517             }
13518         }, this.delay.hide)
13519     },
13520     
13521     show : function (on_el)
13522     {
13523         if (!on_el) {
13524             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13525         }
13526         // set content.
13527         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13528         if (this.html !== false) {
13529             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13530         }
13531         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13532         if (!this.title.length) {
13533             this.el.select('.popover-title',true).hide();
13534         }
13535         
13536         var placement = typeof this.placement == 'function' ?
13537             this.placement.call(this, this.el, on_el) :
13538             this.placement;
13539             
13540         var autoToken = /\s?auto?\s?/i;
13541         var autoPlace = autoToken.test(placement);
13542         if (autoPlace) {
13543             placement = placement.replace(autoToken, '') || 'top';
13544         }
13545         
13546         //this.el.detach()
13547         //this.el.setXY([0,0]);
13548         this.el.show();
13549         this.el.dom.style.display='block';
13550         this.el.addClass(placement);
13551         
13552         //this.el.appendTo(on_el);
13553         
13554         var p = this.getPosition();
13555         var box = this.el.getBox();
13556         
13557         if (autoPlace) {
13558             // fixme..
13559         }
13560         var align = Roo.bootstrap.Popover.alignment[placement]
13561         this.el.alignTo(on_el, align[0],align[1]);
13562         //var arrow = this.el.select('.arrow',true).first();
13563         //arrow.set(align[2], 
13564         
13565         this.el.addClass('in');
13566         this.hoverState = null;
13567         
13568         if (this.el.hasClass('fade')) {
13569             // fade it?
13570         }
13571         
13572     },
13573     hide : function()
13574     {
13575         this.el.setXY([0,0]);
13576         this.el.removeClass('in');
13577         this.el.hide();
13578         
13579     }
13580     
13581 });
13582
13583 Roo.bootstrap.Popover.alignment = {
13584     'left' : ['r-l', [-10,0], 'right'],
13585     'right' : ['l-r', [10,0], 'left'],
13586     'bottom' : ['t-b', [0,10], 'top'],
13587     'top' : [ 'b-t', [0,-10], 'bottom']
13588 };
13589
13590  /*
13591  * - LGPL
13592  *
13593  * Progress
13594  * 
13595  */
13596
13597 /**
13598  * @class Roo.bootstrap.Progress
13599  * @extends Roo.bootstrap.Component
13600  * Bootstrap Progress class
13601  * @cfg {Boolean} striped striped of the progress bar
13602  * @cfg {Boolean} active animated of the progress bar
13603  * 
13604  * 
13605  * @constructor
13606  * Create a new Progress
13607  * @param {Object} config The config object
13608  */
13609
13610 Roo.bootstrap.Progress = function(config){
13611     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13612 };
13613
13614 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13615     
13616     striped : false,
13617     active: false,
13618     
13619     getAutoCreate : function(){
13620         var cfg = {
13621             tag: 'div',
13622             cls: 'progress'
13623         };
13624         
13625         
13626         if(this.striped){
13627             cfg.cls += ' progress-striped';
13628         }
13629       
13630         if(this.active){
13631             cfg.cls += ' active';
13632         }
13633         
13634         
13635         return cfg;
13636     }
13637    
13638 });
13639
13640  
13641
13642  /*
13643  * - LGPL
13644  *
13645  * ProgressBar
13646  * 
13647  */
13648
13649 /**
13650  * @class Roo.bootstrap.ProgressBar
13651  * @extends Roo.bootstrap.Component
13652  * Bootstrap ProgressBar class
13653  * @cfg {Number} aria_valuenow aria-value now
13654  * @cfg {Number} aria_valuemin aria-value min
13655  * @cfg {Number} aria_valuemax aria-value max
13656  * @cfg {String} label label for the progress bar
13657  * @cfg {String} panel (success | info | warning | danger )
13658  * @cfg {String} role role of the progress bar
13659  * @cfg {String} sr_only text
13660  * 
13661  * 
13662  * @constructor
13663  * Create a new ProgressBar
13664  * @param {Object} config The config object
13665  */
13666
13667 Roo.bootstrap.ProgressBar = function(config){
13668     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13669 };
13670
13671 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13672     
13673     aria_valuenow : 0,
13674     aria_valuemin : 0,
13675     aria_valuemax : 100,
13676     label : false,
13677     panel : false,
13678     role : false,
13679     sr_only: false,
13680     
13681     getAutoCreate : function()
13682     {
13683         
13684         var cfg = {
13685             tag: 'div',
13686             cls: 'progress-bar',
13687             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13688         };
13689         
13690         if(this.sr_only){
13691             cfg.cn = {
13692                 tag: 'span',
13693                 cls: 'sr-only',
13694                 html: this.sr_only
13695             }
13696         }
13697         
13698         if(this.role){
13699             cfg.role = this.role;
13700         }
13701         
13702         if(this.aria_valuenow){
13703             cfg['aria-valuenow'] = this.aria_valuenow;
13704         }
13705         
13706         if(this.aria_valuemin){
13707             cfg['aria-valuemin'] = this.aria_valuemin;
13708         }
13709         
13710         if(this.aria_valuemax){
13711             cfg['aria-valuemax'] = this.aria_valuemax;
13712         }
13713         
13714         if(this.label && !this.sr_only){
13715             cfg.html = this.label;
13716         }
13717         
13718         if(this.panel){
13719             cfg.cls += ' progress-bar-' + this.panel;
13720         }
13721         
13722         return cfg;
13723     },
13724     
13725     update : function(aria_valuenow)
13726     {
13727         this.aria_valuenow = aria_valuenow;
13728         
13729         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13730     }
13731    
13732 });
13733
13734  
13735
13736  /*
13737  * - LGPL
13738  *
13739  * column
13740  * 
13741  */
13742
13743 /**
13744  * @class Roo.bootstrap.TabGroup
13745  * @extends Roo.bootstrap.Column
13746  * Bootstrap Column class
13747  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13748  * @cfg {Boolean} carousel true to make the group behave like a carousel
13749  * 
13750  * @constructor
13751  * Create a new TabGroup
13752  * @param {Object} config The config object
13753  */
13754
13755 Roo.bootstrap.TabGroup = function(config){
13756     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13757     if (!this.navId) {
13758         this.navId = Roo.id();
13759     }
13760     this.tabs = [];
13761     Roo.bootstrap.TabGroup.register(this);
13762     
13763 };
13764
13765 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
13766     
13767     carousel : false,
13768      
13769     getAutoCreate : function()
13770     {
13771         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13772         
13773         cfg.cls += ' tab-content';
13774         
13775         if (this.carousel) {
13776             cfg.cls += ' carousel slide';
13777             cfg.cn = [{
13778                cls : 'carousel-inner'
13779             }]
13780         }
13781         
13782         
13783         return cfg;
13784     },
13785     getChildContainer : function()
13786     {
13787         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13788     },
13789     
13790     /**
13791     * register a Navigation item
13792     * @param {Roo.bootstrap.NavItem} the navitem to add
13793     */
13794     register : function(item)
13795     {
13796         this.tabs.push( item);
13797         item.navId = this.navId; // not really needed..
13798     
13799     },
13800     
13801     getActivePanel : function()
13802     {
13803         var r = false;
13804         Roo.each(this.tabs, function(t) {
13805             if (t.active) {
13806                 r = t;
13807                 return false;
13808             }
13809             return null;
13810         });
13811         return r;
13812         
13813     },
13814     getPanelByName : function(n)
13815     {
13816         var r = false;
13817         Roo.each(this.tabs, function(t) {
13818             if (t.tabId == n) {
13819                 r = t;
13820                 return false;
13821             }
13822             return null;
13823         });
13824         return r;
13825     },
13826     indexOfPanel : function(p)
13827     {
13828         var r = false;
13829         Roo.each(this.tabs, function(t,i) {
13830             if (t.tabId == p.tabId) {
13831                 r = i;
13832                 return false;
13833             }
13834             return null;
13835         });
13836         return r;
13837     },
13838     showPanel : function (pan)
13839     {
13840         if (typeof(pan) == 'number') {
13841             pan = this.tabs[pan];
13842         }
13843         if (typeof(pan) == 'string') {
13844             pan = this.getPanelByName(pan);
13845         }
13846         if (pan.tabId == this.getActivePanel().tabId) {
13847             return;
13848         }
13849         var cur = this.getActivePanel();
13850         if (this.carousel) {
13851             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
13852             var lr = dir == 'next' ? 'left' : 'right';
13853             pan.el.addClass(dir); // or prev
13854             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
13855             cur.el.addClass(lr); // or right
13856             pan.el.addClass(lr);
13857             cur.el.on('transitionend', function() {
13858                 Roo.log("trans end?");
13859                 
13860                 pan.el.removeClass([lr,dir]);
13861                 pan.setActive(true);
13862                 
13863                 cur.el.removeClass([lr]);
13864                 cur.setActive(false);
13865                 
13866                 
13867             }, this, { single:  true } );
13868             return;
13869         }
13870         
13871         cur.setActive(false);
13872         pan.setActive(true);
13873         
13874     },
13875     showPanelNext : function()
13876     {
13877         var i = this.indexOfPanel(this.getActivePanel());
13878         if (i > this.tabs.length) {
13879             return;
13880         }
13881         this.showPanel(this.tabs[i+1]);
13882     },
13883     showPanelPrev : function()
13884     {
13885         var i = this.indexOfPanel(this.getActivePanel());
13886         if (i  < 1) {
13887             return;
13888         }
13889         this.showPanel(this.tabs[i-1]);
13890     }
13891     
13892     
13893   
13894 });
13895
13896  
13897
13898  
13899  
13900 Roo.apply(Roo.bootstrap.TabGroup, {
13901     
13902     groups: {},
13903      /**
13904     * register a Navigation Group
13905     * @param {Roo.bootstrap.NavGroup} the navgroup to add
13906     */
13907     register : function(navgrp)
13908     {
13909         this.groups[navgrp.navId] = navgrp;
13910         
13911     },
13912     /**
13913     * fetch a Navigation Group based on the navigation ID
13914     * if one does not exist , it will get created.
13915     * @param {string} the navgroup to add
13916     * @returns {Roo.bootstrap.NavGroup} the navgroup 
13917     */
13918     get: function(navId) {
13919         if (typeof(this.groups[navId]) == 'undefined') {
13920             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
13921         }
13922         return this.groups[navId] ;
13923     }
13924     
13925     
13926     
13927 });
13928
13929  /*
13930  * - LGPL
13931  *
13932  * TabPanel
13933  * 
13934  */
13935
13936 /**
13937  * @class Roo.bootstrap.TabPanel
13938  * @extends Roo.bootstrap.Component
13939  * Bootstrap TabPanel class
13940  * @cfg {Boolean} active panel active
13941  * @cfg {String} html panel content
13942  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
13943  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
13944  * 
13945  * 
13946  * @constructor
13947  * Create a new TabPanel
13948  * @param {Object} config The config object
13949  */
13950
13951 Roo.bootstrap.TabPanel = function(config){
13952     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
13953      this.addEvents({
13954         /**
13955              * @event changed
13956              * Fires when the active status changes
13957              * @param {Roo.bootstrap.TabPanel} this
13958              * @param {Boolean} state the new state
13959             
13960          */
13961         'changed': true
13962      });
13963     this.tabId = this.tabId || Roo.id();
13964   
13965 };
13966
13967 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
13968     
13969     active: false,
13970     html: false,
13971     tabId: false,
13972     navId : false,
13973     
13974     getAutoCreate : function(){
13975         var cfg = {
13976             tag: 'div',
13977             // item is needed for carousel - not sure if it has any effect otherwise
13978             cls: 'tab-pane item',
13979             html: this.html || ''
13980         };
13981         
13982         if(this.active){
13983             cfg.cls += ' active';
13984         }
13985         
13986         if(this.tabId){
13987             cfg.tabId = this.tabId;
13988         }
13989         
13990         
13991         return cfg;
13992     },
13993     
13994     initEvents:  function()
13995     {
13996         Roo.log('-------- init events on tab panel ---------');
13997         
13998         var p = this.parent();
13999         this.navId = this.navId || p.navId;
14000         
14001         if (typeof(this.navId) != 'undefined') {
14002             // not really needed.. but just in case.. parent should be a NavGroup.
14003             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14004             Roo.log(['register', tg, this]);
14005             tg.register(this);
14006         }
14007     },
14008     
14009     
14010     onRender : function(ct, position)
14011     {
14012        // Roo.log("Call onRender: " + this.xtype);
14013         
14014         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14015         
14016         
14017         
14018         
14019         
14020     },
14021     setActive: function(state)
14022     {
14023         Roo.log("panel - set active " + this.tabId + "=" + state);
14024         
14025         this.active = state;
14026         if (!state) {
14027             this.el.removeClass('active');
14028             
14029         } else  if (!this.el.hasClass('active')) {
14030             this.el.addClass('active');
14031         }
14032         this.fireEvent('changed', this, state);
14033     }
14034     
14035     
14036 });
14037  
14038
14039  
14040
14041  /*
14042  * - LGPL
14043  *
14044  * DateField
14045  * 
14046  */
14047
14048 /**
14049  * @class Roo.bootstrap.DateField
14050  * @extends Roo.bootstrap.Input
14051  * Bootstrap DateField class
14052  * @cfg {Number} weekStart default 0
14053  * @cfg {Number} weekStart default 0
14054  * @cfg {Number} viewMode default empty, (months|years)
14055  * @cfg {Number} minViewMode default empty, (months|years)
14056  * @cfg {Number} startDate default -Infinity
14057  * @cfg {Number} endDate default Infinity
14058  * @cfg {Boolean} todayHighlight default false
14059  * @cfg {Boolean} todayBtn default false
14060  * @cfg {Boolean} calendarWeeks default false
14061  * @cfg {Object} daysOfWeekDisabled default empty
14062  * 
14063  * @cfg {Boolean} keyboardNavigation default true
14064  * @cfg {String} language default en
14065  * 
14066  * @constructor
14067  * Create a new DateField
14068  * @param {Object} config The config object
14069  */
14070
14071 Roo.bootstrap.DateField = function(config){
14072     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14073      this.addEvents({
14074             /**
14075              * @event show
14076              * Fires when this field show.
14077              * @param {Roo.bootstrap.DateField} this
14078              * @param {Mixed} date The date value
14079              */
14080             show : true,
14081             /**
14082              * @event show
14083              * Fires when this field hide.
14084              * @param {Roo.bootstrap.DateField} this
14085              * @param {Mixed} date The date value
14086              */
14087             hide : true,
14088             /**
14089              * @event select
14090              * Fires when select a date.
14091              * @param {Roo.bootstrap.DateField} this
14092              * @param {Mixed} date The date value
14093              */
14094             select : true
14095         });
14096 };
14097
14098 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14099     
14100     /**
14101      * @cfg {String} format
14102      * The default date format string which can be overriden for localization support.  The format must be
14103      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14104      */
14105     format : "m/d/y",
14106     /**
14107      * @cfg {String} altFormats
14108      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14109      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14110      */
14111     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14112     
14113     weekStart : 0,
14114     
14115     viewMode : '',
14116     
14117     minViewMode : '',
14118     
14119     todayHighlight : false,
14120     
14121     todayBtn: false,
14122     
14123     language: 'en',
14124     
14125     keyboardNavigation: true,
14126     
14127     calendarWeeks: false,
14128     
14129     startDate: -Infinity,
14130     
14131     endDate: Infinity,
14132     
14133     daysOfWeekDisabled: [],
14134     
14135     _events: [],
14136     
14137     UTCDate: function()
14138     {
14139         return new Date(Date.UTC.apply(Date, arguments));
14140     },
14141     
14142     UTCToday: function()
14143     {
14144         var today = new Date();
14145         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14146     },
14147     
14148     getDate: function() {
14149             var d = this.getUTCDate();
14150             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14151     },
14152     
14153     getUTCDate: function() {
14154             return this.date;
14155     },
14156     
14157     setDate: function(d) {
14158             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14159     },
14160     
14161     setUTCDate: function(d) {
14162             this.date = d;
14163             this.setValue(this.formatDate(this.date));
14164     },
14165         
14166     onRender: function(ct, position)
14167     {
14168         
14169         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14170         
14171         this.language = this.language || 'en';
14172         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14173         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14174         
14175         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14176         this.format = this.format || 'm/d/y';
14177         this.isInline = false;
14178         this.isInput = true;
14179         this.component = this.el.select('.add-on', true).first() || false;
14180         this.component = (this.component && this.component.length === 0) ? false : this.component;
14181         this.hasInput = this.component && this.inputEL().length;
14182         
14183         if (typeof(this.minViewMode === 'string')) {
14184             switch (this.minViewMode) {
14185                 case 'months':
14186                     this.minViewMode = 1;
14187                     break;
14188                 case 'years':
14189                     this.minViewMode = 2;
14190                     break;
14191                 default:
14192                     this.minViewMode = 0;
14193                     break;
14194             }
14195         }
14196         
14197         if (typeof(this.viewMode === 'string')) {
14198             switch (this.viewMode) {
14199                 case 'months':
14200                     this.viewMode = 1;
14201                     break;
14202                 case 'years':
14203                     this.viewMode = 2;
14204                     break;
14205                 default:
14206                     this.viewMode = 0;
14207                     break;
14208             }
14209         }
14210                 
14211         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14212         
14213         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14214         
14215         this.picker().on('mousedown', this.onMousedown, this);
14216         this.picker().on('click', this.onClick, this);
14217         
14218         this.picker().addClass('datepicker-dropdown');
14219         
14220         this.startViewMode = this.viewMode;
14221         
14222         
14223         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14224             if(!this.calendarWeeks){
14225                 v.remove();
14226                 return;
14227             };
14228             
14229             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14230             v.attr('colspan', function(i, val){
14231                 return parseInt(val) + 1;
14232             });
14233         })
14234                         
14235         
14236         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14237         
14238         this.setStartDate(this.startDate);
14239         this.setEndDate(this.endDate);
14240         
14241         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14242         
14243         this.fillDow();
14244         this.fillMonths();
14245         this.update();
14246         this.showMode();
14247         
14248         if(this.isInline) {
14249             this.show();
14250         }
14251     },
14252     
14253     picker : function()
14254     {
14255         return this.el.select('.datepicker', true).first();
14256     },
14257     
14258     fillDow: function()
14259     {
14260         var dowCnt = this.weekStart;
14261         
14262         var dow = {
14263             tag: 'tr',
14264             cn: [
14265                 
14266             ]
14267         };
14268         
14269         if(this.calendarWeeks){
14270             dow.cn.push({
14271                 tag: 'th',
14272                 cls: 'cw',
14273                 html: '&nbsp;'
14274             })
14275         }
14276         
14277         while (dowCnt < this.weekStart + 7) {
14278             dow.cn.push({
14279                 tag: 'th',
14280                 cls: 'dow',
14281                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14282             });
14283         }
14284         
14285         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14286     },
14287     
14288     fillMonths: function()
14289     {    
14290         var i = 0
14291         var months = this.picker().select('>.datepicker-months td', true).first();
14292         
14293         months.dom.innerHTML = '';
14294         
14295         while (i < 12) {
14296             var month = {
14297                 tag: 'span',
14298                 cls: 'month',
14299                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14300             }
14301             
14302             months.createChild(month);
14303         }
14304         
14305     },
14306     
14307     update: function()
14308     {
14309         
14310         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14311         
14312         if (this.date < this.startDate) {
14313             this.viewDate = new Date(this.startDate);
14314         } else if (this.date > this.endDate) {
14315             this.viewDate = new Date(this.endDate);
14316         } else {
14317             this.viewDate = new Date(this.date);
14318         }
14319         
14320         this.fill();
14321     },
14322     
14323     fill: function() 
14324     {
14325         var d = new Date(this.viewDate),
14326                 year = d.getUTCFullYear(),
14327                 month = d.getUTCMonth(),
14328                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14329                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14330                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14331                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14332                 currentDate = this.date && this.date.valueOf(),
14333                 today = this.UTCToday();
14334         
14335         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14336         
14337 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14338         
14339 //        this.picker.select('>tfoot th.today').
14340 //                                              .text(dates[this.language].today)
14341 //                                              .toggle(this.todayBtn !== false);
14342     
14343         this.updateNavArrows();
14344         this.fillMonths();
14345                                                 
14346         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14347         
14348         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14349          
14350         prevMonth.setUTCDate(day);
14351         
14352         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14353         
14354         var nextMonth = new Date(prevMonth);
14355         
14356         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14357         
14358         nextMonth = nextMonth.valueOf();
14359         
14360         var fillMonths = false;
14361         
14362         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14363         
14364         while(prevMonth.valueOf() < nextMonth) {
14365             var clsName = '';
14366             
14367             if (prevMonth.getUTCDay() === this.weekStart) {
14368                 if(fillMonths){
14369                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14370                 }
14371                     
14372                 fillMonths = {
14373                     tag: 'tr',
14374                     cn: []
14375                 };
14376                 
14377                 if(this.calendarWeeks){
14378                     // ISO 8601: First week contains first thursday.
14379                     // ISO also states week starts on Monday, but we can be more abstract here.
14380                     var
14381                     // Start of current week: based on weekstart/current date
14382                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14383                     // Thursday of this week
14384                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14385                     // First Thursday of year, year from thursday
14386                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14387                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14388                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14389                     
14390                     fillMonths.cn.push({
14391                         tag: 'td',
14392                         cls: 'cw',
14393                         html: calWeek
14394                     });
14395                 }
14396             }
14397             
14398             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14399                 clsName += ' old';
14400             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14401                 clsName += ' new';
14402             }
14403             if (this.todayHighlight &&
14404                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14405                 prevMonth.getUTCMonth() == today.getMonth() &&
14406                 prevMonth.getUTCDate() == today.getDate()) {
14407                 clsName += ' today';
14408             }
14409             
14410             if (currentDate && prevMonth.valueOf() === currentDate) {
14411                 clsName += ' active';
14412             }
14413             
14414             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14415                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14416                     clsName += ' disabled';
14417             }
14418             
14419             fillMonths.cn.push({
14420                 tag: 'td',
14421                 cls: 'day ' + clsName,
14422                 html: prevMonth.getDate()
14423             })
14424             
14425             prevMonth.setDate(prevMonth.getDate()+1);
14426         }
14427           
14428         var currentYear = this.date && this.date.getUTCFullYear();
14429         var currentMonth = this.date && this.date.getUTCMonth();
14430         
14431         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14432         
14433         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14434             v.removeClass('active');
14435             
14436             if(currentYear === year && k === currentMonth){
14437                 v.addClass('active');
14438             }
14439             
14440             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14441                 v.addClass('disabled');
14442             }
14443             
14444         });
14445         
14446         
14447         year = parseInt(year/10, 10) * 10;
14448         
14449         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14450         
14451         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14452         
14453         year -= 1;
14454         for (var i = -1; i < 11; i++) {
14455             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14456                 tag: 'span',
14457                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14458                 html: year
14459             })
14460             
14461             year += 1;
14462         }
14463     },
14464     
14465     showMode: function(dir) 
14466     {
14467         if (dir) {
14468             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14469         }
14470         Roo.each(this.picker().select('>div',true).elements, function(v){
14471             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14472             v.hide();
14473         });
14474         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14475     },
14476     
14477     place: function()
14478     {
14479         if(this.isInline) return;
14480         
14481         this.picker().removeClass(['bottom', 'top']);
14482         
14483         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14484             /*
14485              * place to the top of element!
14486              *
14487              */
14488             
14489             this.picker().addClass('top');
14490             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14491             
14492             return;
14493         }
14494         
14495         this.picker().addClass('bottom');
14496         
14497         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14498     },
14499     
14500     parseDate : function(value)
14501     {
14502         if(!value || value instanceof Date){
14503             return value;
14504         }
14505         var v = Date.parseDate(value, this.format);
14506         if (!v && this.useIso) {
14507             v = Date.parseDate(value, 'Y-m-d');
14508         }
14509         if(!v && this.altFormats){
14510             if(!this.altFormatsArray){
14511                 this.altFormatsArray = this.altFormats.split("|");
14512             }
14513             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14514                 v = Date.parseDate(value, this.altFormatsArray[i]);
14515             }
14516         }
14517         return v;
14518     },
14519     
14520     formatDate : function(date, fmt)
14521     {
14522         return (!date || !(date instanceof Date)) ?
14523         date : date.dateFormat(fmt || this.format);
14524     },
14525     
14526     onFocus : function()
14527     {
14528         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14529         this.show();
14530     },
14531     
14532     onBlur : function()
14533     {
14534         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14535         
14536         var d = this.inputEl().getValue();
14537         
14538         if(d && d.length){
14539             this.setValue(d);
14540         }
14541                 
14542         this.hide();
14543     },
14544     
14545     show : function()
14546     {
14547         this.picker().show();
14548         this.update();
14549         this.place();
14550         
14551         this.fireEvent('show', this, this.date);
14552     },
14553     
14554     hide : function()
14555     {
14556         if(this.isInline) return;
14557         this.picker().hide();
14558         this.viewMode = this.startViewMode;
14559         this.showMode();
14560         
14561         this.fireEvent('hide', this, this.date);
14562         
14563     },
14564     
14565     onMousedown: function(e)
14566     {
14567         e.stopPropagation();
14568         e.preventDefault();
14569     },
14570     
14571     keyup: function(e)
14572     {
14573         Roo.bootstrap.DateField.superclass.keyup.call(this);
14574         this.update();
14575     },
14576
14577     setValue: function(v)
14578     {
14579         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14580         
14581         var d = new Date(v);
14582         
14583         if(isNaN(d.getTime())){
14584             return;
14585         }
14586         
14587         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14588
14589         this.update();
14590
14591         this.fireEvent('select', this, this.date);
14592         
14593     },
14594     
14595     getValue: function()
14596     {
14597         return this.formatDate(this.date);
14598     },
14599     
14600     fireKey: function(e)
14601     {
14602         if (!this.picker().isVisible()){
14603             if (e.keyCode == 27) // allow escape to hide and re-show picker
14604                 this.show();
14605             return;
14606         }
14607         
14608         var dateChanged = false,
14609         dir, day, month,
14610         newDate, newViewDate;
14611         
14612         switch(e.keyCode){
14613             case 27: // escape
14614                 this.hide();
14615                 e.preventDefault();
14616                 break;
14617             case 37: // left
14618             case 39: // right
14619                 if (!this.keyboardNavigation) break;
14620                 dir = e.keyCode == 37 ? -1 : 1;
14621                 
14622                 if (e.ctrlKey){
14623                     newDate = this.moveYear(this.date, dir);
14624                     newViewDate = this.moveYear(this.viewDate, dir);
14625                 } else if (e.shiftKey){
14626                     newDate = this.moveMonth(this.date, dir);
14627                     newViewDate = this.moveMonth(this.viewDate, dir);
14628                 } else {
14629                     newDate = new Date(this.date);
14630                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14631                     newViewDate = new Date(this.viewDate);
14632                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14633                 }
14634                 if (this.dateWithinRange(newDate)){
14635                     this.date = newDate;
14636                     this.viewDate = newViewDate;
14637                     this.setValue(this.formatDate(this.date));
14638 //                    this.update();
14639                     e.preventDefault();
14640                     dateChanged = true;
14641                 }
14642                 break;
14643             case 38: // up
14644             case 40: // down
14645                 if (!this.keyboardNavigation) break;
14646                 dir = e.keyCode == 38 ? -1 : 1;
14647                 if (e.ctrlKey){
14648                     newDate = this.moveYear(this.date, dir);
14649                     newViewDate = this.moveYear(this.viewDate, dir);
14650                 } else if (e.shiftKey){
14651                     newDate = this.moveMonth(this.date, dir);
14652                     newViewDate = this.moveMonth(this.viewDate, dir);
14653                 } else {
14654                     newDate = new Date(this.date);
14655                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14656                     newViewDate = new Date(this.viewDate);
14657                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14658                 }
14659                 if (this.dateWithinRange(newDate)){
14660                     this.date = newDate;
14661                     this.viewDate = newViewDate;
14662                     this.setValue(this.formatDate(this.date));
14663 //                    this.update();
14664                     e.preventDefault();
14665                     dateChanged = true;
14666                 }
14667                 break;
14668             case 13: // enter
14669                 this.setValue(this.formatDate(this.date));
14670                 this.hide();
14671                 e.preventDefault();
14672                 break;
14673             case 9: // tab
14674                 this.setValue(this.formatDate(this.date));
14675                 this.hide();
14676                 break;
14677             case 16: // shift
14678             case 17: // ctrl
14679             case 18: // alt
14680                 break;
14681             default :
14682                 this.hide();
14683                 
14684         }
14685     },
14686     
14687     
14688     onClick: function(e) 
14689     {
14690         e.stopPropagation();
14691         e.preventDefault();
14692         
14693         var target = e.getTarget();
14694         
14695         if(target.nodeName.toLowerCase() === 'i'){
14696             target = Roo.get(target).dom.parentNode;
14697         }
14698         
14699         var nodeName = target.nodeName;
14700         var className = target.className;
14701         var html = target.innerHTML;
14702         
14703         switch(nodeName.toLowerCase()) {
14704             case 'th':
14705                 switch(className) {
14706                     case 'switch':
14707                         this.showMode(1);
14708                         break;
14709                     case 'prev':
14710                     case 'next':
14711                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14712                         switch(this.viewMode){
14713                                 case 0:
14714                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14715                                         break;
14716                                 case 1:
14717                                 case 2:
14718                                         this.viewDate = this.moveYear(this.viewDate, dir);
14719                                         break;
14720                         }
14721                         this.fill();
14722                         break;
14723                     case 'today':
14724                         var date = new Date();
14725                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14726 //                        this.fill()
14727                         this.setValue(this.formatDate(this.date));
14728                         
14729                         this.hide();
14730                         break;
14731                 }
14732                 break;
14733             case 'span':
14734                 if (className.indexOf('disabled') === -1) {
14735                     this.viewDate.setUTCDate(1);
14736                     if (className.indexOf('month') !== -1) {
14737                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14738                     } else {
14739                         var year = parseInt(html, 10) || 0;
14740                         this.viewDate.setUTCFullYear(year);
14741                         
14742                     }
14743                     this.showMode(-1);
14744                     this.fill();
14745                 }
14746                 break;
14747                 
14748             case 'td':
14749                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14750                     var day = parseInt(html, 10) || 1;
14751                     var year = this.viewDate.getUTCFullYear(),
14752                         month = this.viewDate.getUTCMonth();
14753
14754                     if (className.indexOf('old') !== -1) {
14755                         if(month === 0 ){
14756                             month = 11;
14757                             year -= 1;
14758                         }else{
14759                             month -= 1;
14760                         }
14761                     } else if (className.indexOf('new') !== -1) {
14762                         if (month == 11) {
14763                             month = 0;
14764                             year += 1;
14765                         } else {
14766                             month += 1;
14767                         }
14768                     }
14769                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14770                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14771 //                    this.fill();
14772                     this.setValue(this.formatDate(this.date));
14773                     this.hide();
14774                 }
14775                 break;
14776         }
14777     },
14778     
14779     setStartDate: function(startDate)
14780     {
14781         this.startDate = startDate || -Infinity;
14782         if (this.startDate !== -Infinity) {
14783             this.startDate = this.parseDate(this.startDate);
14784         }
14785         this.update();
14786         this.updateNavArrows();
14787     },
14788
14789     setEndDate: function(endDate)
14790     {
14791         this.endDate = endDate || Infinity;
14792         if (this.endDate !== Infinity) {
14793             this.endDate = this.parseDate(this.endDate);
14794         }
14795         this.update();
14796         this.updateNavArrows();
14797     },
14798     
14799     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14800     {
14801         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14802         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14803             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14804         }
14805         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14806             return parseInt(d, 10);
14807         });
14808         this.update();
14809         this.updateNavArrows();
14810     },
14811     
14812     updateNavArrows: function() 
14813     {
14814         var d = new Date(this.viewDate),
14815         year = d.getUTCFullYear(),
14816         month = d.getUTCMonth();
14817         
14818         Roo.each(this.picker().select('.prev', true).elements, function(v){
14819             v.show();
14820             switch (this.viewMode) {
14821                 case 0:
14822
14823                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
14824                         v.hide();
14825                     }
14826                     break;
14827                 case 1:
14828                 case 2:
14829                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
14830                         v.hide();
14831                     }
14832                     break;
14833             }
14834         });
14835         
14836         Roo.each(this.picker().select('.next', true).elements, function(v){
14837             v.show();
14838             switch (this.viewMode) {
14839                 case 0:
14840
14841                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
14842                         v.hide();
14843                     }
14844                     break;
14845                 case 1:
14846                 case 2:
14847                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
14848                         v.hide();
14849                     }
14850                     break;
14851             }
14852         })
14853     },
14854     
14855     moveMonth: function(date, dir)
14856     {
14857         if (!dir) return date;
14858         var new_date = new Date(date.valueOf()),
14859         day = new_date.getUTCDate(),
14860         month = new_date.getUTCMonth(),
14861         mag = Math.abs(dir),
14862         new_month, test;
14863         dir = dir > 0 ? 1 : -1;
14864         if (mag == 1){
14865             test = dir == -1
14866             // If going back one month, make sure month is not current month
14867             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
14868             ? function(){
14869                 return new_date.getUTCMonth() == month;
14870             }
14871             // If going forward one month, make sure month is as expected
14872             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
14873             : function(){
14874                 return new_date.getUTCMonth() != new_month;
14875             };
14876             new_month = month + dir;
14877             new_date.setUTCMonth(new_month);
14878             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
14879             if (new_month < 0 || new_month > 11)
14880                 new_month = (new_month + 12) % 12;
14881         } else {
14882             // For magnitudes >1, move one month at a time...
14883             for (var i=0; i<mag; i++)
14884                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
14885                 new_date = this.moveMonth(new_date, dir);
14886             // ...then reset the day, keeping it in the new month
14887             new_month = new_date.getUTCMonth();
14888             new_date.setUTCDate(day);
14889             test = function(){
14890                 return new_month != new_date.getUTCMonth();
14891             };
14892         }
14893         // Common date-resetting loop -- if date is beyond end of month, make it
14894         // end of month
14895         while (test()){
14896             new_date.setUTCDate(--day);
14897             new_date.setUTCMonth(new_month);
14898         }
14899         return new_date;
14900     },
14901
14902     moveYear: function(date, dir)
14903     {
14904         return this.moveMonth(date, dir*12);
14905     },
14906
14907     dateWithinRange: function(date)
14908     {
14909         return date >= this.startDate && date <= this.endDate;
14910     },
14911
14912     
14913     remove: function() 
14914     {
14915         this.picker().remove();
14916     }
14917    
14918 });
14919
14920 Roo.apply(Roo.bootstrap.DateField,  {
14921     
14922     head : {
14923         tag: 'thead',
14924         cn: [
14925         {
14926             tag: 'tr',
14927             cn: [
14928             {
14929                 tag: 'th',
14930                 cls: 'prev',
14931                 html: '<i class="fa fa-arrow-left"/>'
14932             },
14933             {
14934                 tag: 'th',
14935                 cls: 'switch',
14936                 colspan: '5'
14937             },
14938             {
14939                 tag: 'th',
14940                 cls: 'next',
14941                 html: '<i class="fa fa-arrow-right"/>'
14942             }
14943
14944             ]
14945         }
14946         ]
14947     },
14948     
14949     content : {
14950         tag: 'tbody',
14951         cn: [
14952         {
14953             tag: 'tr',
14954             cn: [
14955             {
14956                 tag: 'td',
14957                 colspan: '7'
14958             }
14959             ]
14960         }
14961         ]
14962     },
14963     
14964     footer : {
14965         tag: 'tfoot',
14966         cn: [
14967         {
14968             tag: 'tr',
14969             cn: [
14970             {
14971                 tag: 'th',
14972                 colspan: '7',
14973                 cls: 'today'
14974             }
14975                     
14976             ]
14977         }
14978         ]
14979     },
14980     
14981     dates:{
14982         en: {
14983             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
14984             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
14985             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
14986             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
14987             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
14988             today: "Today"
14989         }
14990     },
14991     
14992     modes: [
14993     {
14994         clsName: 'days',
14995         navFnc: 'Month',
14996         navStep: 1
14997     },
14998     {
14999         clsName: 'months',
15000         navFnc: 'FullYear',
15001         navStep: 1
15002     },
15003     {
15004         clsName: 'years',
15005         navFnc: 'FullYear',
15006         navStep: 10
15007     }]
15008 });
15009
15010 Roo.apply(Roo.bootstrap.DateField,  {
15011   
15012     template : {
15013         tag: 'div',
15014         cls: 'datepicker dropdown-menu',
15015         cn: [
15016         {
15017             tag: 'div',
15018             cls: 'datepicker-days',
15019             cn: [
15020             {
15021                 tag: 'table',
15022                 cls: 'table-condensed',
15023                 cn:[
15024                 Roo.bootstrap.DateField.head,
15025                 {
15026                     tag: 'tbody'
15027                 },
15028                 Roo.bootstrap.DateField.footer
15029                 ]
15030             }
15031             ]
15032         },
15033         {
15034             tag: 'div',
15035             cls: 'datepicker-months',
15036             cn: [
15037             {
15038                 tag: 'table',
15039                 cls: 'table-condensed',
15040                 cn:[
15041                 Roo.bootstrap.DateField.head,
15042                 Roo.bootstrap.DateField.content,
15043                 Roo.bootstrap.DateField.footer
15044                 ]
15045             }
15046             ]
15047         },
15048         {
15049             tag: 'div',
15050             cls: 'datepicker-years',
15051             cn: [
15052             {
15053                 tag: 'table',
15054                 cls: 'table-condensed',
15055                 cn:[
15056                 Roo.bootstrap.DateField.head,
15057                 Roo.bootstrap.DateField.content,
15058                 Roo.bootstrap.DateField.footer
15059                 ]
15060             }
15061             ]
15062         }
15063         ]
15064     }
15065 });
15066
15067  
15068
15069  /*
15070  * - LGPL
15071  *
15072  * TimeField
15073  * 
15074  */
15075
15076 /**
15077  * @class Roo.bootstrap.TimeField
15078  * @extends Roo.bootstrap.Input
15079  * Bootstrap DateField class
15080  * 
15081  * 
15082  * @constructor
15083  * Create a new TimeField
15084  * @param {Object} config The config object
15085  */
15086
15087 Roo.bootstrap.TimeField = function(config){
15088     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15089     this.addEvents({
15090             /**
15091              * @event show
15092              * Fires when this field show.
15093              * @param {Roo.bootstrap.DateField} this
15094              * @param {Mixed} date The date value
15095              */
15096             show : true,
15097             /**
15098              * @event show
15099              * Fires when this field hide.
15100              * @param {Roo.bootstrap.DateField} this
15101              * @param {Mixed} date The date value
15102              */
15103             hide : true,
15104             /**
15105              * @event select
15106              * Fires when select a date.
15107              * @param {Roo.bootstrap.DateField} this
15108              * @param {Mixed} date The date value
15109              */
15110             select : true
15111         });
15112 };
15113
15114 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15115     
15116     /**
15117      * @cfg {String} format
15118      * The default time format string which can be overriden for localization support.  The format must be
15119      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15120      */
15121     format : "H:i",
15122        
15123     onRender: function(ct, position)
15124     {
15125         
15126         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15127                 
15128         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15129         
15130         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15131         
15132         this.pop = this.picker().select('>.datepicker-time',true).first();
15133         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15134         
15135         this.picker().on('mousedown', this.onMousedown, this);
15136         this.picker().on('click', this.onClick, this);
15137         
15138         this.picker().addClass('datepicker-dropdown');
15139     
15140         this.fillTime();
15141         this.update();
15142             
15143         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15144         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15145         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15146         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15147         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15148         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15149
15150     },
15151     
15152     fireKey: function(e){
15153         if (!this.picker().isVisible()){
15154             if (e.keyCode == 27) // allow escape to hide and re-show picker
15155                 this.show();
15156             return;
15157         }
15158
15159         e.preventDefault();
15160         
15161         switch(e.keyCode){
15162             case 27: // escape
15163                 this.hide();
15164                 break;
15165             case 37: // left
15166             case 39: // right
15167                 this.onTogglePeriod();
15168                 break;
15169             case 38: // up
15170                 this.onIncrementMinutes();
15171                 break;
15172             case 40: // down
15173                 this.onDecrementMinutes();
15174                 break;
15175             case 13: // enter
15176             case 9: // tab
15177                 this.setTime();
15178                 break;
15179         }
15180     },
15181     
15182     onClick: function(e) {
15183         e.stopPropagation();
15184         e.preventDefault();
15185     },
15186     
15187     picker : function()
15188     {
15189         return this.el.select('.datepicker', true).first();
15190     },
15191     
15192     fillTime: function()
15193     {    
15194         var time = this.pop.select('tbody', true).first();
15195         
15196         time.dom.innerHTML = '';
15197         
15198         time.createChild({
15199             tag: 'tr',
15200             cn: [
15201                 {
15202                     tag: 'td',
15203                     cn: [
15204                         {
15205                             tag: 'a',
15206                             href: '#',
15207                             cls: 'btn',
15208                             cn: [
15209                                 {
15210                                     tag: 'span',
15211                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15212                                 }
15213                             ]
15214                         } 
15215                     ]
15216                 },
15217                 {
15218                     tag: 'td',
15219                     cls: 'separator'
15220                 },
15221                 {
15222                     tag: 'td',
15223                     cn: [
15224                         {
15225                             tag: 'a',
15226                             href: '#',
15227                             cls: 'btn',
15228                             cn: [
15229                                 {
15230                                     tag: 'span',
15231                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15232                                 }
15233                             ]
15234                         }
15235                     ]
15236                 },
15237                 {
15238                     tag: 'td',
15239                     cls: 'separator'
15240                 }
15241             ]
15242         });
15243         
15244         time.createChild({
15245             tag: 'tr',
15246             cn: [
15247                 {
15248                     tag: 'td',
15249                     cn: [
15250                         {
15251                             tag: 'span',
15252                             cls: 'timepicker-hour',
15253                             html: '00'
15254                         }  
15255                     ]
15256                 },
15257                 {
15258                     tag: 'td',
15259                     cls: 'separator',
15260                     html: ':'
15261                 },
15262                 {
15263                     tag: 'td',
15264                     cn: [
15265                         {
15266                             tag: 'span',
15267                             cls: 'timepicker-minute',
15268                             html: '00'
15269                         }  
15270                     ]
15271                 },
15272                 {
15273                     tag: 'td',
15274                     cls: 'separator'
15275                 },
15276                 {
15277                     tag: 'td',
15278                     cn: [
15279                         {
15280                             tag: 'button',
15281                             type: 'button',
15282                             cls: 'btn btn-primary period',
15283                             html: 'AM'
15284                             
15285                         }
15286                     ]
15287                 }
15288             ]
15289         });
15290         
15291         time.createChild({
15292             tag: 'tr',
15293             cn: [
15294                 {
15295                     tag: 'td',
15296                     cn: [
15297                         {
15298                             tag: 'a',
15299                             href: '#',
15300                             cls: 'btn',
15301                             cn: [
15302                                 {
15303                                     tag: 'span',
15304                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15305                                 }
15306                             ]
15307                         }
15308                     ]
15309                 },
15310                 {
15311                     tag: 'td',
15312                     cls: 'separator'
15313                 },
15314                 {
15315                     tag: 'td',
15316                     cn: [
15317                         {
15318                             tag: 'a',
15319                             href: '#',
15320                             cls: 'btn',
15321                             cn: [
15322                                 {
15323                                     tag: 'span',
15324                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15325                                 }
15326                             ]
15327                         }
15328                     ]
15329                 },
15330                 {
15331                     tag: 'td',
15332                     cls: 'separator'
15333                 }
15334             ]
15335         });
15336         
15337     },
15338     
15339     update: function()
15340     {
15341         
15342         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15343         
15344         this.fill();
15345     },
15346     
15347     fill: function() 
15348     {
15349         var hours = this.time.getHours();
15350         var minutes = this.time.getMinutes();
15351         var period = 'AM';
15352         
15353         if(hours > 11){
15354             period = 'PM';
15355         }
15356         
15357         if(hours == 0){
15358             hours = 12;
15359         }
15360         
15361         
15362         if(hours > 12){
15363             hours = hours - 12;
15364         }
15365         
15366         if(hours < 10){
15367             hours = '0' + hours;
15368         }
15369         
15370         if(minutes < 10){
15371             minutes = '0' + minutes;
15372         }
15373         
15374         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15375         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15376         this.pop.select('button', true).first().dom.innerHTML = period;
15377         
15378     },
15379     
15380     place: function()
15381     {   
15382         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15383         
15384         var cls = ['bottom'];
15385         
15386         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15387             cls.pop();
15388             cls.push('top');
15389         }
15390         
15391         cls.push('right');
15392         
15393         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15394             cls.pop();
15395             cls.push('left');
15396         }
15397         
15398         this.picker().addClass(cls.join('-'));
15399         
15400         var _this = this;
15401         
15402         Roo.each(cls, function(c){
15403             if(c == 'bottom'){
15404                 _this.picker().setTop(_this.inputEl().getHeight());
15405                 return;
15406             }
15407             if(c == 'top'){
15408                 _this.picker().setTop(0 - _this.picker().getHeight());
15409                 return;
15410             }
15411             
15412             if(c == 'left'){
15413                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15414                 return;
15415             }
15416             if(c == 'right'){
15417                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15418                 return;
15419             }
15420         });
15421         
15422     },
15423   
15424     onFocus : function()
15425     {
15426         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15427         this.show();
15428     },
15429     
15430     onBlur : function()
15431     {
15432         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15433         this.hide();
15434     },
15435     
15436     show : function()
15437     {
15438         this.picker().show();
15439         this.pop.show();
15440         this.update();
15441         this.place();
15442         
15443         this.fireEvent('show', this, this.date);
15444     },
15445     
15446     hide : function()
15447     {
15448         this.picker().hide();
15449         this.pop.hide();
15450         
15451         this.fireEvent('hide', this, this.date);
15452     },
15453     
15454     setTime : function()
15455     {
15456         this.hide();
15457         this.setValue(this.time.format(this.format));
15458         
15459         this.fireEvent('select', this, this.date);
15460         
15461         
15462     },
15463     
15464     onMousedown: function(e){
15465         e.stopPropagation();
15466         e.preventDefault();
15467     },
15468     
15469     onIncrementHours: function()
15470     {
15471         Roo.log('onIncrementHours');
15472         this.time = this.time.add(Date.HOUR, 1);
15473         this.update();
15474         
15475     },
15476     
15477     onDecrementHours: function()
15478     {
15479         Roo.log('onDecrementHours');
15480         this.time = this.time.add(Date.HOUR, -1);
15481         this.update();
15482     },
15483     
15484     onIncrementMinutes: function()
15485     {
15486         Roo.log('onIncrementMinutes');
15487         this.time = this.time.add(Date.MINUTE, 1);
15488         this.update();
15489     },
15490     
15491     onDecrementMinutes: function()
15492     {
15493         Roo.log('onDecrementMinutes');
15494         this.time = this.time.add(Date.MINUTE, -1);
15495         this.update();
15496     },
15497     
15498     onTogglePeriod: function()
15499     {
15500         Roo.log('onTogglePeriod');
15501         this.time = this.time.add(Date.HOUR, 12);
15502         this.update();
15503     }
15504     
15505    
15506 });
15507
15508 Roo.apply(Roo.bootstrap.TimeField,  {
15509     
15510     content : {
15511         tag: 'tbody',
15512         cn: [
15513             {
15514                 tag: 'tr',
15515                 cn: [
15516                 {
15517                     tag: 'td',
15518                     colspan: '7'
15519                 }
15520                 ]
15521             }
15522         ]
15523     },
15524     
15525     footer : {
15526         tag: 'tfoot',
15527         cn: [
15528             {
15529                 tag: 'tr',
15530                 cn: [
15531                 {
15532                     tag: 'th',
15533                     colspan: '7',
15534                     cls: '',
15535                     cn: [
15536                         {
15537                             tag: 'button',
15538                             cls: 'btn btn-info ok',
15539                             html: 'OK'
15540                         }
15541                     ]
15542                 }
15543
15544                 ]
15545             }
15546         ]
15547     }
15548 });
15549
15550 Roo.apply(Roo.bootstrap.TimeField,  {
15551   
15552     template : {
15553         tag: 'div',
15554         cls: 'datepicker dropdown-menu',
15555         cn: [
15556             {
15557                 tag: 'div',
15558                 cls: 'datepicker-time',
15559                 cn: [
15560                 {
15561                     tag: 'table',
15562                     cls: 'table-condensed',
15563                     cn:[
15564                     Roo.bootstrap.TimeField.content,
15565                     Roo.bootstrap.TimeField.footer
15566                     ]
15567                 }
15568                 ]
15569             }
15570         ]
15571     }
15572 });
15573
15574  
15575
15576  /*
15577  * - LGPL
15578  *
15579  * CheckBox
15580  * 
15581  */
15582
15583 /**
15584  * @class Roo.bootstrap.CheckBox
15585  * @extends Roo.bootstrap.Input
15586  * Bootstrap CheckBox class
15587  * 
15588  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15589  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15590  * @cfg {String} boxLabel The text that appears beside the checkbox
15591  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15592  * @cfg {Boolean} checked initnal the element
15593  * 
15594  * 
15595  * @constructor
15596  * Create a new CheckBox
15597  * @param {Object} config The config object
15598  */
15599
15600 Roo.bootstrap.CheckBox = function(config){
15601     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15602    
15603         this.addEvents({
15604             /**
15605             * @event check
15606             * Fires when the element is checked or unchecked.
15607             * @param {Roo.bootstrap.CheckBox} this This input
15608             * @param {Boolean} checked The new checked value
15609             */
15610            check : true
15611         });
15612 };
15613
15614 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15615     
15616     inputType: 'checkbox',
15617     inputValue: 1,
15618     valueOff: 0,
15619     boxLabel: false,
15620     checked: false,
15621     weight : false,
15622     
15623     getAutoCreate : function()
15624     {
15625         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15626         
15627         var id = Roo.id();
15628         
15629         var cfg = {};
15630         
15631         cfg.cls = 'form-group checkbox' //input-group
15632         
15633         
15634         
15635         
15636         var input =  {
15637             tag: 'input',
15638             id : id,
15639             type : this.inputType,
15640             value : (!this.checked) ? this.valueOff : this.inputValue,
15641             cls : 'roo-checkbox', //'form-box',
15642             placeholder : this.placeholder || ''
15643             
15644         };
15645         
15646         if (this.weight) { // Validity check?
15647             cfg.cls += " checkbox-" + this.weight;
15648         }
15649         
15650         if (this.disabled) {
15651             input.disabled=true;
15652         }
15653         
15654         if(this.checked){
15655             input.checked = this.checked;
15656         }
15657         
15658         if (this.name) {
15659             input.name = this.name;
15660         }
15661         
15662         if (this.size) {
15663             input.cls += ' input-' + this.size;
15664         }
15665         
15666         var settings=this;
15667         ['xs','sm','md','lg'].map(function(size){
15668             if (settings[size]) {
15669                 cfg.cls += ' col-' + size + '-' + settings[size];
15670             }
15671         });
15672         
15673        
15674         
15675         var inputblock = input;
15676         
15677         
15678         
15679         
15680         if (this.before || this.after) {
15681             
15682             inputblock = {
15683                 cls : 'input-group',
15684                 cn :  [] 
15685             };
15686             if (this.before) {
15687                 inputblock.cn.push({
15688                     tag :'span',
15689                     cls : 'input-group-addon',
15690                     html : this.before
15691                 });
15692             }
15693             inputblock.cn.push(input);
15694             if (this.after) {
15695                 inputblock.cn.push({
15696                     tag :'span',
15697                     cls : 'input-group-addon',
15698                     html : this.after
15699                 });
15700             }
15701             
15702         };
15703         
15704         if (align ==='left' && this.fieldLabel.length) {
15705                 Roo.log("left and has label");
15706                 cfg.cn = [
15707                     
15708                     {
15709                         tag: 'label',
15710                         'for' :  id,
15711                         cls : 'control-label col-md-' + this.labelWidth,
15712                         html : this.fieldLabel
15713                         
15714                     },
15715                     {
15716                         cls : "col-md-" + (12 - this.labelWidth), 
15717                         cn: [
15718                             inputblock
15719                         ]
15720                     }
15721                     
15722                 ];
15723         } else if ( this.fieldLabel.length) {
15724                 Roo.log(" label");
15725                 cfg.cn = [
15726                    
15727                     {
15728                         tag: this.boxLabel ? 'span' : 'label',
15729                         'for': id,
15730                         cls: 'control-label box-input-label',
15731                         //cls : 'input-group-addon',
15732                         html : this.fieldLabel
15733                         
15734                     },
15735                     
15736                     inputblock
15737                     
15738                 ];
15739
15740         } else {
15741             
15742                 Roo.log(" no label && no align");
15743                 cfg.cn = [  inputblock ] ;
15744                 
15745                 
15746         };
15747          if(this.boxLabel){
15748             cfg.cn.push( {
15749                 tag: 'label',
15750                 'for': id,
15751                 cls: 'box-label',
15752                 html: this.boxLabel
15753                 
15754             });
15755         }
15756         
15757         
15758        
15759         return cfg;
15760         
15761     },
15762     
15763     /**
15764      * return the real input element.
15765      */
15766     inputEl: function ()
15767     {
15768         return this.el.select('input.roo-checkbox',true).first();
15769     },
15770     
15771     label: function()
15772     {
15773         return this.el.select('label.control-label',true).first();
15774     },
15775     
15776     initEvents : function()
15777     {
15778 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15779         
15780         this.inputEl().on('click', this.onClick,  this);
15781         
15782     },
15783     
15784     onClick : function()
15785     {   
15786         this.setChecked(!this.checked);
15787     },
15788     
15789     setChecked : function(state,suppressEvent)
15790     {
15791         this.checked = state;
15792         
15793         this.inputEl().dom.checked = state;
15794         
15795         if(suppressEvent !== true){
15796             this.fireEvent('check', this, state);
15797         }
15798         
15799         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15800         
15801     },
15802     
15803     setValue : function(v,suppressEvent)
15804     {
15805         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15806     }
15807     
15808 });
15809
15810  
15811 /*
15812  * - LGPL
15813  *
15814  * Radio
15815  * 
15816  */
15817
15818 /**
15819  * @class Roo.bootstrap.Radio
15820  * @extends Roo.bootstrap.CheckBox
15821  * Bootstrap Radio class
15822
15823  * @constructor
15824  * Create a new Radio
15825  * @param {Object} config The config object
15826  */
15827
15828 Roo.bootstrap.Radio = function(config){
15829     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
15830    
15831 };
15832
15833 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
15834     
15835     inputType: 'radio',
15836     inputValue: '',
15837     valueOff: '',
15838     
15839     getAutoCreate : function()
15840     {
15841         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15842         
15843         var id = Roo.id();
15844         
15845         var cfg = {};
15846         
15847         cfg.cls = 'form-group radio' //input-group
15848         
15849         var input =  {
15850             tag: 'input',
15851             id : id,
15852             type : this.inputType,
15853             value : (!this.checked) ? this.valueOff : this.inputValue,
15854             cls : 'roo-radio',
15855             placeholder : this.placeholder || ''
15856             
15857         };
15858           if (this.weight) { // Validity check?
15859             cfg.cls += " radio-" + this.weight;
15860         }
15861         if (this.disabled) {
15862             input.disabled=true;
15863         }
15864         
15865         if(this.checked){
15866             input.checked = this.checked;
15867         }
15868         
15869         if (this.name) {
15870             input.name = this.name;
15871         }
15872         
15873         if (this.size) {
15874             input.cls += ' input-' + this.size;
15875         }
15876         
15877         var settings=this;
15878         ['xs','sm','md','lg'].map(function(size){
15879             if (settings[size]) {
15880                 cfg.cls += ' col-' + size + '-' + settings[size];
15881             }
15882         });
15883         
15884         var inputblock = input;
15885         
15886         if (this.before || this.after) {
15887             
15888             inputblock = {
15889                 cls : 'input-group',
15890                 cn :  [] 
15891             };
15892             if (this.before) {
15893                 inputblock.cn.push({
15894                     tag :'span',
15895                     cls : 'input-group-addon',
15896                     html : this.before
15897                 });
15898             }
15899             inputblock.cn.push(input);
15900             if (this.after) {
15901                 inputblock.cn.push({
15902                     tag :'span',
15903                     cls : 'input-group-addon',
15904                     html : this.after
15905                 });
15906             }
15907             
15908         };
15909         
15910         if (align ==='left' && this.fieldLabel.length) {
15911                 Roo.log("left and has label");
15912                 cfg.cn = [
15913                     
15914                     {
15915                         tag: 'label',
15916                         'for' :  id,
15917                         cls : 'control-label col-md-' + this.labelWidth,
15918                         html : this.fieldLabel
15919                         
15920                     },
15921                     {
15922                         cls : "col-md-" + (12 - this.labelWidth), 
15923                         cn: [
15924                             inputblock
15925                         ]
15926                     }
15927                     
15928                 ];
15929         } else if ( this.fieldLabel.length) {
15930                 Roo.log(" label");
15931                  cfg.cn = [
15932                    
15933                     {
15934                         tag: 'label',
15935                         'for': id,
15936                         cls: 'control-label box-input-label',
15937                         //cls : 'input-group-addon',
15938                         html : this.fieldLabel
15939                         
15940                     },
15941                     
15942                     inputblock
15943                     
15944                 ];
15945
15946         } else {
15947             
15948                    Roo.log(" no label && no align");
15949                 cfg.cn = [
15950                     
15951                         inputblock
15952                     
15953                 ];
15954                 
15955                 
15956         };
15957         
15958         if(this.boxLabel){
15959             cfg.cn.push({
15960                 tag: 'label',
15961                 'for': id,
15962                 cls: 'box-label',
15963                 html: this.boxLabel
15964             })
15965         }
15966         
15967         return cfg;
15968         
15969     },
15970     inputEl: function ()
15971     {
15972         return this.el.select('input.roo-radio',true).first();
15973     },
15974     onClick : function()
15975     {   
15976         this.setChecked(true);
15977     },
15978     
15979     setChecked : function(state,suppressEvent)
15980     {
15981         if(state){
15982             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
15983                 v.dom.checked = false;
15984             });
15985         }
15986         
15987         this.checked = state;
15988         this.inputEl().dom.checked = state;
15989         
15990         if(suppressEvent !== true){
15991             this.fireEvent('check', this, state);
15992         }
15993         
15994         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15995         
15996     },
15997     
15998     getGroupValue : function()
15999     {
16000         var value = ''
16001         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16002             if(v.dom.checked == true){
16003                 value = v.dom.value;
16004             }
16005         });
16006         
16007         return value;
16008     },
16009     
16010     /**
16011      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16012      * @return {Mixed} value The field value
16013      */
16014     getValue : function(){
16015         return this.getGroupValue();
16016     }
16017     
16018 });
16019
16020  
16021 //<script type="text/javascript">
16022
16023 /*
16024  * Based  Ext JS Library 1.1.1
16025  * Copyright(c) 2006-2007, Ext JS, LLC.
16026  * LGPL
16027  *
16028  */
16029  
16030 /**
16031  * @class Roo.HtmlEditorCore
16032  * @extends Roo.Component
16033  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16034  *
16035  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16036  */
16037
16038 Roo.HtmlEditorCore = function(config){
16039     
16040     
16041     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16042     this.addEvents({
16043         /**
16044          * @event initialize
16045          * Fires when the editor is fully initialized (including the iframe)
16046          * @param {Roo.HtmlEditorCore} this
16047          */
16048         initialize: true,
16049         /**
16050          * @event activate
16051          * Fires when the editor is first receives the focus. Any insertion must wait
16052          * until after this event.
16053          * @param {Roo.HtmlEditorCore} this
16054          */
16055         activate: true,
16056          /**
16057          * @event beforesync
16058          * Fires before the textarea is updated with content from the editor iframe. Return false
16059          * to cancel the sync.
16060          * @param {Roo.HtmlEditorCore} this
16061          * @param {String} html
16062          */
16063         beforesync: true,
16064          /**
16065          * @event beforepush
16066          * Fires before the iframe editor is updated with content from the textarea. Return false
16067          * to cancel the push.
16068          * @param {Roo.HtmlEditorCore} this
16069          * @param {String} html
16070          */
16071         beforepush: true,
16072          /**
16073          * @event sync
16074          * Fires when the textarea is updated with content from the editor iframe.
16075          * @param {Roo.HtmlEditorCore} this
16076          * @param {String} html
16077          */
16078         sync: true,
16079          /**
16080          * @event push
16081          * Fires when the iframe editor is updated with content from the textarea.
16082          * @param {Roo.HtmlEditorCore} this
16083          * @param {String} html
16084          */
16085         push: true,
16086         
16087         /**
16088          * @event editorevent
16089          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16090          * @param {Roo.HtmlEditorCore} this
16091          */
16092         editorevent: true
16093     });
16094      
16095 };
16096
16097
16098 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16099
16100
16101      /**
16102      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16103      */
16104     
16105     owner : false,
16106     
16107      /**
16108      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16109      *                        Roo.resizable.
16110      */
16111     resizable : false,
16112      /**
16113      * @cfg {Number} height (in pixels)
16114      */   
16115     height: 300,
16116    /**
16117      * @cfg {Number} width (in pixels)
16118      */   
16119     width: 500,
16120     
16121     /**
16122      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16123      * 
16124      */
16125     stylesheets: false,
16126     
16127     // id of frame..
16128     frameId: false,
16129     
16130     // private properties
16131     validationEvent : false,
16132     deferHeight: true,
16133     initialized : false,
16134     activated : false,
16135     sourceEditMode : false,
16136     onFocus : Roo.emptyFn,
16137     iframePad:3,
16138     hideMode:'offsets',
16139     
16140     clearUp: true,
16141     
16142      
16143     
16144
16145     /**
16146      * Protected method that will not generally be called directly. It
16147      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16148      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16149      */
16150     getDocMarkup : function(){
16151         // body styles..
16152         var st = '';
16153         Roo.log(this.stylesheets);
16154         
16155         // inherit styels from page...?? 
16156         if (this.stylesheets === false) {
16157             
16158             Roo.get(document.head).select('style').each(function(node) {
16159                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16160             });
16161             
16162             Roo.get(document.head).select('link').each(function(node) { 
16163                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16164             });
16165             
16166         } else if (!this.stylesheets.length) {
16167                 // simple..
16168                 st = '<style type="text/css">' +
16169                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16170                    '</style>';
16171         } else {
16172             Roo.each(this.stylesheets, function(s) {
16173                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16174             });
16175             
16176         }
16177         
16178         st +=  '<style type="text/css">' +
16179             'IMG { cursor: pointer } ' +
16180         '</style>';
16181
16182         
16183         return '<html><head>' + st  +
16184             //<style type="text/css">' +
16185             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16186             //'</style>' +
16187             ' </head><body class="roo-htmleditor-body"></body></html>';
16188     },
16189
16190     // private
16191     onRender : function(ct, position)
16192     {
16193         var _t = this;
16194         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16195         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16196         
16197         
16198         this.el.dom.style.border = '0 none';
16199         this.el.dom.setAttribute('tabIndex', -1);
16200         this.el.addClass('x-hidden hide');
16201         
16202         
16203         
16204         if(Roo.isIE){ // fix IE 1px bogus margin
16205             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16206         }
16207        
16208         
16209         this.frameId = Roo.id();
16210         
16211          
16212         
16213         var iframe = this.owner.wrap.createChild({
16214             tag: 'iframe',
16215             cls: 'form-control', // bootstrap..
16216             id: this.frameId,
16217             name: this.frameId,
16218             frameBorder : 'no',
16219             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16220         }, this.el
16221         );
16222         
16223         
16224         this.iframe = iframe.dom;
16225
16226          this.assignDocWin();
16227         
16228         this.doc.designMode = 'on';
16229        
16230         this.doc.open();
16231         this.doc.write(this.getDocMarkup());
16232         this.doc.close();
16233
16234         
16235         var task = { // must defer to wait for browser to be ready
16236             run : function(){
16237                 //console.log("run task?" + this.doc.readyState);
16238                 this.assignDocWin();
16239                 if(this.doc.body || this.doc.readyState == 'complete'){
16240                     try {
16241                         this.doc.designMode="on";
16242                     } catch (e) {
16243                         return;
16244                     }
16245                     Roo.TaskMgr.stop(task);
16246                     this.initEditor.defer(10, this);
16247                 }
16248             },
16249             interval : 10,
16250             duration: 10000,
16251             scope: this
16252         };
16253         Roo.TaskMgr.start(task);
16254
16255         
16256          
16257     },
16258
16259     // private
16260     onResize : function(w, h)
16261     {
16262          Roo.log('resize: ' +w + ',' + h );
16263         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16264         if(!this.iframe){
16265             return;
16266         }
16267         if(typeof w == 'number'){
16268             
16269             this.iframe.style.width = w + 'px';
16270         }
16271         if(typeof h == 'number'){
16272             
16273             this.iframe.style.height = h + 'px';
16274             if(this.doc){
16275                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16276             }
16277         }
16278         
16279     },
16280
16281     /**
16282      * Toggles the editor between standard and source edit mode.
16283      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16284      */
16285     toggleSourceEdit : function(sourceEditMode){
16286         
16287         this.sourceEditMode = sourceEditMode === true;
16288         
16289         if(this.sourceEditMode){
16290  
16291             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16292             
16293         }else{
16294             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16295             //this.iframe.className = '';
16296             this.deferFocus();
16297         }
16298         //this.setSize(this.owner.wrap.getSize());
16299         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16300     },
16301
16302     
16303   
16304
16305     /**
16306      * Protected method that will not generally be called directly. If you need/want
16307      * custom HTML cleanup, this is the method you should override.
16308      * @param {String} html The HTML to be cleaned
16309      * return {String} The cleaned HTML
16310      */
16311     cleanHtml : function(html){
16312         html = String(html);
16313         if(html.length > 5){
16314             if(Roo.isSafari){ // strip safari nonsense
16315                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16316             }
16317         }
16318         if(html == '&nbsp;'){
16319             html = '';
16320         }
16321         return html;
16322     },
16323
16324     /**
16325      * HTML Editor -> Textarea
16326      * Protected method that will not generally be called directly. Syncs the contents
16327      * of the editor iframe with the textarea.
16328      */
16329     syncValue : function(){
16330         if(this.initialized){
16331             var bd = (this.doc.body || this.doc.documentElement);
16332             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16333             var html = bd.innerHTML;
16334             if(Roo.isSafari){
16335                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16336                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16337                 if(m && m[1]){
16338                     html = '<div style="'+m[0]+'">' + html + '</div>';
16339                 }
16340             }
16341             html = this.cleanHtml(html);
16342             // fix up the special chars.. normaly like back quotes in word...
16343             // however we do not want to do this with chinese..
16344             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16345                 var cc = b.charCodeAt();
16346                 if (
16347                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16348                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16349                     (cc >= 0xf900 && cc < 0xfb00 )
16350                 ) {
16351                         return b;
16352                 }
16353                 return "&#"+cc+";" 
16354             });
16355             if(this.owner.fireEvent('beforesync', this, html) !== false){
16356                 this.el.dom.value = html;
16357                 this.owner.fireEvent('sync', this, html);
16358             }
16359         }
16360     },
16361
16362     /**
16363      * Protected method that will not generally be called directly. Pushes the value of the textarea
16364      * into the iframe editor.
16365      */
16366     pushValue : function(){
16367         if(this.initialized){
16368             var v = this.el.dom.value.trim();
16369             
16370 //            if(v.length < 1){
16371 //                v = '&#160;';
16372 //            }
16373             
16374             if(this.owner.fireEvent('beforepush', this, v) !== false){
16375                 var d = (this.doc.body || this.doc.documentElement);
16376                 d.innerHTML = v;
16377                 this.cleanUpPaste();
16378                 this.el.dom.value = d.innerHTML;
16379                 this.owner.fireEvent('push', this, v);
16380             }
16381         }
16382     },
16383
16384     // private
16385     deferFocus : function(){
16386         this.focus.defer(10, this);
16387     },
16388
16389     // doc'ed in Field
16390     focus : function(){
16391         if(this.win && !this.sourceEditMode){
16392             this.win.focus();
16393         }else{
16394             this.el.focus();
16395         }
16396     },
16397     
16398     assignDocWin: function()
16399     {
16400         var iframe = this.iframe;
16401         
16402          if(Roo.isIE){
16403             this.doc = iframe.contentWindow.document;
16404             this.win = iframe.contentWindow;
16405         } else {
16406             if (!Roo.get(this.frameId)) {
16407                 return;
16408             }
16409             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16410             this.win = Roo.get(this.frameId).dom.contentWindow;
16411         }
16412     },
16413     
16414     // private
16415     initEditor : function(){
16416         //console.log("INIT EDITOR");
16417         this.assignDocWin();
16418         
16419         
16420         
16421         this.doc.designMode="on";
16422         this.doc.open();
16423         this.doc.write(this.getDocMarkup());
16424         this.doc.close();
16425         
16426         var dbody = (this.doc.body || this.doc.documentElement);
16427         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16428         // this copies styles from the containing element into thsi one..
16429         // not sure why we need all of this..
16430         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16431         
16432         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16433         //ss['background-attachment'] = 'fixed'; // w3c
16434         dbody.bgProperties = 'fixed'; // ie
16435         //Roo.DomHelper.applyStyles(dbody, ss);
16436         Roo.EventManager.on(this.doc, {
16437             //'mousedown': this.onEditorEvent,
16438             'mouseup': this.onEditorEvent,
16439             'dblclick': this.onEditorEvent,
16440             'click': this.onEditorEvent,
16441             'keyup': this.onEditorEvent,
16442             buffer:100,
16443             scope: this
16444         });
16445         if(Roo.isGecko){
16446             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16447         }
16448         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16449             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16450         }
16451         this.initialized = true;
16452
16453         this.owner.fireEvent('initialize', this);
16454         this.pushValue();
16455     },
16456
16457     // private
16458     onDestroy : function(){
16459         
16460         
16461         
16462         if(this.rendered){
16463             
16464             //for (var i =0; i < this.toolbars.length;i++) {
16465             //    // fixme - ask toolbars for heights?
16466             //    this.toolbars[i].onDestroy();
16467            // }
16468             
16469             //this.wrap.dom.innerHTML = '';
16470             //this.wrap.remove();
16471         }
16472     },
16473
16474     // private
16475     onFirstFocus : function(){
16476         
16477         this.assignDocWin();
16478         
16479         
16480         this.activated = true;
16481          
16482     
16483         if(Roo.isGecko){ // prevent silly gecko errors
16484             this.win.focus();
16485             var s = this.win.getSelection();
16486             if(!s.focusNode || s.focusNode.nodeType != 3){
16487                 var r = s.getRangeAt(0);
16488                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16489                 r.collapse(true);
16490                 this.deferFocus();
16491             }
16492             try{
16493                 this.execCmd('useCSS', true);
16494                 this.execCmd('styleWithCSS', false);
16495             }catch(e){}
16496         }
16497         this.owner.fireEvent('activate', this);
16498     },
16499
16500     // private
16501     adjustFont: function(btn){
16502         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16503         //if(Roo.isSafari){ // safari
16504         //    adjust *= 2;
16505        // }
16506         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16507         if(Roo.isSafari){ // safari
16508             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16509             v =  (v < 10) ? 10 : v;
16510             v =  (v > 48) ? 48 : v;
16511             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16512             
16513         }
16514         
16515         
16516         v = Math.max(1, v+adjust);
16517         
16518         this.execCmd('FontSize', v  );
16519     },
16520
16521     onEditorEvent : function(e){
16522         this.owner.fireEvent('editorevent', this, e);
16523       //  this.updateToolbar();
16524         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16525     },
16526
16527     insertTag : function(tg)
16528     {
16529         // could be a bit smarter... -> wrap the current selected tRoo..
16530         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16531             
16532             range = this.createRange(this.getSelection());
16533             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16534             wrappingNode.appendChild(range.extractContents());
16535             range.insertNode(wrappingNode);
16536
16537             return;
16538             
16539             
16540             
16541         }
16542         this.execCmd("formatblock",   tg);
16543         
16544     },
16545     
16546     insertText : function(txt)
16547     {
16548         
16549         
16550         var range = this.createRange();
16551         range.deleteContents();
16552                //alert(Sender.getAttribute('label'));
16553                
16554         range.insertNode(this.doc.createTextNode(txt));
16555     } ,
16556     
16557      
16558
16559     /**
16560      * Executes a Midas editor command on the editor document and performs necessary focus and
16561      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16562      * @param {String} cmd The Midas command
16563      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16564      */
16565     relayCmd : function(cmd, value){
16566         this.win.focus();
16567         this.execCmd(cmd, value);
16568         this.owner.fireEvent('editorevent', this);
16569         //this.updateToolbar();
16570         this.owner.deferFocus();
16571     },
16572
16573     /**
16574      * Executes a Midas editor command directly on the editor document.
16575      * For visual commands, you should use {@link #relayCmd} instead.
16576      * <b>This should only be called after the editor is initialized.</b>
16577      * @param {String} cmd The Midas command
16578      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16579      */
16580     execCmd : function(cmd, value){
16581         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16582         this.syncValue();
16583     },
16584  
16585  
16586    
16587     /**
16588      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16589      * to insert tRoo.
16590      * @param {String} text | dom node.. 
16591      */
16592     insertAtCursor : function(text)
16593     {
16594         
16595         
16596         
16597         if(!this.activated){
16598             return;
16599         }
16600         /*
16601         if(Roo.isIE){
16602             this.win.focus();
16603             var r = this.doc.selection.createRange();
16604             if(r){
16605                 r.collapse(true);
16606                 r.pasteHTML(text);
16607                 this.syncValue();
16608                 this.deferFocus();
16609             
16610             }
16611             return;
16612         }
16613         */
16614         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16615             this.win.focus();
16616             
16617             
16618             // from jquery ui (MIT licenced)
16619             var range, node;
16620             var win = this.win;
16621             
16622             if (win.getSelection && win.getSelection().getRangeAt) {
16623                 range = win.getSelection().getRangeAt(0);
16624                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16625                 range.insertNode(node);
16626             } else if (win.document.selection && win.document.selection.createRange) {
16627                 // no firefox support
16628                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16629                 win.document.selection.createRange().pasteHTML(txt);
16630             } else {
16631                 // no firefox support
16632                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16633                 this.execCmd('InsertHTML', txt);
16634             } 
16635             
16636             this.syncValue();
16637             
16638             this.deferFocus();
16639         }
16640     },
16641  // private
16642     mozKeyPress : function(e){
16643         if(e.ctrlKey){
16644             var c = e.getCharCode(), cmd;
16645           
16646             if(c > 0){
16647                 c = String.fromCharCode(c).toLowerCase();
16648                 switch(c){
16649                     case 'b':
16650                         cmd = 'bold';
16651                         break;
16652                     case 'i':
16653                         cmd = 'italic';
16654                         break;
16655                     
16656                     case 'u':
16657                         cmd = 'underline';
16658                         break;
16659                     
16660                     case 'v':
16661                         this.cleanUpPaste.defer(100, this);
16662                         return;
16663                         
16664                 }
16665                 if(cmd){
16666                     this.win.focus();
16667                     this.execCmd(cmd);
16668                     this.deferFocus();
16669                     e.preventDefault();
16670                 }
16671                 
16672             }
16673         }
16674     },
16675
16676     // private
16677     fixKeys : function(){ // load time branching for fastest keydown performance
16678         if(Roo.isIE){
16679             return function(e){
16680                 var k = e.getKey(), r;
16681                 if(k == e.TAB){
16682                     e.stopEvent();
16683                     r = this.doc.selection.createRange();
16684                     if(r){
16685                         r.collapse(true);
16686                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16687                         this.deferFocus();
16688                     }
16689                     return;
16690                 }
16691                 
16692                 if(k == e.ENTER){
16693                     r = this.doc.selection.createRange();
16694                     if(r){
16695                         var target = r.parentElement();
16696                         if(!target || target.tagName.toLowerCase() != 'li'){
16697                             e.stopEvent();
16698                             r.pasteHTML('<br />');
16699                             r.collapse(false);
16700                             r.select();
16701                         }
16702                     }
16703                 }
16704                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16705                     this.cleanUpPaste.defer(100, this);
16706                     return;
16707                 }
16708                 
16709                 
16710             };
16711         }else if(Roo.isOpera){
16712             return function(e){
16713                 var k = e.getKey();
16714                 if(k == e.TAB){
16715                     e.stopEvent();
16716                     this.win.focus();
16717                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16718                     this.deferFocus();
16719                 }
16720                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16721                     this.cleanUpPaste.defer(100, this);
16722                     return;
16723                 }
16724                 
16725             };
16726         }else if(Roo.isSafari){
16727             return function(e){
16728                 var k = e.getKey();
16729                 
16730                 if(k == e.TAB){
16731                     e.stopEvent();
16732                     this.execCmd('InsertText','\t');
16733                     this.deferFocus();
16734                     return;
16735                 }
16736                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16737                     this.cleanUpPaste.defer(100, this);
16738                     return;
16739                 }
16740                 
16741              };
16742         }
16743     }(),
16744     
16745     getAllAncestors: function()
16746     {
16747         var p = this.getSelectedNode();
16748         var a = [];
16749         if (!p) {
16750             a.push(p); // push blank onto stack..
16751             p = this.getParentElement();
16752         }
16753         
16754         
16755         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16756             a.push(p);
16757             p = p.parentNode;
16758         }
16759         a.push(this.doc.body);
16760         return a;
16761     },
16762     lastSel : false,
16763     lastSelNode : false,
16764     
16765     
16766     getSelection : function() 
16767     {
16768         this.assignDocWin();
16769         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16770     },
16771     
16772     getSelectedNode: function() 
16773     {
16774         // this may only work on Gecko!!!
16775         
16776         // should we cache this!!!!
16777         
16778         
16779         
16780          
16781         var range = this.createRange(this.getSelection()).cloneRange();
16782         
16783         if (Roo.isIE) {
16784             var parent = range.parentElement();
16785             while (true) {
16786                 var testRange = range.duplicate();
16787                 testRange.moveToElementText(parent);
16788                 if (testRange.inRange(range)) {
16789                     break;
16790                 }
16791                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16792                     break;
16793                 }
16794                 parent = parent.parentElement;
16795             }
16796             return parent;
16797         }
16798         
16799         // is ancestor a text element.
16800         var ac =  range.commonAncestorContainer;
16801         if (ac.nodeType == 3) {
16802             ac = ac.parentNode;
16803         }
16804         
16805         var ar = ac.childNodes;
16806          
16807         var nodes = [];
16808         var other_nodes = [];
16809         var has_other_nodes = false;
16810         for (var i=0;i<ar.length;i++) {
16811             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16812                 continue;
16813             }
16814             // fullly contained node.
16815             
16816             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
16817                 nodes.push(ar[i]);
16818                 continue;
16819             }
16820             
16821             // probably selected..
16822             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
16823                 other_nodes.push(ar[i]);
16824                 continue;
16825             }
16826             // outer..
16827             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
16828                 continue;
16829             }
16830             
16831             
16832             has_other_nodes = true;
16833         }
16834         if (!nodes.length && other_nodes.length) {
16835             nodes= other_nodes;
16836         }
16837         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
16838             return false;
16839         }
16840         
16841         return nodes[0];
16842     },
16843     createRange: function(sel)
16844     {
16845         // this has strange effects when using with 
16846         // top toolbar - not sure if it's a great idea.
16847         //this.editor.contentWindow.focus();
16848         if (typeof sel != "undefined") {
16849             try {
16850                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
16851             } catch(e) {
16852                 return this.doc.createRange();
16853             }
16854         } else {
16855             return this.doc.createRange();
16856         }
16857     },
16858     getParentElement: function()
16859     {
16860         
16861         this.assignDocWin();
16862         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
16863         
16864         var range = this.createRange(sel);
16865          
16866         try {
16867             var p = range.commonAncestorContainer;
16868             while (p.nodeType == 3) { // text node
16869                 p = p.parentNode;
16870             }
16871             return p;
16872         } catch (e) {
16873             return null;
16874         }
16875     
16876     },
16877     /***
16878      *
16879      * Range intersection.. the hard stuff...
16880      *  '-1' = before
16881      *  '0' = hits..
16882      *  '1' = after.
16883      *         [ -- selected range --- ]
16884      *   [fail]                        [fail]
16885      *
16886      *    basically..
16887      *      if end is before start or  hits it. fail.
16888      *      if start is after end or hits it fail.
16889      *
16890      *   if either hits (but other is outside. - then it's not 
16891      *   
16892      *    
16893      **/
16894     
16895     
16896     // @see http://www.thismuchiknow.co.uk/?p=64.
16897     rangeIntersectsNode : function(range, node)
16898     {
16899         var nodeRange = node.ownerDocument.createRange();
16900         try {
16901             nodeRange.selectNode(node);
16902         } catch (e) {
16903             nodeRange.selectNodeContents(node);
16904         }
16905     
16906         var rangeStartRange = range.cloneRange();
16907         rangeStartRange.collapse(true);
16908     
16909         var rangeEndRange = range.cloneRange();
16910         rangeEndRange.collapse(false);
16911     
16912         var nodeStartRange = nodeRange.cloneRange();
16913         nodeStartRange.collapse(true);
16914     
16915         var nodeEndRange = nodeRange.cloneRange();
16916         nodeEndRange.collapse(false);
16917     
16918         return rangeStartRange.compareBoundaryPoints(
16919                  Range.START_TO_START, nodeEndRange) == -1 &&
16920                rangeEndRange.compareBoundaryPoints(
16921                  Range.START_TO_START, nodeStartRange) == 1;
16922         
16923          
16924     },
16925     rangeCompareNode : function(range, node)
16926     {
16927         var nodeRange = node.ownerDocument.createRange();
16928         try {
16929             nodeRange.selectNode(node);
16930         } catch (e) {
16931             nodeRange.selectNodeContents(node);
16932         }
16933         
16934         
16935         range.collapse(true);
16936     
16937         nodeRange.collapse(true);
16938      
16939         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
16940         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
16941          
16942         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
16943         
16944         var nodeIsBefore   =  ss == 1;
16945         var nodeIsAfter    = ee == -1;
16946         
16947         if (nodeIsBefore && nodeIsAfter)
16948             return 0; // outer
16949         if (!nodeIsBefore && nodeIsAfter)
16950             return 1; //right trailed.
16951         
16952         if (nodeIsBefore && !nodeIsAfter)
16953             return 2;  // left trailed.
16954         // fully contined.
16955         return 3;
16956     },
16957
16958     // private? - in a new class?
16959     cleanUpPaste :  function()
16960     {
16961         // cleans up the whole document..
16962         Roo.log('cleanuppaste');
16963         
16964         this.cleanUpChildren(this.doc.body);
16965         var clean = this.cleanWordChars(this.doc.body.innerHTML);
16966         if (clean != this.doc.body.innerHTML) {
16967             this.doc.body.innerHTML = clean;
16968         }
16969         
16970     },
16971     
16972     cleanWordChars : function(input) {// change the chars to hex code
16973         var he = Roo.HtmlEditorCore;
16974         
16975         var output = input;
16976         Roo.each(he.swapCodes, function(sw) { 
16977             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
16978             
16979             output = output.replace(swapper, sw[1]);
16980         });
16981         
16982         return output;
16983     },
16984     
16985     
16986     cleanUpChildren : function (n)
16987     {
16988         if (!n.childNodes.length) {
16989             return;
16990         }
16991         for (var i = n.childNodes.length-1; i > -1 ; i--) {
16992            this.cleanUpChild(n.childNodes[i]);
16993         }
16994     },
16995     
16996     
16997         
16998     
16999     cleanUpChild : function (node)
17000     {
17001         var ed = this;
17002         //console.log(node);
17003         if (node.nodeName == "#text") {
17004             // clean up silly Windows -- stuff?
17005             return; 
17006         }
17007         if (node.nodeName == "#comment") {
17008             node.parentNode.removeChild(node);
17009             // clean up silly Windows -- stuff?
17010             return; 
17011         }
17012         
17013         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17014             // remove node.
17015             node.parentNode.removeChild(node);
17016             return;
17017             
17018         }
17019         
17020         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17021         
17022         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17023         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17024         
17025         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17026         //    remove_keep_children = true;
17027         //}
17028         
17029         if (remove_keep_children) {
17030             this.cleanUpChildren(node);
17031             // inserts everything just before this node...
17032             while (node.childNodes.length) {
17033                 var cn = node.childNodes[0];
17034                 node.removeChild(cn);
17035                 node.parentNode.insertBefore(cn, node);
17036             }
17037             node.parentNode.removeChild(node);
17038             return;
17039         }
17040         
17041         if (!node.attributes || !node.attributes.length) {
17042             this.cleanUpChildren(node);
17043             return;
17044         }
17045         
17046         function cleanAttr(n,v)
17047         {
17048             
17049             if (v.match(/^\./) || v.match(/^\//)) {
17050                 return;
17051             }
17052             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17053                 return;
17054             }
17055             if (v.match(/^#/)) {
17056                 return;
17057             }
17058 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17059             node.removeAttribute(n);
17060             
17061         }
17062         
17063         function cleanStyle(n,v)
17064         {
17065             if (v.match(/expression/)) { //XSS?? should we even bother..
17066                 node.removeAttribute(n);
17067                 return;
17068             }
17069             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17070             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17071             
17072             
17073             var parts = v.split(/;/);
17074             var clean = [];
17075             
17076             Roo.each(parts, function(p) {
17077                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17078                 if (!p.length) {
17079                     return true;
17080                 }
17081                 var l = p.split(':').shift().replace(/\s+/g,'');
17082                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17083                 
17084                 if ( cblack.indexOf(l) > -1) {
17085 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17086                     //node.removeAttribute(n);
17087                     return true;
17088                 }
17089                 //Roo.log()
17090                 // only allow 'c whitelisted system attributes'
17091                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17092 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17093                     //node.removeAttribute(n);
17094                     return true;
17095                 }
17096                 
17097                 
17098                  
17099                 
17100                 clean.push(p);
17101                 return true;
17102             });
17103             if (clean.length) { 
17104                 node.setAttribute(n, clean.join(';'));
17105             } else {
17106                 node.removeAttribute(n);
17107             }
17108             
17109         }
17110         
17111         
17112         for (var i = node.attributes.length-1; i > -1 ; i--) {
17113             var a = node.attributes[i];
17114             //console.log(a);
17115             
17116             if (a.name.toLowerCase().substr(0,2)=='on')  {
17117                 node.removeAttribute(a.name);
17118                 continue;
17119             }
17120             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17121                 node.removeAttribute(a.name);
17122                 continue;
17123             }
17124             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17125                 cleanAttr(a.name,a.value); // fixme..
17126                 continue;
17127             }
17128             if (a.name == 'style') {
17129                 cleanStyle(a.name,a.value);
17130                 continue;
17131             }
17132             /// clean up MS crap..
17133             // tecnically this should be a list of valid class'es..
17134             
17135             
17136             if (a.name == 'class') {
17137                 if (a.value.match(/^Mso/)) {
17138                     node.className = '';
17139                 }
17140                 
17141                 if (a.value.match(/body/)) {
17142                     node.className = '';
17143                 }
17144                 continue;
17145             }
17146             
17147             // style cleanup!?
17148             // class cleanup?
17149             
17150         }
17151         
17152         
17153         this.cleanUpChildren(node);
17154         
17155         
17156     },
17157     /**
17158      * Clean up MS wordisms...
17159      */
17160     cleanWord : function(node)
17161     {
17162         var _t = this;
17163         var cleanWordChildren = function()
17164         {
17165             if (!node.childNodes.length) {
17166                 return;
17167             }
17168             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17169                _t.cleanWord(node.childNodes[i]);
17170             }
17171         }
17172         
17173         
17174         if (!node) {
17175             this.cleanWord(this.doc.body);
17176             return;
17177         }
17178         if (node.nodeName == "#text") {
17179             // clean up silly Windows -- stuff?
17180             return; 
17181         }
17182         if (node.nodeName == "#comment") {
17183             node.parentNode.removeChild(node);
17184             // clean up silly Windows -- stuff?
17185             return; 
17186         }
17187         
17188         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17189             node.parentNode.removeChild(node);
17190             return;
17191         }
17192         
17193         // remove - but keep children..
17194         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17195             while (node.childNodes.length) {
17196                 var cn = node.childNodes[0];
17197                 node.removeChild(cn);
17198                 node.parentNode.insertBefore(cn, node);
17199             }
17200             node.parentNode.removeChild(node);
17201             cleanWordChildren();
17202             return;
17203         }
17204         // clean styles
17205         if (node.className.length) {
17206             
17207             var cn = node.className.split(/\W+/);
17208             var cna = [];
17209             Roo.each(cn, function(cls) {
17210                 if (cls.match(/Mso[a-zA-Z]+/)) {
17211                     return;
17212                 }
17213                 cna.push(cls);
17214             });
17215             node.className = cna.length ? cna.join(' ') : '';
17216             if (!cna.length) {
17217                 node.removeAttribute("class");
17218             }
17219         }
17220         
17221         if (node.hasAttribute("lang")) {
17222             node.removeAttribute("lang");
17223         }
17224         
17225         if (node.hasAttribute("style")) {
17226             
17227             var styles = node.getAttribute("style").split(";");
17228             var nstyle = [];
17229             Roo.each(styles, function(s) {
17230                 if (!s.match(/:/)) {
17231                     return;
17232                 }
17233                 var kv = s.split(":");
17234                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17235                     return;
17236                 }
17237                 // what ever is left... we allow.
17238                 nstyle.push(s);
17239             });
17240             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17241             if (!nstyle.length) {
17242                 node.removeAttribute('style');
17243             }
17244         }
17245         
17246         cleanWordChildren();
17247         
17248         
17249     },
17250     domToHTML : function(currentElement, depth, nopadtext) {
17251         
17252             depth = depth || 0;
17253             nopadtext = nopadtext || false;
17254         
17255             if (!currentElement) {
17256                 return this.domToHTML(this.doc.body);
17257             }
17258             
17259             //Roo.log(currentElement);
17260             var j;
17261             var allText = false;
17262             var nodeName = currentElement.nodeName;
17263             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17264             
17265             if  (nodeName == '#text') {
17266                 return currentElement.nodeValue;
17267             }
17268             
17269             
17270             var ret = '';
17271             if (nodeName != 'BODY') {
17272                  
17273                 var i = 0;
17274                 // Prints the node tagName, such as <A>, <IMG>, etc
17275                 if (tagName) {
17276                     var attr = [];
17277                     for(i = 0; i < currentElement.attributes.length;i++) {
17278                         // quoting?
17279                         var aname = currentElement.attributes.item(i).name;
17280                         if (!currentElement.attributes.item(i).value.length) {
17281                             continue;
17282                         }
17283                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17284                     }
17285                     
17286                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17287                 } 
17288                 else {
17289                     
17290                     // eack
17291                 }
17292             } else {
17293                 tagName = false;
17294             }
17295             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17296                 return ret;
17297             }
17298             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17299                 nopadtext = true;
17300             }
17301             
17302             
17303             // Traverse the tree
17304             i = 0;
17305             var currentElementChild = currentElement.childNodes.item(i);
17306             var allText = true;
17307             var innerHTML  = '';
17308             lastnode = '';
17309             while (currentElementChild) {
17310                 // Formatting code (indent the tree so it looks nice on the screen)
17311                 var nopad = nopadtext;
17312                 if (lastnode == 'SPAN') {
17313                     nopad  = true;
17314                 }
17315                 // text
17316                 if  (currentElementChild.nodeName == '#text') {
17317                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17318                     if (!nopad && toadd.length > 80) {
17319                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17320                     }
17321                     innerHTML  += toadd;
17322                     
17323                     i++;
17324                     currentElementChild = currentElement.childNodes.item(i);
17325                     lastNode = '';
17326                     continue;
17327                 }
17328                 allText = false;
17329                 
17330                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17331                     
17332                 // Recursively traverse the tree structure of the child node
17333                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17334                 lastnode = currentElementChild.nodeName;
17335                 i++;
17336                 currentElementChild=currentElement.childNodes.item(i);
17337             }
17338             
17339             ret += innerHTML;
17340             
17341             if (!allText) {
17342                     // The remaining code is mostly for formatting the tree
17343                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17344             }
17345             
17346             
17347             if (tagName) {
17348                 ret+= "</"+tagName+">";
17349             }
17350             return ret;
17351             
17352         }
17353     
17354     // hide stuff that is not compatible
17355     /**
17356      * @event blur
17357      * @hide
17358      */
17359     /**
17360      * @event change
17361      * @hide
17362      */
17363     /**
17364      * @event focus
17365      * @hide
17366      */
17367     /**
17368      * @event specialkey
17369      * @hide
17370      */
17371     /**
17372      * @cfg {String} fieldClass @hide
17373      */
17374     /**
17375      * @cfg {String} focusClass @hide
17376      */
17377     /**
17378      * @cfg {String} autoCreate @hide
17379      */
17380     /**
17381      * @cfg {String} inputType @hide
17382      */
17383     /**
17384      * @cfg {String} invalidClass @hide
17385      */
17386     /**
17387      * @cfg {String} invalidText @hide
17388      */
17389     /**
17390      * @cfg {String} msgFx @hide
17391      */
17392     /**
17393      * @cfg {String} validateOnBlur @hide
17394      */
17395 });
17396
17397 Roo.HtmlEditorCore.white = [
17398         'area', 'br', 'img', 'input', 'hr', 'wbr',
17399         
17400        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17401        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17402        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17403        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17404        'table',   'ul',         'xmp', 
17405        
17406        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17407       'thead',   'tr', 
17408      
17409       'dir', 'menu', 'ol', 'ul', 'dl',
17410        
17411       'embed',  'object'
17412 ];
17413
17414
17415 Roo.HtmlEditorCore.black = [
17416     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17417         'applet', // 
17418         'base',   'basefont', 'bgsound', 'blink',  'body', 
17419         'frame',  'frameset', 'head',    'html',   'ilayer', 
17420         'iframe', 'layer',  'link',     'meta',    'object',   
17421         'script', 'style' ,'title',  'xml' // clean later..
17422 ];
17423 Roo.HtmlEditorCore.clean = [
17424     'script', 'style', 'title', 'xml'
17425 ];
17426 Roo.HtmlEditorCore.remove = [
17427     'font'
17428 ];
17429 // attributes..
17430
17431 Roo.HtmlEditorCore.ablack = [
17432     'on'
17433 ];
17434     
17435 Roo.HtmlEditorCore.aclean = [ 
17436     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17437 ];
17438
17439 // protocols..
17440 Roo.HtmlEditorCore.pwhite= [
17441         'http',  'https',  'mailto'
17442 ];
17443
17444 // white listed style attributes.
17445 Roo.HtmlEditorCore.cwhite= [
17446       //  'text-align', /// default is to allow most things..
17447       
17448          
17449 //        'font-size'//??
17450 ];
17451
17452 // black listed style attributes.
17453 Roo.HtmlEditorCore.cblack= [
17454       //  'font-size' -- this can be set by the project 
17455 ];
17456
17457
17458 Roo.HtmlEditorCore.swapCodes   =[ 
17459     [    8211, "--" ], 
17460     [    8212, "--" ], 
17461     [    8216,  "'" ],  
17462     [    8217, "'" ],  
17463     [    8220, '"' ],  
17464     [    8221, '"' ],  
17465     [    8226, "*" ],  
17466     [    8230, "..." ]
17467 ]; 
17468
17469     /*
17470  * - LGPL
17471  *
17472  * HtmlEditor
17473  * 
17474  */
17475
17476 /**
17477  * @class Roo.bootstrap.HtmlEditor
17478  * @extends Roo.bootstrap.TextArea
17479  * Bootstrap HtmlEditor class
17480
17481  * @constructor
17482  * Create a new HtmlEditor
17483  * @param {Object} config The config object
17484  */
17485
17486 Roo.bootstrap.HtmlEditor = function(config){
17487     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17488     if (!this.toolbars) {
17489         this.toolbars = [];
17490     }
17491     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17492     this.addEvents({
17493             /**
17494              * @event initialize
17495              * Fires when the editor is fully initialized (including the iframe)
17496              * @param {HtmlEditor} this
17497              */
17498             initialize: true,
17499             /**
17500              * @event activate
17501              * Fires when the editor is first receives the focus. Any insertion must wait
17502              * until after this event.
17503              * @param {HtmlEditor} this
17504              */
17505             activate: true,
17506              /**
17507              * @event beforesync
17508              * Fires before the textarea is updated with content from the editor iframe. Return false
17509              * to cancel the sync.
17510              * @param {HtmlEditor} this
17511              * @param {String} html
17512              */
17513             beforesync: true,
17514              /**
17515              * @event beforepush
17516              * Fires before the iframe editor is updated with content from the textarea. Return false
17517              * to cancel the push.
17518              * @param {HtmlEditor} this
17519              * @param {String} html
17520              */
17521             beforepush: true,
17522              /**
17523              * @event sync
17524              * Fires when the textarea is updated with content from the editor iframe.
17525              * @param {HtmlEditor} this
17526              * @param {String} html
17527              */
17528             sync: true,
17529              /**
17530              * @event push
17531              * Fires when the iframe editor is updated with content from the textarea.
17532              * @param {HtmlEditor} this
17533              * @param {String} html
17534              */
17535             push: true,
17536              /**
17537              * @event editmodechange
17538              * Fires when the editor switches edit modes
17539              * @param {HtmlEditor} this
17540              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17541              */
17542             editmodechange: true,
17543             /**
17544              * @event editorevent
17545              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17546              * @param {HtmlEditor} this
17547              */
17548             editorevent: true,
17549             /**
17550              * @event firstfocus
17551              * Fires when on first focus - needed by toolbars..
17552              * @param {HtmlEditor} this
17553              */
17554             firstfocus: true,
17555             /**
17556              * @event autosave
17557              * Auto save the htmlEditor value as a file into Events
17558              * @param {HtmlEditor} this
17559              */
17560             autosave: true,
17561             /**
17562              * @event savedpreview
17563              * preview the saved version of htmlEditor
17564              * @param {HtmlEditor} this
17565              */
17566             savedpreview: true
17567         });
17568 };
17569
17570
17571 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17572     
17573     
17574       /**
17575      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17576      */
17577     toolbars : false,
17578    
17579      /**
17580      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17581      *                        Roo.resizable.
17582      */
17583     resizable : false,
17584      /**
17585      * @cfg {Number} height (in pixels)
17586      */   
17587     height: 300,
17588    /**
17589      * @cfg {Number} width (in pixels)
17590      */   
17591     width: false,
17592     
17593     /**
17594      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17595      * 
17596      */
17597     stylesheets: false,
17598     
17599     // id of frame..
17600     frameId: false,
17601     
17602     // private properties
17603     validationEvent : false,
17604     deferHeight: true,
17605     initialized : false,
17606     activated : false,
17607     
17608     onFocus : Roo.emptyFn,
17609     iframePad:3,
17610     hideMode:'offsets',
17611     
17612     
17613     tbContainer : false,
17614     
17615     toolbarContainer :function() {
17616         return this.wrap.select('.x-html-editor-tb',true).first();
17617     },
17618
17619     /**
17620      * Protected method that will not generally be called directly. It
17621      * is called when the editor creates its toolbar. Override this method if you need to
17622      * add custom toolbar buttons.
17623      * @param {HtmlEditor} editor
17624      */
17625     createToolbar : function(){
17626         
17627         Roo.log("create toolbars");
17628         
17629         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17630         this.toolbars[0].render(this.toolbarContainer());
17631         
17632         return;
17633         
17634 //        if (!editor.toolbars || !editor.toolbars.length) {
17635 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17636 //        }
17637 //        
17638 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17639 //            editor.toolbars[i] = Roo.factory(
17640 //                    typeof(editor.toolbars[i]) == 'string' ?
17641 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17642 //                Roo.bootstrap.HtmlEditor);
17643 //            editor.toolbars[i].init(editor);
17644 //        }
17645     },
17646
17647      
17648     // private
17649     onRender : function(ct, position)
17650     {
17651        // Roo.log("Call onRender: " + this.xtype);
17652         var _t = this;
17653         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17654       
17655         this.wrap = this.inputEl().wrap({
17656             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17657         });
17658         
17659         this.editorcore.onRender(ct, position);
17660          
17661         if (this.resizable) {
17662             this.resizeEl = new Roo.Resizable(this.wrap, {
17663                 pinned : true,
17664                 wrap: true,
17665                 dynamic : true,
17666                 minHeight : this.height,
17667                 height: this.height,
17668                 handles : this.resizable,
17669                 width: this.width,
17670                 listeners : {
17671                     resize : function(r, w, h) {
17672                         _t.onResize(w,h); // -something
17673                     }
17674                 }
17675             });
17676             
17677         }
17678         this.createToolbar(this);
17679        
17680         
17681         if(!this.width && this.resizable){
17682             this.setSize(this.wrap.getSize());
17683         }
17684         if (this.resizeEl) {
17685             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17686             // should trigger onReize..
17687         }
17688         
17689     },
17690
17691     // private
17692     onResize : function(w, h)
17693     {
17694         Roo.log('resize: ' +w + ',' + h );
17695         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17696         var ew = false;
17697         var eh = false;
17698         
17699         if(this.inputEl() ){
17700             if(typeof w == 'number'){
17701                 var aw = w - this.wrap.getFrameWidth('lr');
17702                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17703                 ew = aw;
17704             }
17705             if(typeof h == 'number'){
17706                  var tbh = -11;  // fixme it needs to tool bar size!
17707                 for (var i =0; i < this.toolbars.length;i++) {
17708                     // fixme - ask toolbars for heights?
17709                     tbh += this.toolbars[i].el.getHeight();
17710                     //if (this.toolbars[i].footer) {
17711                     //    tbh += this.toolbars[i].footer.el.getHeight();
17712                     //}
17713                 }
17714               
17715                 
17716                 
17717                 
17718                 
17719                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17720                 ah -= 5; // knock a few pixes off for look..
17721                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17722                 var eh = ah;
17723             }
17724         }
17725         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17726         this.editorcore.onResize(ew,eh);
17727         
17728     },
17729
17730     /**
17731      * Toggles the editor between standard and source edit mode.
17732      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17733      */
17734     toggleSourceEdit : function(sourceEditMode)
17735     {
17736         this.editorcore.toggleSourceEdit(sourceEditMode);
17737         
17738         if(this.editorcore.sourceEditMode){
17739             Roo.log('editor - showing textarea');
17740             
17741 //            Roo.log('in');
17742 //            Roo.log(this.syncValue());
17743             this.syncValue();
17744             this.inputEl().removeClass(['hide', 'x-hidden']);
17745             this.inputEl().dom.removeAttribute('tabIndex');
17746             this.inputEl().focus();
17747         }else{
17748             Roo.log('editor - hiding textarea');
17749 //            Roo.log('out')
17750 //            Roo.log(this.pushValue()); 
17751             this.pushValue();
17752             
17753             this.inputEl().addClass(['hide', 'x-hidden']);
17754             this.inputEl().dom.setAttribute('tabIndex', -1);
17755             //this.deferFocus();
17756         }
17757          
17758         if(this.resizable){
17759             this.setSize(this.wrap.getSize());
17760         }
17761         
17762         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17763     },
17764  
17765     // private (for BoxComponent)
17766     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17767
17768     // private (for BoxComponent)
17769     getResizeEl : function(){
17770         return this.wrap;
17771     },
17772
17773     // private (for BoxComponent)
17774     getPositionEl : function(){
17775         return this.wrap;
17776     },
17777
17778     // private
17779     initEvents : function(){
17780         this.originalValue = this.getValue();
17781     },
17782
17783 //    /**
17784 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17785 //     * @method
17786 //     */
17787 //    markInvalid : Roo.emptyFn,
17788 //    /**
17789 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17790 //     * @method
17791 //     */
17792 //    clearInvalid : Roo.emptyFn,
17793
17794     setValue : function(v){
17795         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17796         this.editorcore.pushValue();
17797     },
17798
17799      
17800     // private
17801     deferFocus : function(){
17802         this.focus.defer(10, this);
17803     },
17804
17805     // doc'ed in Field
17806     focus : function(){
17807         this.editorcore.focus();
17808         
17809     },
17810       
17811
17812     // private
17813     onDestroy : function(){
17814         
17815         
17816         
17817         if(this.rendered){
17818             
17819             for (var i =0; i < this.toolbars.length;i++) {
17820                 // fixme - ask toolbars for heights?
17821                 this.toolbars[i].onDestroy();
17822             }
17823             
17824             this.wrap.dom.innerHTML = '';
17825             this.wrap.remove();
17826         }
17827     },
17828
17829     // private
17830     onFirstFocus : function(){
17831         //Roo.log("onFirstFocus");
17832         this.editorcore.onFirstFocus();
17833          for (var i =0; i < this.toolbars.length;i++) {
17834             this.toolbars[i].onFirstFocus();
17835         }
17836         
17837     },
17838     
17839     // private
17840     syncValue : function()
17841     {   
17842         this.editorcore.syncValue();
17843     },
17844     
17845     pushValue : function()
17846     {   
17847         this.editorcore.pushValue();
17848     }
17849      
17850     
17851     // hide stuff that is not compatible
17852     /**
17853      * @event blur
17854      * @hide
17855      */
17856     /**
17857      * @event change
17858      * @hide
17859      */
17860     /**
17861      * @event focus
17862      * @hide
17863      */
17864     /**
17865      * @event specialkey
17866      * @hide
17867      */
17868     /**
17869      * @cfg {String} fieldClass @hide
17870      */
17871     /**
17872      * @cfg {String} focusClass @hide
17873      */
17874     /**
17875      * @cfg {String} autoCreate @hide
17876      */
17877     /**
17878      * @cfg {String} inputType @hide
17879      */
17880     /**
17881      * @cfg {String} invalidClass @hide
17882      */
17883     /**
17884      * @cfg {String} invalidText @hide
17885      */
17886     /**
17887      * @cfg {String} msgFx @hide
17888      */
17889     /**
17890      * @cfg {String} validateOnBlur @hide
17891      */
17892 });
17893  
17894     
17895    
17896    
17897    
17898       
17899 Roo.namespace('Roo.bootstrap.htmleditor');
17900 /**
17901  * @class Roo.bootstrap.HtmlEditorToolbar1
17902  * Basic Toolbar
17903  * 
17904  * Usage:
17905  *
17906  new Roo.bootstrap.HtmlEditor({
17907     ....
17908     toolbars : [
17909         new Roo.bootstrap.HtmlEditorToolbar1({
17910             disable : { fonts: 1 , format: 1, ..., ... , ...],
17911             btns : [ .... ]
17912         })
17913     }
17914      
17915  * 
17916  * @cfg {Object} disable List of elements to disable..
17917  * @cfg {Array} btns List of additional buttons.
17918  * 
17919  * 
17920  * NEEDS Extra CSS? 
17921  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
17922  */
17923  
17924 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
17925 {
17926     
17927     Roo.apply(this, config);
17928     
17929     // default disabled, based on 'good practice'..
17930     this.disable = this.disable || {};
17931     Roo.applyIf(this.disable, {
17932         fontSize : true,
17933         colors : true,
17934         specialElements : true
17935     });
17936     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
17937     
17938     this.editor = config.editor;
17939     this.editorcore = config.editor.editorcore;
17940     
17941     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
17942     
17943     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
17944     // dont call parent... till later.
17945 }
17946 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
17947      
17948     bar : true,
17949     
17950     editor : false,
17951     editorcore : false,
17952     
17953     
17954     formats : [
17955         "p" ,  
17956         "h1","h2","h3","h4","h5","h6", 
17957         "pre", "code", 
17958         "abbr", "acronym", "address", "cite", "samp", "var",
17959         'div','span'
17960     ],
17961     
17962     onRender : function(ct, position)
17963     {
17964        // Roo.log("Call onRender: " + this.xtype);
17965         
17966        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
17967        Roo.log(this.el);
17968        this.el.dom.style.marginBottom = '0';
17969        var _this = this;
17970        var editorcore = this.editorcore;
17971        var editor= this.editor;
17972        
17973        var children = [];
17974        var btn = function(id,cmd , toggle, handler){
17975        
17976             var  event = toggle ? 'toggle' : 'click';
17977        
17978             var a = {
17979                 size : 'sm',
17980                 xtype: 'Button',
17981                 xns: Roo.bootstrap,
17982                 glyphicon : id,
17983                 cmd : id || cmd,
17984                 enableToggle:toggle !== false,
17985                 //html : 'submit'
17986                 pressed : toggle ? false : null,
17987                 listeners : {}
17988             }
17989             a.listeners[toggle ? 'toggle' : 'click'] = function() {
17990                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
17991             }
17992             children.push(a);
17993             return a;
17994        }
17995         
17996         var style = {
17997                 xtype: 'Button',
17998                 size : 'sm',
17999                 xns: Roo.bootstrap,
18000                 glyphicon : 'font',
18001                 //html : 'submit'
18002                 menu : {
18003                     xtype: 'Menu',
18004                     xns: Roo.bootstrap,
18005                     items:  []
18006                 }
18007         };
18008         Roo.each(this.formats, function(f) {
18009             style.menu.items.push({
18010                 xtype :'MenuItem',
18011                 xns: Roo.bootstrap,
18012                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18013                 tagname : f,
18014                 listeners : {
18015                     click : function()
18016                     {
18017                         editorcore.insertTag(this.tagname);
18018                         editor.focus();
18019                     }
18020                 }
18021                 
18022             });
18023         });
18024          children.push(style);   
18025             
18026             
18027         btn('bold',false,true);
18028         btn('italic',false,true);
18029         btn('align-left', 'justifyleft',true);
18030         btn('align-center', 'justifycenter',true);
18031         btn('align-right' , 'justifyright',true);
18032         btn('link', false, false, function(btn) {
18033             //Roo.log("create link?");
18034             var url = prompt(this.createLinkText, this.defaultLinkValue);
18035             if(url && url != 'http:/'+'/'){
18036                 this.editorcore.relayCmd('createlink', url);
18037             }
18038         }),
18039         btn('list','insertunorderedlist',true);
18040         btn('pencil', false,true, function(btn){
18041                 Roo.log(this);
18042                 
18043                 this.toggleSourceEdit(btn.pressed);
18044         });
18045         /*
18046         var cog = {
18047                 xtype: 'Button',
18048                 size : 'sm',
18049                 xns: Roo.bootstrap,
18050                 glyphicon : 'cog',
18051                 //html : 'submit'
18052                 menu : {
18053                     xtype: 'Menu',
18054                     xns: Roo.bootstrap,
18055                     items:  []
18056                 }
18057         };
18058         
18059         cog.menu.items.push({
18060             xtype :'MenuItem',
18061             xns: Roo.bootstrap,
18062             html : Clean styles,
18063             tagname : f,
18064             listeners : {
18065                 click : function()
18066                 {
18067                     editorcore.insertTag(this.tagname);
18068                     editor.focus();
18069                 }
18070             }
18071             
18072         });
18073        */
18074         
18075          
18076        this.xtype = 'NavSimplebar';
18077         
18078         for(var i=0;i< children.length;i++) {
18079             
18080             this.buttons.add(this.addxtypeChild(children[i]));
18081             
18082         }
18083         
18084         editor.on('editorevent', this.updateToolbar, this);
18085     },
18086     onBtnClick : function(id)
18087     {
18088        this.editorcore.relayCmd(id);
18089        this.editorcore.focus();
18090     },
18091     
18092     /**
18093      * Protected method that will not generally be called directly. It triggers
18094      * a toolbar update by reading the markup state of the current selection in the editor.
18095      */
18096     updateToolbar: function(){
18097
18098         if(!this.editorcore.activated){
18099             this.editor.onFirstFocus(); // is this neeed?
18100             return;
18101         }
18102
18103         var btns = this.buttons; 
18104         var doc = this.editorcore.doc;
18105         btns.get('bold').setActive(doc.queryCommandState('bold'));
18106         btns.get('italic').setActive(doc.queryCommandState('italic'));
18107         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18108         
18109         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18110         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18111         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18112         
18113         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18114         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18115          /*
18116         
18117         var ans = this.editorcore.getAllAncestors();
18118         if (this.formatCombo) {
18119             
18120             
18121             var store = this.formatCombo.store;
18122             this.formatCombo.setValue("");
18123             for (var i =0; i < ans.length;i++) {
18124                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18125                     // select it..
18126                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18127                     break;
18128                 }
18129             }
18130         }
18131         
18132         
18133         
18134         // hides menus... - so this cant be on a menu...
18135         Roo.bootstrap.MenuMgr.hideAll();
18136         */
18137         Roo.bootstrap.MenuMgr.hideAll();
18138         //this.editorsyncValue();
18139     },
18140     onFirstFocus: function() {
18141         this.buttons.each(function(item){
18142            item.enable();
18143         });
18144     },
18145     toggleSourceEdit : function(sourceEditMode){
18146         
18147           
18148         if(sourceEditMode){
18149             Roo.log("disabling buttons");
18150            this.buttons.each( function(item){
18151                 if(item.cmd != 'pencil'){
18152                     item.disable();
18153                 }
18154             });
18155           
18156         }else{
18157             Roo.log("enabling buttons");
18158             if(this.editorcore.initialized){
18159                 this.buttons.each( function(item){
18160                     item.enable();
18161                 });
18162             }
18163             
18164         }
18165         Roo.log("calling toggole on editor");
18166         // tell the editor that it's been pressed..
18167         this.editor.toggleSourceEdit(sourceEditMode);
18168        
18169     }
18170 });
18171
18172
18173
18174
18175
18176 /**
18177  * @class Roo.bootstrap.Table.AbstractSelectionModel
18178  * @extends Roo.util.Observable
18179  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18180  * implemented by descendant classes.  This class should not be directly instantiated.
18181  * @constructor
18182  */
18183 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18184     this.locked = false;
18185     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18186 };
18187
18188
18189 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18190     /** @ignore Called by the grid automatically. Do not call directly. */
18191     init : function(grid){
18192         this.grid = grid;
18193         this.initEvents();
18194     },
18195
18196     /**
18197      * Locks the selections.
18198      */
18199     lock : function(){
18200         this.locked = true;
18201     },
18202
18203     /**
18204      * Unlocks the selections.
18205      */
18206     unlock : function(){
18207         this.locked = false;
18208     },
18209
18210     /**
18211      * Returns true if the selections are locked.
18212      * @return {Boolean}
18213      */
18214     isLocked : function(){
18215         return this.locked;
18216     }
18217 });
18218 /**
18219  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18220  * @class Roo.bootstrap.Table.RowSelectionModel
18221  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18222  * It supports multiple selections and keyboard selection/navigation. 
18223  * @constructor
18224  * @param {Object} config
18225  */
18226
18227 Roo.bootstrap.Table.RowSelectionModel = function(config){
18228     Roo.apply(this, config);
18229     this.selections = new Roo.util.MixedCollection(false, function(o){
18230         return o.id;
18231     });
18232
18233     this.last = false;
18234     this.lastActive = false;
18235
18236     this.addEvents({
18237         /**
18238              * @event selectionchange
18239              * Fires when the selection changes
18240              * @param {SelectionModel} this
18241              */
18242             "selectionchange" : true,
18243         /**
18244              * @event afterselectionchange
18245              * Fires after the selection changes (eg. by key press or clicking)
18246              * @param {SelectionModel} this
18247              */
18248             "afterselectionchange" : true,
18249         /**
18250              * @event beforerowselect
18251              * Fires when a row is selected being selected, return false to cancel.
18252              * @param {SelectionModel} this
18253              * @param {Number} rowIndex The selected index
18254              * @param {Boolean} keepExisting False if other selections will be cleared
18255              */
18256             "beforerowselect" : true,
18257         /**
18258              * @event rowselect
18259              * Fires when a row is selected.
18260              * @param {SelectionModel} this
18261              * @param {Number} rowIndex The selected index
18262              * @param {Roo.data.Record} r The record
18263              */
18264             "rowselect" : true,
18265         /**
18266              * @event rowdeselect
18267              * Fires when a row is deselected.
18268              * @param {SelectionModel} this
18269              * @param {Number} rowIndex The selected index
18270              */
18271         "rowdeselect" : true
18272     });
18273     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18274     this.locked = false;
18275 };
18276
18277 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18278     /**
18279      * @cfg {Boolean} singleSelect
18280      * True to allow selection of only one row at a time (defaults to false)
18281      */
18282     singleSelect : false,
18283
18284     // private
18285     initEvents : function(){
18286
18287         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18288             this.grid.on("mousedown", this.handleMouseDown, this);
18289         }else{ // allow click to work like normal
18290             this.grid.on("rowclick", this.handleDragableRowClick, this);
18291         }
18292
18293         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18294             "up" : function(e){
18295                 if(!e.shiftKey){
18296                     this.selectPrevious(e.shiftKey);
18297                 }else if(this.last !== false && this.lastActive !== false){
18298                     var last = this.last;
18299                     this.selectRange(this.last,  this.lastActive-1);
18300                     this.grid.getView().focusRow(this.lastActive);
18301                     if(last !== false){
18302                         this.last = last;
18303                     }
18304                 }else{
18305                     this.selectFirstRow();
18306                 }
18307                 this.fireEvent("afterselectionchange", this);
18308             },
18309             "down" : function(e){
18310                 if(!e.shiftKey){
18311                     this.selectNext(e.shiftKey);
18312                 }else if(this.last !== false && this.lastActive !== false){
18313                     var last = this.last;
18314                     this.selectRange(this.last,  this.lastActive+1);
18315                     this.grid.getView().focusRow(this.lastActive);
18316                     if(last !== false){
18317                         this.last = last;
18318                     }
18319                 }else{
18320                     this.selectFirstRow();
18321                 }
18322                 this.fireEvent("afterselectionchange", this);
18323             },
18324             scope: this
18325         });
18326
18327         var view = this.grid.view;
18328         view.on("refresh", this.onRefresh, this);
18329         view.on("rowupdated", this.onRowUpdated, this);
18330         view.on("rowremoved", this.onRemove, this);
18331     },
18332
18333     // private
18334     onRefresh : function(){
18335         var ds = this.grid.dataSource, i, v = this.grid.view;
18336         var s = this.selections;
18337         s.each(function(r){
18338             if((i = ds.indexOfId(r.id)) != -1){
18339                 v.onRowSelect(i);
18340             }else{
18341                 s.remove(r);
18342             }
18343         });
18344     },
18345
18346     // private
18347     onRemove : function(v, index, r){
18348         this.selections.remove(r);
18349     },
18350
18351     // private
18352     onRowUpdated : function(v, index, r){
18353         if(this.isSelected(r)){
18354             v.onRowSelect(index);
18355         }
18356     },
18357
18358     /**
18359      * Select records.
18360      * @param {Array} records The records to select
18361      * @param {Boolean} keepExisting (optional) True to keep existing selections
18362      */
18363     selectRecords : function(records, keepExisting){
18364         if(!keepExisting){
18365             this.clearSelections();
18366         }
18367         var ds = this.grid.dataSource;
18368         for(var i = 0, len = records.length; i < len; i++){
18369             this.selectRow(ds.indexOf(records[i]), true);
18370         }
18371     },
18372
18373     /**
18374      * Gets the number of selected rows.
18375      * @return {Number}
18376      */
18377     getCount : function(){
18378         return this.selections.length;
18379     },
18380
18381     /**
18382      * Selects the first row in the grid.
18383      */
18384     selectFirstRow : function(){
18385         this.selectRow(0);
18386     },
18387
18388     /**
18389      * Select the last row.
18390      * @param {Boolean} keepExisting (optional) True to keep existing selections
18391      */
18392     selectLastRow : function(keepExisting){
18393         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18394     },
18395
18396     /**
18397      * Selects the row immediately following the last selected row.
18398      * @param {Boolean} keepExisting (optional) True to keep existing selections
18399      */
18400     selectNext : function(keepExisting){
18401         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18402             this.selectRow(this.last+1, keepExisting);
18403             this.grid.getView().focusRow(this.last);
18404         }
18405     },
18406
18407     /**
18408      * Selects the row that precedes the last selected row.
18409      * @param {Boolean} keepExisting (optional) True to keep existing selections
18410      */
18411     selectPrevious : function(keepExisting){
18412         if(this.last){
18413             this.selectRow(this.last-1, keepExisting);
18414             this.grid.getView().focusRow(this.last);
18415         }
18416     },
18417
18418     /**
18419      * Returns the selected records
18420      * @return {Array} Array of selected records
18421      */
18422     getSelections : function(){
18423         return [].concat(this.selections.items);
18424     },
18425
18426     /**
18427      * Returns the first selected record.
18428      * @return {Record}
18429      */
18430     getSelected : function(){
18431         return this.selections.itemAt(0);
18432     },
18433
18434
18435     /**
18436      * Clears all selections.
18437      */
18438     clearSelections : function(fast){
18439         if(this.locked) return;
18440         if(fast !== true){
18441             var ds = this.grid.dataSource;
18442             var s = this.selections;
18443             s.each(function(r){
18444                 this.deselectRow(ds.indexOfId(r.id));
18445             }, this);
18446             s.clear();
18447         }else{
18448             this.selections.clear();
18449         }
18450         this.last = false;
18451     },
18452
18453
18454     /**
18455      * Selects all rows.
18456      */
18457     selectAll : function(){
18458         if(this.locked) return;
18459         this.selections.clear();
18460         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18461             this.selectRow(i, true);
18462         }
18463     },
18464
18465     /**
18466      * Returns True if there is a selection.
18467      * @return {Boolean}
18468      */
18469     hasSelection : function(){
18470         return this.selections.length > 0;
18471     },
18472
18473     /**
18474      * Returns True if the specified row is selected.
18475      * @param {Number/Record} record The record or index of the record to check
18476      * @return {Boolean}
18477      */
18478     isSelected : function(index){
18479         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18480         return (r && this.selections.key(r.id) ? true : false);
18481     },
18482
18483     /**
18484      * Returns True if the specified record id is selected.
18485      * @param {String} id The id of record to check
18486      * @return {Boolean}
18487      */
18488     isIdSelected : function(id){
18489         return (this.selections.key(id) ? true : false);
18490     },
18491
18492     // private
18493     handleMouseDown : function(e, t){
18494         var view = this.grid.getView(), rowIndex;
18495         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18496             return;
18497         };
18498         if(e.shiftKey && this.last !== false){
18499             var last = this.last;
18500             this.selectRange(last, rowIndex, e.ctrlKey);
18501             this.last = last; // reset the last
18502             view.focusRow(rowIndex);
18503         }else{
18504             var isSelected = this.isSelected(rowIndex);
18505             if(e.button !== 0 && isSelected){
18506                 view.focusRow(rowIndex);
18507             }else if(e.ctrlKey && isSelected){
18508                 this.deselectRow(rowIndex);
18509             }else if(!isSelected){
18510                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18511                 view.focusRow(rowIndex);
18512             }
18513         }
18514         this.fireEvent("afterselectionchange", this);
18515     },
18516     // private
18517     handleDragableRowClick :  function(grid, rowIndex, e) 
18518     {
18519         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18520             this.selectRow(rowIndex, false);
18521             grid.view.focusRow(rowIndex);
18522              this.fireEvent("afterselectionchange", this);
18523         }
18524     },
18525     
18526     /**
18527      * Selects multiple rows.
18528      * @param {Array} rows Array of the indexes of the row to select
18529      * @param {Boolean} keepExisting (optional) True to keep existing selections
18530      */
18531     selectRows : function(rows, keepExisting){
18532         if(!keepExisting){
18533             this.clearSelections();
18534         }
18535         for(var i = 0, len = rows.length; i < len; i++){
18536             this.selectRow(rows[i], true);
18537         }
18538     },
18539
18540     /**
18541      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18542      * @param {Number} startRow The index of the first row in the range
18543      * @param {Number} endRow The index of the last row in the range
18544      * @param {Boolean} keepExisting (optional) True to retain existing selections
18545      */
18546     selectRange : function(startRow, endRow, keepExisting){
18547         if(this.locked) return;
18548         if(!keepExisting){
18549             this.clearSelections();
18550         }
18551         if(startRow <= endRow){
18552             for(var i = startRow; i <= endRow; i++){
18553                 this.selectRow(i, true);
18554             }
18555         }else{
18556             for(var i = startRow; i >= endRow; i--){
18557                 this.selectRow(i, true);
18558             }
18559         }
18560     },
18561
18562     /**
18563      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18564      * @param {Number} startRow The index of the first row in the range
18565      * @param {Number} endRow The index of the last row in the range
18566      */
18567     deselectRange : function(startRow, endRow, preventViewNotify){
18568         if(this.locked) return;
18569         for(var i = startRow; i <= endRow; i++){
18570             this.deselectRow(i, preventViewNotify);
18571         }
18572     },
18573
18574     /**
18575      * Selects a row.
18576      * @param {Number} row The index of the row to select
18577      * @param {Boolean} keepExisting (optional) True to keep existing selections
18578      */
18579     selectRow : function(index, keepExisting, preventViewNotify){
18580         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18581         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18582             if(!keepExisting || this.singleSelect){
18583                 this.clearSelections();
18584             }
18585             var r = this.grid.dataSource.getAt(index);
18586             this.selections.add(r);
18587             this.last = this.lastActive = index;
18588             if(!preventViewNotify){
18589                 this.grid.getView().onRowSelect(index);
18590             }
18591             this.fireEvent("rowselect", this, index, r);
18592             this.fireEvent("selectionchange", this);
18593         }
18594     },
18595
18596     /**
18597      * Deselects a row.
18598      * @param {Number} row The index of the row to deselect
18599      */
18600     deselectRow : function(index, preventViewNotify){
18601         if(this.locked) return;
18602         if(this.last == index){
18603             this.last = false;
18604         }
18605         if(this.lastActive == index){
18606             this.lastActive = false;
18607         }
18608         var r = this.grid.dataSource.getAt(index);
18609         this.selections.remove(r);
18610         if(!preventViewNotify){
18611             this.grid.getView().onRowDeselect(index);
18612         }
18613         this.fireEvent("rowdeselect", this, index);
18614         this.fireEvent("selectionchange", this);
18615     },
18616
18617     // private
18618     restoreLast : function(){
18619         if(this._last){
18620             this.last = this._last;
18621         }
18622     },
18623
18624     // private
18625     acceptsNav : function(row, col, cm){
18626         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18627     },
18628
18629     // private
18630     onEditorKey : function(field, e){
18631         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18632         if(k == e.TAB){
18633             e.stopEvent();
18634             ed.completeEdit();
18635             if(e.shiftKey){
18636                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18637             }else{
18638                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18639             }
18640         }else if(k == e.ENTER && !e.ctrlKey){
18641             e.stopEvent();
18642             ed.completeEdit();
18643             if(e.shiftKey){
18644                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18645             }else{
18646                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18647             }
18648         }else if(k == e.ESC){
18649             ed.cancelEdit();
18650         }
18651         if(newCell){
18652             g.startEditing(newCell[0], newCell[1]);
18653         }
18654     }
18655 });/*
18656  * Based on:
18657  * Ext JS Library 1.1.1
18658  * Copyright(c) 2006-2007, Ext JS, LLC.
18659  *
18660  * Originally Released Under LGPL - original licence link has changed is not relivant.
18661  *
18662  * Fork - LGPL
18663  * <script type="text/javascript">
18664  */
18665  
18666 /**
18667  * @class Roo.bootstrap.PagingToolbar
18668  * @extends Roo.Row
18669  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18670  * @constructor
18671  * Create a new PagingToolbar
18672  * @param {Object} config The config object
18673  */
18674 Roo.bootstrap.PagingToolbar = function(config)
18675 {
18676     // old args format still supported... - xtype is prefered..
18677         // created from xtype...
18678     var ds = config.dataSource;
18679     this.toolbarItems = [];
18680     if (config.items) {
18681         this.toolbarItems = config.items;
18682 //        config.items = [];
18683     }
18684     
18685     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18686     this.ds = ds;
18687     this.cursor = 0;
18688     if (ds) { 
18689         this.bind(ds);
18690     }
18691     
18692     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18693     
18694 };
18695
18696 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18697     /**
18698      * @cfg {Roo.data.Store} dataSource
18699      * The underlying data store providing the paged data
18700      */
18701     /**
18702      * @cfg {String/HTMLElement/Element} container
18703      * container The id or element that will contain the toolbar
18704      */
18705     /**
18706      * @cfg {Boolean} displayInfo
18707      * True to display the displayMsg (defaults to false)
18708      */
18709     /**
18710      * @cfg {Number} pageSize
18711      * The number of records to display per page (defaults to 20)
18712      */
18713     pageSize: 20,
18714     /**
18715      * @cfg {String} displayMsg
18716      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18717      */
18718     displayMsg : 'Displaying {0} - {1} of {2}',
18719     /**
18720      * @cfg {String} emptyMsg
18721      * The message to display when no records are found (defaults to "No data to display")
18722      */
18723     emptyMsg : 'No data to display',
18724     /**
18725      * Customizable piece of the default paging text (defaults to "Page")
18726      * @type String
18727      */
18728     beforePageText : "Page",
18729     /**
18730      * Customizable piece of the default paging text (defaults to "of %0")
18731      * @type String
18732      */
18733     afterPageText : "of {0}",
18734     /**
18735      * Customizable piece of the default paging text (defaults to "First Page")
18736      * @type String
18737      */
18738     firstText : "First Page",
18739     /**
18740      * Customizable piece of the default paging text (defaults to "Previous Page")
18741      * @type String
18742      */
18743     prevText : "Previous Page",
18744     /**
18745      * Customizable piece of the default paging text (defaults to "Next Page")
18746      * @type String
18747      */
18748     nextText : "Next Page",
18749     /**
18750      * Customizable piece of the default paging text (defaults to "Last Page")
18751      * @type String
18752      */
18753     lastText : "Last Page",
18754     /**
18755      * Customizable piece of the default paging text (defaults to "Refresh")
18756      * @type String
18757      */
18758     refreshText : "Refresh",
18759
18760     buttons : false,
18761     // private
18762     onRender : function(ct, position) 
18763     {
18764         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18765         this.navgroup.parentId = this.id;
18766         this.navgroup.onRender(this.el, null);
18767         // add the buttons to the navgroup
18768         
18769         if(this.displayInfo){
18770             Roo.log(this.el.select('ul.navbar-nav',true).first());
18771             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18772             this.displayEl = this.el.select('.x-paging-info', true).first();
18773 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18774 //            this.displayEl = navel.el.select('span',true).first();
18775         }
18776         
18777         var _this = this;
18778         
18779         if(this.buttons){
18780             Roo.each(_this.buttons, function(e){
18781                Roo.factory(e).onRender(_this.el, null);
18782             });
18783         }
18784             
18785         Roo.each(_this.toolbarItems, function(e) {
18786             _this.navgroup.addItem(e);
18787         });
18788         
18789         this.first = this.navgroup.addItem({
18790             tooltip: this.firstText,
18791             cls: "prev",
18792             icon : 'fa fa-backward',
18793             disabled: true,
18794             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18795         });
18796         
18797         this.prev =  this.navgroup.addItem({
18798             tooltip: this.prevText,
18799             cls: "prev",
18800             icon : 'fa fa-step-backward',
18801             disabled: true,
18802             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18803         });
18804     //this.addSeparator();
18805         
18806         
18807         var field = this.navgroup.addItem( {
18808             tagtype : 'span',
18809             cls : 'x-paging-position',
18810             
18811             html : this.beforePageText  +
18812                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
18813                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
18814          } ); //?? escaped?
18815         
18816         this.field = field.el.select('input', true).first();
18817         this.field.on("keydown", this.onPagingKeydown, this);
18818         this.field.on("focus", function(){this.dom.select();});
18819     
18820     
18821         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
18822         //this.field.setHeight(18);
18823         //this.addSeparator();
18824         this.next = this.navgroup.addItem({
18825             tooltip: this.nextText,
18826             cls: "next",
18827             html : ' <i class="fa fa-step-forward">',
18828             disabled: true,
18829             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
18830         });
18831         this.last = this.navgroup.addItem({
18832             tooltip: this.lastText,
18833             icon : 'fa fa-forward',
18834             cls: "next",
18835             disabled: true,
18836             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
18837         });
18838     //this.addSeparator();
18839         this.loading = this.navgroup.addItem({
18840             tooltip: this.refreshText,
18841             icon: 'fa fa-refresh',
18842             
18843             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
18844         });
18845
18846     },
18847
18848     // private
18849     updateInfo : function(){
18850         if(this.displayEl){
18851             var count = this.ds.getCount();
18852             var msg = count == 0 ?
18853                 this.emptyMsg :
18854                 String.format(
18855                     this.displayMsg,
18856                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
18857                 );
18858             this.displayEl.update(msg);
18859         }
18860     },
18861
18862     // private
18863     onLoad : function(ds, r, o){
18864        this.cursor = o.params ? o.params.start : 0;
18865        var d = this.getPageData(),
18866             ap = d.activePage,
18867             ps = d.pages;
18868         
18869        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
18870        this.field.dom.value = ap;
18871        this.first.setDisabled(ap == 1);
18872        this.prev.setDisabled(ap == 1);
18873        this.next.setDisabled(ap == ps);
18874        this.last.setDisabled(ap == ps);
18875        this.loading.enable();
18876        this.updateInfo();
18877     },
18878
18879     // private
18880     getPageData : function(){
18881         var total = this.ds.getTotalCount();
18882         return {
18883             total : total,
18884             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
18885             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
18886         };
18887     },
18888
18889     // private
18890     onLoadError : function(){
18891         this.loading.enable();
18892     },
18893
18894     // private
18895     onPagingKeydown : function(e){
18896         var k = e.getKey();
18897         var d = this.getPageData();
18898         if(k == e.RETURN){
18899             var v = this.field.dom.value, pageNum;
18900             if(!v || isNaN(pageNum = parseInt(v, 10))){
18901                 this.field.dom.value = d.activePage;
18902                 return;
18903             }
18904             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
18905             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18906             e.stopEvent();
18907         }
18908         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))
18909         {
18910           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
18911           this.field.dom.value = pageNum;
18912           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
18913           e.stopEvent();
18914         }
18915         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18916         {
18917           var v = this.field.dom.value, pageNum; 
18918           var increment = (e.shiftKey) ? 10 : 1;
18919           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18920             increment *= -1;
18921           if(!v || isNaN(pageNum = parseInt(v, 10))) {
18922             this.field.dom.value = d.activePage;
18923             return;
18924           }
18925           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
18926           {
18927             this.field.dom.value = parseInt(v, 10) + increment;
18928             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
18929             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18930           }
18931           e.stopEvent();
18932         }
18933     },
18934
18935     // private
18936     beforeLoad : function(){
18937         if(this.loading){
18938             this.loading.disable();
18939         }
18940     },
18941
18942     // private
18943     onClick : function(which){
18944         var ds = this.ds;
18945         if (!ds) {
18946             return;
18947         }
18948         switch(which){
18949             case "first":
18950                 ds.load({params:{start: 0, limit: this.pageSize}});
18951             break;
18952             case "prev":
18953                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
18954             break;
18955             case "next":
18956                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
18957             break;
18958             case "last":
18959                 var total = ds.getTotalCount();
18960                 var extra = total % this.pageSize;
18961                 var lastStart = extra ? (total - extra) : total-this.pageSize;
18962                 ds.load({params:{start: lastStart, limit: this.pageSize}});
18963             break;
18964             case "refresh":
18965                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
18966             break;
18967         }
18968     },
18969
18970     /**
18971      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
18972      * @param {Roo.data.Store} store The data store to unbind
18973      */
18974     unbind : function(ds){
18975         ds.un("beforeload", this.beforeLoad, this);
18976         ds.un("load", this.onLoad, this);
18977         ds.un("loadexception", this.onLoadError, this);
18978         ds.un("remove", this.updateInfo, this);
18979         ds.un("add", this.updateInfo, this);
18980         this.ds = undefined;
18981     },
18982
18983     /**
18984      * Binds the paging toolbar to the specified {@link Roo.data.Store}
18985      * @param {Roo.data.Store} store The data store to bind
18986      */
18987     bind : function(ds){
18988         ds.on("beforeload", this.beforeLoad, this);
18989         ds.on("load", this.onLoad, this);
18990         ds.on("loadexception", this.onLoadError, this);
18991         ds.on("remove", this.updateInfo, this);
18992         ds.on("add", this.updateInfo, this);
18993         this.ds = ds;
18994     }
18995 });/*
18996  * - LGPL
18997  *
18998  * element
18999  * 
19000  */
19001
19002 /**
19003  * @class Roo.bootstrap.MessageBar
19004  * @extends Roo.bootstrap.Component
19005  * Bootstrap MessageBar class
19006  * @cfg {String} html contents of the MessageBar
19007  * @cfg {String} weight (info | success | warning | danger) default info
19008  * @cfg {String} beforeClass insert the bar before the given class
19009  * @cfg {Boolean} closable (true | false) default false
19010  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19011  * 
19012  * @constructor
19013  * Create a new Element
19014  * @param {Object} config The config object
19015  */
19016
19017 Roo.bootstrap.MessageBar = function(config){
19018     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19019 };
19020
19021 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19022     
19023     html: '',
19024     weight: 'info',
19025     closable: false,
19026     fixed: false,
19027     beforeClass: 'bootstrap-sticky-wrap',
19028     
19029     getAutoCreate : function(){
19030         
19031         var cfg = {
19032             tag: 'div',
19033             cls: 'alert alert-dismissable alert-' + this.weight,
19034             cn: [
19035                 {
19036                     tag: 'span',
19037                     cls: 'message',
19038                     html: this.html || ''
19039                 }
19040             ]
19041         }
19042         
19043         if(this.fixed){
19044             cfg.cls += ' alert-messages-fixed';
19045         }
19046         
19047         if(this.closable){
19048             cfg.cn.push({
19049                 tag: 'button',
19050                 cls: 'close',
19051                 html: 'x'
19052             });
19053         }
19054         
19055         return cfg;
19056     },
19057     
19058     onRender : function(ct, position)
19059     {
19060         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19061         
19062         if(!this.el){
19063             var cfg = Roo.apply({},  this.getAutoCreate());
19064             cfg.id = Roo.id();
19065             
19066             if (this.cls) {
19067                 cfg.cls += ' ' + this.cls;
19068             }
19069             if (this.style) {
19070                 cfg.style = this.style;
19071             }
19072             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19073             
19074             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19075         }
19076         
19077         this.el.select('>button.close').on('click', this.hide, this);
19078         
19079     },
19080     
19081     show : function()
19082     {
19083         if (!this.rendered) {
19084             this.render();
19085         }
19086         
19087         this.el.show();
19088         
19089         this.fireEvent('show', this);
19090         
19091     },
19092     
19093     hide : function()
19094     {
19095         if (!this.rendered) {
19096             this.render();
19097         }
19098         
19099         this.el.hide();
19100         
19101         this.fireEvent('hide', this);
19102     },
19103     
19104     update : function()
19105     {
19106 //        var e = this.el.dom.firstChild;
19107 //        
19108 //        if(this.closable){
19109 //            e = e.nextSibling;
19110 //        }
19111 //        
19112 //        e.data = this.html || '';
19113
19114         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19115     }
19116    
19117 });
19118
19119  
19120
19121      /*
19122  * - LGPL
19123  *
19124  * Graph
19125  * 
19126  */
19127
19128
19129 /**
19130  * @class Roo.bootstrap.Graph
19131  * @extends Roo.bootstrap.Component
19132  * Bootstrap Graph class
19133 > Prameters
19134  -sm {number} sm 4
19135  -md {number} md 5
19136  @cfg {String} graphtype  bar | vbar | pie
19137  @cfg {number} g_x coodinator | centre x (pie)
19138  @cfg {number} g_y coodinator | centre y (pie)
19139  @cfg {number} g_r radius (pie)
19140  @cfg {number} g_height height of the chart (respected by all elements in the set)
19141  @cfg {number} g_width width of the chart (respected by all elements in the set)
19142  @cfg {Object} title The title of the chart
19143     
19144  -{Array}  values
19145  -opts (object) options for the chart 
19146      o {
19147      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19148      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19149      o vgutter (number)
19150      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.
19151      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19152      o to
19153      o stretch (boolean)
19154      o }
19155  -opts (object) options for the pie
19156      o{
19157      o cut
19158      o startAngle (number)
19159      o endAngle (number)
19160      } 
19161  *
19162  * @constructor
19163  * Create a new Input
19164  * @param {Object} config The config object
19165  */
19166
19167 Roo.bootstrap.Graph = function(config){
19168     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19169     
19170     this.addEvents({
19171         // img events
19172         /**
19173          * @event click
19174          * The img click event for the img.
19175          * @param {Roo.EventObject} e
19176          */
19177         "click" : true
19178     });
19179 };
19180
19181 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19182     
19183     sm: 4,
19184     md: 5,
19185     graphtype: 'bar',
19186     g_height: 250,
19187     g_width: 400,
19188     g_x: 50,
19189     g_y: 50,
19190     g_r: 30,
19191     opts:{
19192         //g_colors: this.colors,
19193         g_type: 'soft',
19194         g_gutter: '20%'
19195
19196     },
19197     title : false,
19198
19199     getAutoCreate : function(){
19200         
19201         var cfg = {
19202             tag: 'div',
19203             html : null
19204         }
19205         
19206         
19207         return  cfg;
19208     },
19209
19210     onRender : function(ct,position){
19211         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19212         this.raphael = Raphael(this.el.dom);
19213         
19214                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19215                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19216                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19217                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19218                 /*
19219                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19220                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19221                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19222                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19223                 
19224                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19225                 r.barchart(330, 10, 300, 220, data1);
19226                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19227                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19228                 */
19229                 
19230                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19231                 // r.barchart(30, 30, 560, 250,  xdata, {
19232                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19233                 //     axis : "0 0 1 1",
19234                 //     axisxlabels :  xdata
19235                 //     //yvalues : cols,
19236                    
19237                 // });
19238 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19239 //        
19240 //        this.load(null,xdata,{
19241 //                axis : "0 0 1 1",
19242 //                axisxlabels :  xdata
19243 //                });
19244
19245     },
19246
19247     load : function(graphtype,xdata,opts){
19248         this.raphael.clear();
19249         if(!graphtype) {
19250             graphtype = this.graphtype;
19251         }
19252         if(!opts){
19253             opts = this.opts;
19254         }
19255         var r = this.raphael,
19256             fin = function () {
19257                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19258             },
19259             fout = function () {
19260                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19261             },
19262             pfin = function() {
19263                 this.sector.stop();
19264                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19265
19266                 if (this.label) {
19267                     this.label[0].stop();
19268                     this.label[0].attr({ r: 7.5 });
19269                     this.label[1].attr({ "font-weight": 800 });
19270                 }
19271             },
19272             pfout = function() {
19273                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19274
19275                 if (this.label) {
19276                     this.label[0].animate({ r: 5 }, 500, "bounce");
19277                     this.label[1].attr({ "font-weight": 400 });
19278                 }
19279             };
19280
19281         switch(graphtype){
19282             case 'bar':
19283                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19284                 break;
19285             case 'hbar':
19286                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19287                 break;
19288             case 'pie':
19289 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19290 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19291 //            
19292                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19293                 
19294                 break;
19295
19296         }
19297         
19298         if(this.title){
19299             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19300         }
19301         
19302     },
19303     
19304     setTitle: function(o)
19305     {
19306         this.title = o;
19307     },
19308     
19309     initEvents: function() {
19310         
19311         if(!this.href){
19312             this.el.on('click', this.onClick, this);
19313         }
19314     },
19315     
19316     onClick : function(e)
19317     {
19318         Roo.log('img onclick');
19319         this.fireEvent('click', this, e);
19320     }
19321    
19322 });
19323
19324  
19325 /*
19326  * - LGPL
19327  *
19328  * numberBox
19329  * 
19330  */
19331 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19332
19333 /**
19334  * @class Roo.bootstrap.dash.NumberBox
19335  * @extends Roo.bootstrap.Component
19336  * Bootstrap NumberBox class
19337  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19338  * @cfg {String} headline Box headline
19339  * @cfg {String} content Box content
19340  * @cfg {String} icon Box icon
19341  * @cfg {String} footer Footer text
19342  * @cfg {String} fhref Footer href
19343  * 
19344  * @constructor
19345  * Create a new NumberBox
19346  * @param {Object} config The config object
19347  */
19348
19349
19350 Roo.bootstrap.dash.NumberBox = function(config){
19351     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19352     
19353 };
19354
19355 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19356     
19357     bgcolor : 'aqua',
19358     headline : '',
19359     content : '',
19360     icon : '',
19361     footer : '',
19362     fhref : '',
19363     ficon : '',
19364     
19365     getAutoCreate : function(){
19366         
19367         var cfg = {
19368             tag : 'div',
19369             cls : 'small-box bg-' + this.bgcolor,
19370             cn : [
19371                 {
19372                     tag : 'div',
19373                     cls : 'inner',
19374                     cn :[
19375                         {
19376                             tag : 'h3',
19377                             cls : 'roo-headline',
19378                             html : this.headline
19379                         },
19380                         {
19381                             tag : 'p',
19382                             cls : 'roo-content',
19383                             html : this.content
19384                         }
19385                     ]
19386                 }
19387             ]
19388         }
19389         
19390         if(this.icon){
19391             cfg.cn.push({
19392                 tag : 'div',
19393                 cls : 'icon',
19394                 cn :[
19395                     {
19396                         tag : 'i',
19397                         cls : 'ion ' + this.icon
19398                     }
19399                 ]
19400             });
19401         }
19402         
19403         if(this.footer){
19404             var footer = {
19405                 tag : 'a',
19406                 cls : 'small-box-footer',
19407                 href : this.fhref || '#',
19408                 html : this.footer
19409             };
19410             
19411             cfg.cn.push(footer);
19412             
19413         }
19414         
19415         return  cfg;
19416     },
19417
19418     onRender : function(ct,position){
19419         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19420
19421
19422        
19423                 
19424     },
19425
19426     setHeadline: function (value)
19427     {
19428         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19429     },
19430     
19431     setFooter: function (value, href)
19432     {
19433         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19434         
19435         if(href){
19436             this.el.select('a.small-box-footer',true).first().attr('href', href);
19437         }
19438         
19439     },
19440
19441     setContent: function (value)
19442     {
19443         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19444     },
19445
19446     initEvents: function() 
19447     {   
19448         
19449     }
19450     
19451 });
19452
19453  
19454 /*
19455  * - LGPL
19456  *
19457  * TabBox
19458  * 
19459  */
19460 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19461
19462 /**
19463  * @class Roo.bootstrap.dash.TabBox
19464  * @extends Roo.bootstrap.Component
19465  * Bootstrap TabBox class
19466  * @cfg {String} title Title of the TabBox
19467  * @cfg {String} icon Icon of the TabBox
19468  * 
19469  * @constructor
19470  * Create a new TabBox
19471  * @param {Object} config The config object
19472  */
19473
19474
19475 Roo.bootstrap.dash.TabBox = function(config){
19476     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19477     this.addEvents({
19478         // raw events
19479         /**
19480          * @event addpane
19481          * When a pane is added
19482          * @param {Roo.bootstrap.dash.TabPane} pane
19483          */
19484         "addpane" : true
19485          
19486     });
19487 };
19488
19489 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19490
19491     title : '',
19492     icon : false,
19493     
19494     getChildContainer : function()
19495     {
19496         return this.el.select('.tab-content', true).first();
19497     },
19498     
19499     getAutoCreate : function(){
19500         
19501         var header = {
19502             tag: 'li',
19503             cls: 'pull-left header',
19504             html: this.title,
19505             cn : []
19506         };
19507         
19508         if(this.icon){
19509             header.cn.push({
19510                 tag: 'i',
19511                 cls: 'fa ' + this.icon
19512             });
19513         }
19514         
19515         
19516         var cfg = {
19517             tag: 'div',
19518             cls: 'nav-tabs-custom',
19519             cn: [
19520                 {
19521                     tag: 'ul',
19522                     cls: 'nav nav-tabs pull-right',
19523                     cn: [
19524                         header
19525                     ]
19526                 },
19527                 {
19528                     tag: 'div',
19529                     cls: 'tab-content no-padding',
19530                     cn: []
19531                 }
19532             ]
19533         }
19534
19535         return  cfg;
19536     },
19537     initEvents : function()
19538     {
19539         //Roo.log('add add pane handler');
19540         this.on('addpane', this.onAddPane, this);
19541     },
19542      /**
19543      * Updates the box title
19544      * @param {String} html to set the title to.
19545      */
19546     setTitle : function(value)
19547     {
19548         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19549     },
19550     onAddPane : function(pane)
19551     {
19552         //Roo.log('addpane');
19553         //Roo.log(pane);
19554         // tabs are rendere left to right..
19555         var ctr = this.el.select('.nav-tabs', true).first();
19556          
19557          
19558         var existing = ctr.select('.nav-tab',true);
19559         var qty = existing.getCount();;
19560         
19561         
19562         var tab = ctr.createChild({
19563             tag : 'li',
19564             cls : 'nav-tab' + (qty ? '' : ' active'),
19565             cn : [
19566                 {
19567                     tag : 'a',
19568                     href:'#',
19569                     html : pane.title
19570                 }
19571             ]
19572         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19573         pane.tab = tab;
19574         
19575         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19576         if (!qty) {
19577             pane.el.addClass('active');
19578         }
19579         
19580                 
19581     },
19582     onTabClick : function(ev,un,ob,pane)
19583     {
19584         //Roo.log('tab - prev default');
19585         ev.preventDefault();
19586         
19587         
19588         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19589         pane.tab.addClass('active');
19590         //Roo.log(pane.title);
19591         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19592         // technically we should have a deactivate event.. but maybe add later.
19593         // and it should not de-activate the selected tab...
19594         
19595         pane.el.addClass('active');
19596         pane.fireEvent('activate');
19597         
19598         
19599     }
19600     
19601     
19602 });
19603
19604  
19605 /*
19606  * - LGPL
19607  *
19608  * Tab pane
19609  * 
19610  */
19611 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19612 /**
19613  * @class Roo.bootstrap.TabPane
19614  * @extends Roo.bootstrap.Component
19615  * Bootstrap TabPane class
19616  * @cfg {Boolean} active (false | true) Default false
19617  * @cfg {String} title title of panel
19618
19619  * 
19620  * @constructor
19621  * Create a new TabPane
19622  * @param {Object} config The config object
19623  */
19624
19625 Roo.bootstrap.dash.TabPane = function(config){
19626     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19627     
19628 };
19629
19630 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19631     
19632     active : false,
19633     title : '',
19634     
19635     // the tabBox that this is attached to.
19636     tab : false,
19637      
19638     getAutoCreate : function() 
19639     {
19640         var cfg = {
19641             tag: 'div',
19642             cls: 'tab-pane'
19643         }
19644         
19645         if(this.active){
19646             cfg.cls += ' active';
19647         }
19648         
19649         return cfg;
19650     },
19651     initEvents  : function()
19652     {
19653         //Roo.log('trigger add pane handler');
19654         this.parent().fireEvent('addpane', this)
19655     },
19656     
19657      /**
19658      * Updates the tab title 
19659      * @param {String} html to set the title to.
19660      */
19661     setTitle: function(str)
19662     {
19663         if (!this.tab) {
19664             return;
19665         }
19666         this.title = str;
19667         this.tab.select('a'.true).first().dom.innerHTML = str;
19668         
19669     }
19670     
19671     
19672     
19673 });
19674
19675  
19676
19677
19678  /*
19679  * - LGPL
19680  *
19681  * menu
19682  * 
19683  */
19684 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19685
19686 /**
19687  * @class Roo.bootstrap.menu.Menu
19688  * @extends Roo.bootstrap.Component
19689  * Bootstrap Menu class - container for Menu
19690  * @cfg {String} html Text of the menu
19691  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19692  * @cfg {String} icon Font awesome icon
19693  * @cfg {String} pos Menu align to (top | bottom) default bottom
19694  * 
19695  * 
19696  * @constructor
19697  * Create a new Menu
19698  * @param {Object} config The config object
19699  */
19700
19701
19702 Roo.bootstrap.menu.Menu = function(config){
19703     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19704     
19705     this.addEvents({
19706         /**
19707          * @event beforeshow
19708          * Fires before this menu is displayed
19709          * @param {Roo.bootstrap.menu.Menu} this
19710          */
19711         beforeshow : true,
19712         /**
19713          * @event beforehide
19714          * Fires before this menu is hidden
19715          * @param {Roo.bootstrap.menu.Menu} this
19716          */
19717         beforehide : true,
19718         /**
19719          * @event show
19720          * Fires after this menu is displayed
19721          * @param {Roo.bootstrap.menu.Menu} this
19722          */
19723         show : true,
19724         /**
19725          * @event hide
19726          * Fires after this menu is hidden
19727          * @param {Roo.bootstrap.menu.Menu} this
19728          */
19729         hide : true,
19730         /**
19731          * @event click
19732          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19733          * @param {Roo.bootstrap.menu.Menu} this
19734          * @param {Roo.EventObject} e
19735          */
19736         click : true
19737     });
19738     
19739 };
19740
19741 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19742     
19743     submenu : false,
19744     html : '',
19745     weight : 'default',
19746     icon : false,
19747     pos : 'bottom',
19748     
19749     
19750     getChildContainer : function() {
19751         if(this.isSubMenu){
19752             return this.el;
19753         }
19754         
19755         return this.el.select('ul.dropdown-menu', true).first();  
19756     },
19757     
19758     getAutoCreate : function()
19759     {
19760         var text = [
19761             {
19762                 tag : 'span',
19763                 cls : 'roo-menu-text',
19764                 html : this.html
19765             }
19766         ];
19767         
19768         if(this.icon){
19769             text.unshift({
19770                 tag : 'i',
19771                 cls : 'fa ' + this.icon
19772             })
19773         }
19774         
19775         
19776         var cfg = {
19777             tag : 'div',
19778             cls : 'btn-group',
19779             cn : [
19780                 {
19781                     tag : 'button',
19782                     cls : 'dropdown-button btn btn-' + this.weight,
19783                     cn : text
19784                 },
19785                 {
19786                     tag : 'button',
19787                     cls : 'dropdown-toggle btn btn-' + this.weight,
19788                     cn : [
19789                         {
19790                             tag : 'span',
19791                             cls : 'caret'
19792                         }
19793                     ]
19794                 },
19795                 {
19796                     tag : 'ul',
19797                     cls : 'dropdown-menu'
19798                 }
19799             ]
19800             
19801         };
19802         
19803         if(this.pos == 'top'){
19804             cfg.cls += ' dropup';
19805         }
19806         
19807         if(this.isSubMenu){
19808             cfg = {
19809                 tag : 'ul',
19810                 cls : 'dropdown-menu'
19811             }
19812         }
19813         
19814         return cfg;
19815     },
19816     
19817     onRender : function(ct, position)
19818     {
19819         this.isSubMenu = ct.hasClass('dropdown-submenu');
19820         
19821         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
19822     },
19823     
19824     initEvents : function() 
19825     {
19826         if(this.isSubMenu){
19827             return;
19828         }
19829         
19830         this.hidden = true;
19831         
19832         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
19833         this.triggerEl.on('click', this.onTriggerPress, this);
19834         
19835         this.buttonEl = this.el.select('button.dropdown-button', true).first();
19836         this.buttonEl.on('click', this.onClick, this);
19837         
19838     },
19839     
19840     list : function()
19841     {
19842         if(this.isSubMenu){
19843             return this.el;
19844         }
19845         
19846         return this.el.select('ul.dropdown-menu', true).first();
19847     },
19848     
19849     onClick : function(e)
19850     {
19851         this.fireEvent("click", this, e);
19852     },
19853     
19854     onTriggerPress  : function(e)
19855     {   
19856         if (this.isVisible()) {
19857             this.hide();
19858         } else {
19859             this.show();
19860         }
19861     },
19862     
19863     isVisible : function(){
19864         return !this.hidden;
19865     },
19866     
19867     show : function()
19868     {
19869         this.fireEvent("beforeshow", this);
19870         
19871         this.hidden = false;
19872         this.el.addClass('open');
19873         
19874         Roo.get(document).on("mouseup", this.onMouseUp, this);
19875         
19876         this.fireEvent("show", this);
19877         
19878         
19879     },
19880     
19881     hide : function()
19882     {
19883         this.fireEvent("beforehide", this);
19884         
19885         this.hidden = true;
19886         this.el.removeClass('open');
19887         
19888         Roo.get(document).un("mouseup", this.onMouseUp);
19889         
19890         this.fireEvent("hide", this);
19891     },
19892     
19893     onMouseUp : function()
19894     {
19895         this.hide();
19896     }
19897     
19898 });
19899
19900  
19901  /*
19902  * - LGPL
19903  *
19904  * menu item
19905  * 
19906  */
19907 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19908
19909 /**
19910  * @class Roo.bootstrap.menu.Item
19911  * @extends Roo.bootstrap.Component
19912  * Bootstrap MenuItem class
19913  * @cfg {Boolean} submenu (true | false) default false
19914  * @cfg {String} html text of the item
19915  * @cfg {String} href the link
19916  * @cfg {Boolean} disable (true | false) default false
19917  * @cfg {Boolean} preventDefault (true | false) default true
19918  * @cfg {String} icon Font awesome icon
19919  * @cfg {String} pos Submenu align to (left | right) default right 
19920  * 
19921  * 
19922  * @constructor
19923  * Create a new Item
19924  * @param {Object} config The config object
19925  */
19926
19927
19928 Roo.bootstrap.menu.Item = function(config){
19929     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
19930     this.addEvents({
19931         /**
19932          * @event mouseover
19933          * Fires when the mouse is hovering over this menu
19934          * @param {Roo.bootstrap.menu.Item} this
19935          * @param {Roo.EventObject} e
19936          */
19937         mouseover : true,
19938         /**
19939          * @event mouseout
19940          * Fires when the mouse exits this menu
19941          * @param {Roo.bootstrap.menu.Item} this
19942          * @param {Roo.EventObject} e
19943          */
19944         mouseout : true,
19945         // raw events
19946         /**
19947          * @event click
19948          * The raw click event for the entire grid.
19949          * @param {Roo.EventObject} e
19950          */
19951         click : true
19952     });
19953 };
19954
19955 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
19956     
19957     submenu : false,
19958     href : '',
19959     html : '',
19960     preventDefault: true,
19961     disable : false,
19962     icon : false,
19963     pos : 'right',
19964     
19965     getAutoCreate : function()
19966     {
19967         var text = [
19968             {
19969                 tag : 'span',
19970                 cls : 'roo-menu-item-text',
19971                 html : this.html
19972             }
19973         ];
19974         
19975         if(this.icon){
19976             text.unshift({
19977                 tag : 'i',
19978                 cls : 'fa ' + this.icon
19979             })
19980         }
19981         
19982         var cfg = {
19983             tag : 'li',
19984             cn : [
19985                 {
19986                     tag : 'a',
19987                     href : this.href || '#',
19988                     cn : text
19989                 }
19990             ]
19991         };
19992         
19993         if(this.disable){
19994             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
19995         }
19996         
19997         if(this.submenu){
19998             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
19999             
20000             if(this.pos == 'left'){
20001                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20002             }
20003         }
20004         
20005         return cfg;
20006     },
20007     
20008     initEvents : function() 
20009     {
20010         this.el.on('mouseover', this.onMouseOver, this);
20011         this.el.on('mouseout', this.onMouseOut, this);
20012         
20013         this.el.select('a', true).first().on('click', this.onClick, this);
20014         
20015     },
20016     
20017     onClick : function(e)
20018     {
20019         if(this.preventDefault){
20020             e.preventDefault();
20021         }
20022         
20023         this.fireEvent("click", this, e);
20024     },
20025     
20026     onMouseOver : function(e)
20027     {
20028         if(this.submenu && this.pos == 'left'){
20029             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20030         }
20031         
20032         this.fireEvent("mouseover", this, e);
20033     },
20034     
20035     onMouseOut : function(e)
20036     {
20037         this.fireEvent("mouseout", this, e);
20038     }
20039 });
20040
20041  
20042
20043  /*
20044  * - LGPL
20045  *
20046  * menu separator
20047  * 
20048  */
20049 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20050
20051 /**
20052  * @class Roo.bootstrap.menu.Separator
20053  * @extends Roo.bootstrap.Component
20054  * Bootstrap Separator class
20055  * 
20056  * @constructor
20057  * Create a new Separator
20058  * @param {Object} config The config object
20059  */
20060
20061
20062 Roo.bootstrap.menu.Separator = function(config){
20063     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20064 };
20065
20066 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20067     
20068     getAutoCreate : function(){
20069         var cfg = {
20070             tag : 'li',
20071             cls: 'divider'
20072         };
20073         
20074         return cfg;
20075     }
20076    
20077 });
20078
20079  
20080
20081