fonts/fontawesome-webfont.woff
[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 });
962
963  /*
964  * - LGPL
965  *
966  * image
967  * 
968  */
969
970
971 /**
972  * @class Roo.bootstrap.Img
973  * @extends Roo.bootstrap.Component
974  * Bootstrap Img class
975  * @cfg {Boolean} imgResponsive false | true
976  * @cfg {String} border rounded | circle | thumbnail
977  * @cfg {String} src image source
978  * @cfg {String} alt image alternative text
979  * @cfg {String} href a tag href
980  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
981  * 
982  * @constructor
983  * Create a new Input
984  * @param {Object} config The config object
985  */
986
987 Roo.bootstrap.Img = function(config){
988     Roo.bootstrap.Img.superclass.constructor.call(this, config);
989     
990     this.addEvents({
991         // img events
992         /**
993          * @event click
994          * The img click event for the img.
995          * @param {Roo.EventObject} e
996          */
997         "click" : true
998     });
999 };
1000
1001 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1002     
1003     imgResponsive: true,
1004     border: '',
1005     src: '',
1006     href: false,
1007     target: false,
1008
1009     getAutoCreate : function(){
1010         
1011         var cfg = {
1012             tag: 'img',
1013             cls: (this.imgResponsive) ? 'img-responsive' : '',
1014             html : null
1015         }
1016         
1017         cfg.html = this.html || cfg.html;
1018         
1019         cfg.src = this.src || cfg.src;
1020         
1021         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1022             cfg.cls += ' img-' + this.border;
1023         }
1024         
1025         if(this.alt){
1026             cfg.alt = this.alt;
1027         }
1028         
1029         if(this.href){
1030             var a = {
1031                 tag: 'a',
1032                 href: this.href,
1033                 cn: [
1034                     cfg
1035                 ]
1036             }
1037             
1038             if(this.target){
1039                 a.target = this.target;
1040             }
1041             
1042         }
1043         
1044         
1045         return (this.href) ? a : cfg;
1046     },
1047     
1048     initEvents: function() {
1049         
1050         if(!this.href){
1051             this.el.on('click', this.onClick, this);
1052         }
1053     },
1054     
1055     onClick : function(e)
1056     {
1057         Roo.log('img onclick');
1058         this.fireEvent('click', this, e);
1059     }
1060    
1061 });
1062
1063  /*
1064  * - LGPL
1065  *
1066  * image
1067  * 
1068  */
1069
1070
1071 /**
1072  * @class Roo.bootstrap.Link
1073  * @extends Roo.bootstrap.Component
1074  * Bootstrap Link Class
1075  * @cfg {String} alt image alternative text
1076  * @cfg {String} href a tag href
1077  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1078  * @cfg {String} html the content of the link.
1079
1080  * 
1081  * @constructor
1082  * Create a new Input
1083  * @param {Object} config The config object
1084  */
1085
1086 Roo.bootstrap.Link = function(config){
1087     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1088     
1089     this.addEvents({
1090         // img events
1091         /**
1092          * @event click
1093          * The img click event for the img.
1094          * @param {Roo.EventObject} e
1095          */
1096         "click" : true
1097     });
1098 };
1099
1100 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1101     
1102     href: false,
1103     target: false,
1104
1105     getAutoCreate : function(){
1106         
1107         var cfg = {
1108             tag: 'a',
1109             html : this.html || 'html-missing'
1110         }
1111         
1112         
1113         if(this.alt){
1114             cfg.alt = this.alt;
1115         }
1116         cfg.href = this.href || '#';
1117         if(this.target){
1118             cfg.target = this.target;
1119         }
1120         
1121         return cfg;
1122     },
1123     
1124     initEvents: function() {
1125         
1126         if(!this.href){
1127             this.el.on('click', this.onClick, this);
1128         }
1129     },
1130     
1131     onClick : function(e)
1132     {
1133         //Roo.log('img onclick');
1134         this.fireEvent('click', this, e);
1135     }
1136    
1137 });
1138
1139  /*
1140  * - LGPL
1141  *
1142  * header
1143  * 
1144  */
1145
1146 /**
1147  * @class Roo.bootstrap.Header
1148  * @extends Roo.bootstrap.Component
1149  * Bootstrap Header class
1150  * @cfg {String} html content of header
1151  * @cfg {Number} level (1|2|3|4|5|6) default 1
1152  * 
1153  * @constructor
1154  * Create a new Header
1155  * @param {Object} config The config object
1156  */
1157
1158
1159 Roo.bootstrap.Header  = function(config){
1160     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1161 };
1162
1163 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1164     
1165     //href : false,
1166     html : false,
1167     level : 1,
1168     
1169     
1170     
1171     getAutoCreate : function(){
1172         
1173         var cfg = {
1174             tag: 'h' + (1 *this.level),
1175             html: this.html || 'fill in html'
1176         } ;
1177         
1178         return cfg;
1179     }
1180    
1181 });
1182
1183  
1184
1185  /*
1186  * Based on:
1187  * Ext JS Library 1.1.1
1188  * Copyright(c) 2006-2007, Ext JS, LLC.
1189  *
1190  * Originally Released Under LGPL - original licence link has changed is not relivant.
1191  *
1192  * Fork - LGPL
1193  * <script type="text/javascript">
1194  */
1195  
1196 /**
1197  * @class Roo.bootstrap.MenuMgr
1198  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1199  * @singleton
1200  */
1201 Roo.bootstrap.MenuMgr = function(){
1202    var menus, active, groups = {}, attached = false, lastShow = new Date();
1203
1204    // private - called when first menu is created
1205    function init(){
1206        menus = {};
1207        active = new Roo.util.MixedCollection();
1208        Roo.get(document).addKeyListener(27, function(){
1209            if(active.length > 0){
1210                hideAll();
1211            }
1212        });
1213    }
1214
1215    // private
1216    function hideAll(){
1217        if(active && active.length > 0){
1218            var c = active.clone();
1219            c.each(function(m){
1220                m.hide();
1221            });
1222        }
1223    }
1224
1225    // private
1226    function onHide(m){
1227        active.remove(m);
1228        if(active.length < 1){
1229            Roo.get(document).un("mouseup", onMouseDown);
1230             
1231            attached = false;
1232        }
1233    }
1234
1235    // private
1236    function onShow(m){
1237        var last = active.last();
1238        lastShow = new Date();
1239        active.add(m);
1240        if(!attached){
1241           Roo.get(document).on("mouseup", onMouseDown);
1242            
1243            attached = true;
1244        }
1245        if(m.parentMenu){
1246           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1247           m.parentMenu.activeChild = m;
1248        }else if(last && last.isVisible()){
1249           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1250        }
1251    }
1252
1253    // private
1254    function onBeforeHide(m){
1255        if(m.activeChild){
1256            m.activeChild.hide();
1257        }
1258        if(m.autoHideTimer){
1259            clearTimeout(m.autoHideTimer);
1260            delete m.autoHideTimer;
1261        }
1262    }
1263
1264    // private
1265    function onBeforeShow(m){
1266        var pm = m.parentMenu;
1267        if(!pm && !m.allowOtherMenus){
1268            hideAll();
1269        }else if(pm && pm.activeChild && active != m){
1270            pm.activeChild.hide();
1271        }
1272    }
1273
1274    // private
1275    function onMouseDown(e){
1276         Roo.log("on MouseDown");
1277         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1278            hideAll();
1279         }
1280         
1281         
1282    }
1283
1284    // private
1285    function onBeforeCheck(mi, state){
1286        if(state){
1287            var g = groups[mi.group];
1288            for(var i = 0, l = g.length; i < l; i++){
1289                if(g[i] != mi){
1290                    g[i].setChecked(false);
1291                }
1292            }
1293        }
1294    }
1295
1296    return {
1297
1298        /**
1299         * Hides all menus that are currently visible
1300         */
1301        hideAll : function(){
1302             hideAll();  
1303        },
1304
1305        // private
1306        register : function(menu){
1307            if(!menus){
1308                init();
1309            }
1310            menus[menu.id] = menu;
1311            menu.on("beforehide", onBeforeHide);
1312            menu.on("hide", onHide);
1313            menu.on("beforeshow", onBeforeShow);
1314            menu.on("show", onShow);
1315            var g = menu.group;
1316            if(g && menu.events["checkchange"]){
1317                if(!groups[g]){
1318                    groups[g] = [];
1319                }
1320                groups[g].push(menu);
1321                menu.on("checkchange", onCheck);
1322            }
1323        },
1324
1325         /**
1326          * Returns a {@link Roo.menu.Menu} object
1327          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1328          * be used to generate and return a new Menu instance.
1329          */
1330        get : function(menu){
1331            if(typeof menu == "string"){ // menu id
1332                return menus[menu];
1333            }else if(menu.events){  // menu instance
1334                return menu;
1335            }
1336            /*else if(typeof menu.length == 'number'){ // array of menu items?
1337                return new Roo.bootstrap.Menu({items:menu});
1338            }else{ // otherwise, must be a config
1339                return new Roo.bootstrap.Menu(menu);
1340            }
1341            */
1342            return false;
1343        },
1344
1345        // private
1346        unregister : function(menu){
1347            delete menus[menu.id];
1348            menu.un("beforehide", onBeforeHide);
1349            menu.un("hide", onHide);
1350            menu.un("beforeshow", onBeforeShow);
1351            menu.un("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                groups[g].remove(menu);
1355                menu.un("checkchange", onCheck);
1356            }
1357        },
1358
1359        // private
1360        registerCheckable : function(menuItem){
1361            var g = menuItem.group;
1362            if(g){
1363                if(!groups[g]){
1364                    groups[g] = [];
1365                }
1366                groups[g].push(menuItem);
1367                menuItem.on("beforecheckchange", onBeforeCheck);
1368            }
1369        },
1370
1371        // private
1372        unregisterCheckable : function(menuItem){
1373            var g = menuItem.group;
1374            if(g){
1375                groups[g].remove(menuItem);
1376                menuItem.un("beforecheckchange", onBeforeCheck);
1377            }
1378        }
1379    };
1380 }();/*
1381  * - LGPL
1382  *
1383  * menu
1384  * 
1385  */
1386
1387 /**
1388  * @class Roo.bootstrap.Menu
1389  * @extends Roo.bootstrap.Component
1390  * Bootstrap Menu class - container for MenuItems
1391  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1392  * 
1393  * @constructor
1394  * Create a new Menu
1395  * @param {Object} config The config object
1396  */
1397
1398
1399 Roo.bootstrap.Menu = function(config){
1400     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1401     if (this.registerMenu) {
1402         Roo.bootstrap.MenuMgr.register(this);
1403     }
1404     this.addEvents({
1405         /**
1406          * @event beforeshow
1407          * Fires before this menu is displayed
1408          * @param {Roo.menu.Menu} this
1409          */
1410         beforeshow : true,
1411         /**
1412          * @event beforehide
1413          * Fires before this menu is hidden
1414          * @param {Roo.menu.Menu} this
1415          */
1416         beforehide : true,
1417         /**
1418          * @event show
1419          * Fires after this menu is displayed
1420          * @param {Roo.menu.Menu} this
1421          */
1422         show : true,
1423         /**
1424          * @event hide
1425          * Fires after this menu is hidden
1426          * @param {Roo.menu.Menu} this
1427          */
1428         hide : true,
1429         /**
1430          * @event click
1431          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1432          * @param {Roo.menu.Menu} this
1433          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1434          * @param {Roo.EventObject} e
1435          */
1436         click : true,
1437         /**
1438          * @event mouseover
1439          * Fires when the mouse is hovering over this menu
1440          * @param {Roo.menu.Menu} this
1441          * @param {Roo.EventObject} e
1442          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1443          */
1444         mouseover : true,
1445         /**
1446          * @event mouseout
1447          * Fires when the mouse exits this menu
1448          * @param {Roo.menu.Menu} this
1449          * @param {Roo.EventObject} e
1450          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1451          */
1452         mouseout : true,
1453         /**
1454          * @event itemclick
1455          * Fires when a menu item contained in this menu is clicked
1456          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1457          * @param {Roo.EventObject} e
1458          */
1459         itemclick: true
1460     });
1461     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1462 };
1463
1464 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1465     
1466    /// html : false,
1467     //align : '',
1468     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1469     type: false,
1470     /**
1471      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1472      */
1473     registerMenu : true,
1474     
1475     menuItems :false, // stores the menu items..
1476     
1477     hidden:true,
1478     
1479     parentMenu : false,
1480     
1481     getChildContainer : function() {
1482         return this.el;  
1483     },
1484     
1485     getAutoCreate : function(){
1486          
1487         //if (['right'].indexOf(this.align)!==-1) {
1488         //    cfg.cn[1].cls += ' pull-right'
1489         //}
1490         
1491         
1492         var cfg = {
1493             tag : 'ul',
1494             cls : 'dropdown-menu' ,
1495             style : 'z-index:1000'
1496             
1497         }
1498         
1499         if (this.type === 'submenu') {
1500             cfg.cls = 'submenu active';
1501         }
1502         if (this.type === 'treeview') {
1503             cfg.cls = 'treeview-menu';
1504         }
1505         
1506         return cfg;
1507     },
1508     initEvents : function() {
1509         
1510        // Roo.log("ADD event");
1511        // Roo.log(this.triggerEl.dom);
1512         this.triggerEl.on('click', this.onTriggerPress, this);
1513         this.triggerEl.addClass('dropdown-toggle');
1514         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1515
1516         this.el.on("mouseover", this.onMouseOver, this);
1517         this.el.on("mouseout", this.onMouseOut, this);
1518         
1519         
1520     },
1521     findTargetItem : function(e){
1522         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1523         if(!t){
1524             return false;
1525         }
1526         //Roo.log(t);         Roo.log(t.id);
1527         if(t && t.id){
1528             //Roo.log(this.menuitems);
1529             return this.menuitems.get(t.id);
1530             
1531             //return this.items.get(t.menuItemId);
1532         }
1533         
1534         return false;
1535     },
1536     onClick : function(e){
1537         Roo.log("menu.onClick");
1538         var t = this.findTargetItem(e);
1539         if(!t){
1540             return;
1541         }
1542         Roo.log(e);
1543         /*
1544         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1545             if(t == this.activeItem && t.shouldDeactivate(e)){
1546                 this.activeItem.deactivate();
1547                 delete this.activeItem;
1548                 return;
1549             }
1550             if(t.canActivate){
1551                 this.setActiveItem(t, true);
1552             }
1553             return;
1554             
1555             
1556         }
1557         */
1558         Roo.log('pass click event');
1559         
1560         t.onClick(e);
1561         
1562         this.fireEvent("click", this, t, e);
1563         
1564         this.hide();
1565     },
1566      onMouseOver : function(e){
1567         var t  = this.findTargetItem(e);
1568         //Roo.log(t);
1569         //if(t){
1570         //    if(t.canActivate && !t.disabled){
1571         //        this.setActiveItem(t, true);
1572         //    }
1573         //}
1574         
1575         this.fireEvent("mouseover", this, e, t);
1576     },
1577     isVisible : function(){
1578         return !this.hidden;
1579     },
1580      onMouseOut : function(e){
1581         var t  = this.findTargetItem(e);
1582         
1583         //if(t ){
1584         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1585         //        this.activeItem.deactivate();
1586         //        delete this.activeItem;
1587         //    }
1588         //}
1589         this.fireEvent("mouseout", this, e, t);
1590     },
1591     
1592     
1593     /**
1594      * Displays this menu relative to another element
1595      * @param {String/HTMLElement/Roo.Element} element The element to align to
1596      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1597      * the element (defaults to this.defaultAlign)
1598      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1599      */
1600     show : function(el, pos, parentMenu){
1601         this.parentMenu = parentMenu;
1602         if(!this.el){
1603             this.render();
1604         }
1605         this.fireEvent("beforeshow", this);
1606         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1607     },
1608      /**
1609      * Displays this menu at a specific xy position
1610      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1611      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1612      */
1613     showAt : function(xy, parentMenu, /* private: */_e){
1614         this.parentMenu = parentMenu;
1615         if(!this.el){
1616             this.render();
1617         }
1618         if(_e !== false){
1619             this.fireEvent("beforeshow", this);
1620             
1621             //xy = this.el.adjustForConstraints(xy);
1622         }
1623         //this.el.setXY(xy);
1624         //this.el.show();
1625         this.hideMenuItems();
1626         this.hidden = false;
1627         this.triggerEl.addClass('open');
1628         this.focus();
1629         this.fireEvent("show", this);
1630     },
1631     
1632     focus : function(){
1633         return;
1634         if(!this.hidden){
1635             this.doFocus.defer(50, this);
1636         }
1637     },
1638
1639     doFocus : function(){
1640         if(!this.hidden){
1641             this.focusEl.focus();
1642         }
1643     },
1644
1645     /**
1646      * Hides this menu and optionally all parent menus
1647      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1648      */
1649     hide : function(deep){
1650         
1651         this.hideMenuItems();
1652         if(this.el && this.isVisible()){
1653             this.fireEvent("beforehide", this);
1654             if(this.activeItem){
1655                 this.activeItem.deactivate();
1656                 this.activeItem = null;
1657             }
1658             this.triggerEl.removeClass('open');;
1659             this.hidden = true;
1660             this.fireEvent("hide", this);
1661         }
1662         if(deep === true && this.parentMenu){
1663             this.parentMenu.hide(true);
1664         }
1665     },
1666     
1667     onTriggerPress  : function(e)
1668     {
1669         
1670         Roo.log('trigger press');
1671         //Roo.log(e.getTarget());
1672        // Roo.log(this.triggerEl.dom);
1673         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1674             return;
1675         }
1676         if (this.isVisible()) {
1677             Roo.log('hide');
1678             this.hide();
1679         } else {
1680             this.show(this.triggerEl, false, false);
1681         }
1682         
1683         
1684     },
1685     
1686          
1687        
1688     
1689     hideMenuItems : function()
1690     {
1691         //$(backdrop).remove()
1692         Roo.select('.open',true).each(function(aa) {
1693             
1694             aa.removeClass('open');
1695           //var parent = getParent($(this))
1696           //var relatedTarget = { relatedTarget: this }
1697           
1698            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1699           //if (e.isDefaultPrevented()) return
1700            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1701         })
1702     },
1703     addxtypeChild : function (tree, cntr) {
1704         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1705           
1706         this.menuitems.add(comp);
1707         return comp;
1708
1709     },
1710     getEl : function()
1711     {
1712         Roo.log(this.el);
1713         return this.el;
1714     }
1715 });
1716
1717  
1718  /*
1719  * - LGPL
1720  *
1721  * menu item
1722  * 
1723  */
1724
1725
1726 /**
1727  * @class Roo.bootstrap.MenuItem
1728  * @extends Roo.bootstrap.Component
1729  * Bootstrap MenuItem class
1730  * @cfg {String} html the menu label
1731  * @cfg {String} href the link
1732  * @cfg {Boolean} preventDefault (true | false) default true
1733  * 
1734  * 
1735  * @constructor
1736  * Create a new MenuItem
1737  * @param {Object} config The config object
1738  */
1739
1740
1741 Roo.bootstrap.MenuItem = function(config){
1742     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1743     this.addEvents({
1744         // raw events
1745         /**
1746          * @event click
1747          * The raw click event for the entire grid.
1748          * @param {Roo.EventObject} e
1749          */
1750         "click" : true
1751     });
1752 };
1753
1754 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1755     
1756     href : false,
1757     html : false,
1758     preventDefault: true,
1759     
1760     getAutoCreate : function(){
1761         var cfg= {
1762             tag: 'li',
1763             cls: 'dropdown-menu-item',
1764             cn: [
1765                     {
1766                         tag : 'a',
1767                         href : '#',
1768                         html : 'Link'
1769                     }
1770                 ]
1771         };
1772         if (this.parent().type == 'treeview') {
1773             cfg.cls = 'treeview-menu';
1774         }
1775         
1776         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1777         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1778         return cfg;
1779     },
1780     
1781     initEvents: function() {
1782         
1783         //this.el.select('a').on('click', this.onClick, this);
1784         
1785     },
1786     onClick : function(e)
1787     {
1788         Roo.log('item on click ');
1789         //if(this.preventDefault){
1790         //    e.preventDefault();
1791         //}
1792         //this.parent().hideMenuItems();
1793         
1794         this.fireEvent('click', this, e);
1795     },
1796     getEl : function()
1797     {
1798         return this.el;
1799     }
1800 });
1801
1802  
1803
1804  /*
1805  * - LGPL
1806  *
1807  * menu separator
1808  * 
1809  */
1810
1811
1812 /**
1813  * @class Roo.bootstrap.MenuSeparator
1814  * @extends Roo.bootstrap.Component
1815  * Bootstrap MenuSeparator class
1816  * 
1817  * @constructor
1818  * Create a new MenuItem
1819  * @param {Object} config The config object
1820  */
1821
1822
1823 Roo.bootstrap.MenuSeparator = function(config){
1824     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1825 };
1826
1827 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1828     
1829     getAutoCreate : function(){
1830         var cfg = {
1831             cls: 'divider',
1832             tag : 'li'
1833         };
1834         
1835         return cfg;
1836     }
1837    
1838 });
1839
1840  
1841
1842  
1843 /*
1844 <div class="modal fade">
1845   <div class="modal-dialog">
1846     <div class="modal-content">
1847       <div class="modal-header">
1848         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1849         <h4 class="modal-title">Modal title</h4>
1850       </div>
1851       <div class="modal-body">
1852         <p>One fine body&hellip;</p>
1853       </div>
1854       <div class="modal-footer">
1855         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1856         <button type="button" class="btn btn-primary">Save changes</button>
1857       </div>
1858     </div><!-- /.modal-content -->
1859   </div><!-- /.modal-dialog -->
1860 </div><!-- /.modal -->
1861 */
1862 /*
1863  * - LGPL
1864  *
1865  * page contgainer.
1866  * 
1867  */
1868
1869 /**
1870  * @class Roo.bootstrap.Modal
1871  * @extends Roo.bootstrap.Component
1872  * Bootstrap Modal class
1873  * @cfg {String} title Title of dialog
1874  * @cfg {Boolean} specificTitle (true|false) default false
1875  * @cfg {Array} buttons Array of buttons or standard button set..
1876  * @cfg {String} buttonPosition (left|right|center) default right
1877  * 
1878  * @constructor
1879  * Create a new Modal Dialog
1880  * @param {Object} config The config object
1881  */
1882
1883 Roo.bootstrap.Modal = function(config){
1884     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1885     this.addEvents({
1886         // raw events
1887         /**
1888          * @event btnclick
1889          * The raw btnclick event for the button
1890          * @param {Roo.EventObject} e
1891          */
1892         "btnclick" : true
1893     });
1894     this.buttons = this.buttons || [];
1895 };
1896
1897 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1898     
1899     title : 'test dialog',
1900    
1901     buttons : false,
1902     
1903     // set on load...
1904     body:  false,
1905     
1906     specificTitle: false,
1907     
1908     buttonPosition: 'right',
1909     
1910     onRender : function(ct, position)
1911     {
1912         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1913      
1914         if(!this.el){
1915             var cfg = Roo.apply({},  this.getAutoCreate());
1916             cfg.id = Roo.id();
1917             //if(!cfg.name){
1918             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1919             //}
1920             //if (!cfg.name.length) {
1921             //    delete cfg.name;
1922            // }
1923             if (this.cls) {
1924                 cfg.cls += ' ' + this.cls;
1925             }
1926             if (this.style) {
1927                 cfg.style = this.style;
1928             }
1929             this.el = Roo.get(document.body).createChild(cfg, position);
1930         }
1931         //var type = this.el.dom.type;
1932         
1933         if(this.tabIndex !== undefined){
1934             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1935         }
1936         
1937         
1938         
1939         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1940         this.maskEl.enableDisplayMode("block");
1941         this.maskEl.hide();
1942         //this.el.addClass("x-dlg-modal");
1943     
1944         if (this.buttons.length) {
1945             Roo.each(this.buttons, function(bb) {
1946                 b = Roo.apply({}, bb);
1947                 b.xns = b.xns || Roo.bootstrap;
1948                 b.xtype = b.xtype || 'Button';
1949                 if (typeof(b.listeners) == 'undefined') {
1950                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1951                 }
1952                 
1953                 var btn = Roo.factory(b);
1954                 
1955                 btn.onRender(this.el.select('.modal-footer div').first());
1956                 
1957             },this);
1958         }
1959         // render the children.
1960         var nitems = [];
1961         
1962         if(typeof(this.items) != 'undefined'){
1963             var items = this.items;
1964             delete this.items;
1965
1966             for(var i =0;i < items.length;i++) {
1967                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1968             }
1969         }
1970         
1971         this.items = nitems;
1972         
1973         this.body = this.el.select('.modal-body',true).first();
1974         this.close = this.el.select('.modal-header .close', true).first();
1975         this.footer = this.el.select('.modal-footer',true).first();
1976         this.initEvents();
1977         //this.el.addClass([this.fieldClass, this.cls]);
1978         
1979     },
1980     getAutoCreate : function(){
1981         
1982         
1983         var bdy = {
1984                 cls : 'modal-body',
1985                 html : this.html || ''
1986         };
1987         
1988         var title = {
1989             tag: 'h4',
1990             cls : 'modal-title',
1991             html : this.title
1992         };
1993         
1994         if(this.specificTitle){
1995             title = this.title;
1996         };
1997         
1998         return modal = {
1999             cls: "modal fade",
2000             style : 'display: none',
2001             cn : [
2002                 {
2003                     cls: "modal-dialog",
2004                     cn : [
2005                         {
2006                             cls : "modal-content",
2007                             cn : [
2008                                 {
2009                                     cls : 'modal-header',
2010                                     cn : [
2011                                         {
2012                                             tag: 'button',
2013                                             cls : 'close',
2014                                             html : '&times'
2015                                         },
2016                                         title
2017                                     ]
2018                                 },
2019                                 bdy,
2020                                 {
2021                                     cls : 'modal-footer',
2022                                     cn : [
2023                                         {
2024                                             tag: 'div',
2025                                             cls: 'btn-' + this.buttonPosition
2026                                         }
2027                                     ]
2028                                     
2029                                 }
2030                                 
2031                                 
2032                             ]
2033                             
2034                         }
2035                     ]
2036                         
2037                 }
2038             ]
2039             
2040             
2041         };
2042           
2043     },
2044     getChildContainer : function() {
2045          
2046          return this.el.select('.modal-body',true).first();
2047         
2048     },
2049     getButtonContainer : function() {
2050          return this.el.select('.modal-footer div',true).first();
2051         
2052     },
2053     initEvents : function()
2054     {
2055         this.el.select('.modal-header .close').on('click', this.hide, this);
2056 //        
2057 //        this.addxtype(this);
2058     },
2059     show : function() {
2060         
2061         if (!this.rendered) {
2062             this.render();
2063         }
2064        
2065         this.el.addClass('on');
2066         this.el.removeClass('fade');
2067         this.el.setStyle('display', 'block');
2068         Roo.get(document.body).addClass("x-body-masked");
2069         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2070         this.maskEl.show();
2071         this.el.setStyle('zIndex', '10001');
2072         this.fireEvent('show', this);
2073         
2074         
2075     },
2076     hide : function()
2077     {
2078         Roo.log('Modal hide?!');
2079         this.maskEl.hide();
2080         Roo.get(document.body).removeClass("x-body-masked");
2081         this.el.removeClass('on');
2082         this.el.addClass('fade');
2083         this.el.setStyle('display', 'none');
2084         this.fireEvent('hide', this);
2085     },
2086     
2087     addButton : function(str, cb)
2088     {
2089          
2090         
2091         var b = Roo.apply({}, { html : str } );
2092         b.xns = b.xns || Roo.bootstrap;
2093         b.xtype = b.xtype || 'Button';
2094         if (typeof(b.listeners) == 'undefined') {
2095             b.listeners = { click : cb.createDelegate(this)  };
2096         }
2097         
2098         var btn = Roo.factory(b);
2099            
2100         btn.onRender(this.el.select('.modal-footer div').first());
2101         
2102         return btn;   
2103        
2104     },
2105     
2106     setDefaultButton : function(btn)
2107     {
2108         //this.el.select('.modal-footer').()
2109     },
2110     resizeTo: function(w,h)
2111     {
2112         // skip..
2113     },
2114     setContentSize  : function(w, h)
2115     {
2116         
2117     },
2118     onButtonClick: function(btn,e)
2119     {
2120         //Roo.log([a,b,c]);
2121         this.fireEvent('btnclick', btn.name, e);
2122     },
2123     setTitle: function(str) {
2124         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2125         
2126     }
2127 });
2128
2129
2130 Roo.apply(Roo.bootstrap.Modal,  {
2131     /**
2132          * Button config that displays a single OK button
2133          * @type Object
2134          */
2135         OK :  [{
2136             name : 'ok',
2137             weight : 'primary',
2138             html : 'OK'
2139         }], 
2140         /**
2141          * Button config that displays Yes and No buttons
2142          * @type Object
2143          */
2144         YESNO : [
2145             {
2146                 name  : 'no',
2147                 html : 'No'
2148             },
2149             {
2150                 name  :'yes',
2151                 weight : 'primary',
2152                 html : 'Yes'
2153             }
2154         ],
2155         
2156         /**
2157          * Button config that displays OK and Cancel buttons
2158          * @type Object
2159          */
2160         OKCANCEL : [
2161             {
2162                name : 'cancel',
2163                 html : 'Cancel'
2164             },
2165             {
2166                 name : 'ok',
2167                 weight : 'primary',
2168                 html : 'OK'
2169             }
2170         ],
2171         /**
2172          * Button config that displays Yes, No and Cancel buttons
2173          * @type Object
2174          */
2175         YESNOCANCEL : [
2176             {
2177                 name : 'yes',
2178                 weight : 'primary',
2179                 html : 'Yes'
2180             },
2181             {
2182                 name : 'no',
2183                 html : 'No'
2184             },
2185             {
2186                 name : 'cancel',
2187                 html : 'Cancel'
2188             }
2189         ]
2190 });
2191  /*
2192  * - LGPL
2193  *
2194  * messagebox - can be used as a replace
2195  * 
2196  */
2197 /**
2198  * @class Roo.MessageBox
2199  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2200  * Example usage:
2201  *<pre><code>
2202 // Basic alert:
2203 Roo.Msg.alert('Status', 'Changes saved successfully.');
2204
2205 // Prompt for user data:
2206 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2207     if (btn == 'ok'){
2208         // process text value...
2209     }
2210 });
2211
2212 // Show a dialog using config options:
2213 Roo.Msg.show({
2214    title:'Save Changes?',
2215    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2216    buttons: Roo.Msg.YESNOCANCEL,
2217    fn: processResult,
2218    animEl: 'elId'
2219 });
2220 </code></pre>
2221  * @singleton
2222  */
2223 Roo.bootstrap.MessageBox = function(){
2224     var dlg, opt, mask, waitTimer;
2225     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2226     var buttons, activeTextEl, bwidth;
2227
2228     
2229     // private
2230     var handleButton = function(button){
2231         dlg.hide();
2232         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2233     };
2234
2235     // private
2236     var handleHide = function(){
2237         if(opt && opt.cls){
2238             dlg.el.removeClass(opt.cls);
2239         }
2240         //if(waitTimer){
2241         //    Roo.TaskMgr.stop(waitTimer);
2242         //    waitTimer = null;
2243         //}
2244     };
2245
2246     // private
2247     var updateButtons = function(b){
2248         var width = 0;
2249         if(!b){
2250             buttons["ok"].hide();
2251             buttons["cancel"].hide();
2252             buttons["yes"].hide();
2253             buttons["no"].hide();
2254             //dlg.footer.dom.style.display = 'none';
2255             return width;
2256         }
2257         dlg.footer.dom.style.display = '';
2258         for(var k in buttons){
2259             if(typeof buttons[k] != "function"){
2260                 if(b[k]){
2261                     buttons[k].show();
2262                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2263                     width += buttons[k].el.getWidth()+15;
2264                 }else{
2265                     buttons[k].hide();
2266                 }
2267             }
2268         }
2269         return width;
2270     };
2271
2272     // private
2273     var handleEsc = function(d, k, e){
2274         if(opt && opt.closable !== false){
2275             dlg.hide();
2276         }
2277         if(e){
2278             e.stopEvent();
2279         }
2280     };
2281
2282     return {
2283         /**
2284          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2285          * @return {Roo.BasicDialog} The BasicDialog element
2286          */
2287         getDialog : function(){
2288            if(!dlg){
2289                 dlg = new Roo.bootstrap.Modal( {
2290                     //draggable: true,
2291                     //resizable:false,
2292                     //constraintoviewport:false,
2293                     //fixedcenter:true,
2294                     //collapsible : false,
2295                     //shim:true,
2296                     //modal: true,
2297                   //  width:400,
2298                   //  height:100,
2299                     //buttonAlign:"center",
2300                     closeClick : function(){
2301                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2302                             handleButton("no");
2303                         }else{
2304                             handleButton("cancel");
2305                         }
2306                     }
2307                 });
2308                 dlg.render();
2309                 dlg.on("hide", handleHide);
2310                 mask = dlg.mask;
2311                 //dlg.addKeyListener(27, handleEsc);
2312                 buttons = {};
2313                 this.buttons = buttons;
2314                 var bt = this.buttonText;
2315                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2316                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2317                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2318                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2319                 Roo.log(buttons)
2320                 bodyEl = dlg.body.createChild({
2321
2322                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2323                         '<textarea class="roo-mb-textarea"></textarea>' +
2324                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2325                 });
2326                 msgEl = bodyEl.dom.firstChild;
2327                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2328                 textboxEl.enableDisplayMode();
2329                 textboxEl.addKeyListener([10,13], function(){
2330                     if(dlg.isVisible() && opt && opt.buttons){
2331                         if(opt.buttons.ok){
2332                             handleButton("ok");
2333                         }else if(opt.buttons.yes){
2334                             handleButton("yes");
2335                         }
2336                     }
2337                 });
2338                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2339                 textareaEl.enableDisplayMode();
2340                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2341                 progressEl.enableDisplayMode();
2342                 var pf = progressEl.dom.firstChild;
2343                 if (pf) {
2344                     pp = Roo.get(pf.firstChild);
2345                     pp.setHeight(pf.offsetHeight);
2346                 }
2347                 
2348             }
2349             return dlg;
2350         },
2351
2352         /**
2353          * Updates the message box body text
2354          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2355          * the XHTML-compliant non-breaking space character '&amp;#160;')
2356          * @return {Roo.MessageBox} This message box
2357          */
2358         updateText : function(text){
2359             if(!dlg.isVisible() && !opt.width){
2360                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2361             }
2362             msgEl.innerHTML = text || '&#160;';
2363       
2364             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2365             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2366             var w = Math.max(
2367                     Math.min(opt.width || cw , this.maxWidth), 
2368                     Math.max(opt.minWidth || this.minWidth, bwidth)
2369             );
2370             if(opt.prompt){
2371                 activeTextEl.setWidth(w);
2372             }
2373             if(dlg.isVisible()){
2374                 dlg.fixedcenter = false;
2375             }
2376             // to big, make it scroll. = But as usual stupid IE does not support
2377             // !important..
2378             
2379             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2380                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2381                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2382             } else {
2383                 bodyEl.dom.style.height = '';
2384                 bodyEl.dom.style.overflowY = '';
2385             }
2386             if (cw > w) {
2387                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2388             } else {
2389                 bodyEl.dom.style.overflowX = '';
2390             }
2391             
2392             dlg.setContentSize(w, bodyEl.getHeight());
2393             if(dlg.isVisible()){
2394                 dlg.fixedcenter = true;
2395             }
2396             return this;
2397         },
2398
2399         /**
2400          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2401          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2402          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2403          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2404          * @return {Roo.MessageBox} This message box
2405          */
2406         updateProgress : function(value, text){
2407             if(text){
2408                 this.updateText(text);
2409             }
2410             if (pp) { // weird bug on my firefox - for some reason this is not defined
2411                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2412             }
2413             return this;
2414         },        
2415
2416         /**
2417          * Returns true if the message box is currently displayed
2418          * @return {Boolean} True if the message box is visible, else false
2419          */
2420         isVisible : function(){
2421             return dlg && dlg.isVisible();  
2422         },
2423
2424         /**
2425          * Hides the message box if it is displayed
2426          */
2427         hide : function(){
2428             if(this.isVisible()){
2429                 dlg.hide();
2430             }  
2431         },
2432
2433         /**
2434          * Displays a new message box, or reinitializes an existing message box, based on the config options
2435          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2436          * The following config object properties are supported:
2437          * <pre>
2438 Property    Type             Description
2439 ----------  ---------------  ------------------------------------------------------------------------------------
2440 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2441                                    closes (defaults to undefined)
2442 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2443                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2444 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2445                                    progress and wait dialogs will ignore this property and always hide the
2446                                    close button as they can only be closed programmatically.
2447 cls               String           A custom CSS class to apply to the message box element
2448 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2449                                    displayed (defaults to 75)
2450 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2451                                    function will be btn (the name of the button that was clicked, if applicable,
2452                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2453                                    Progress and wait dialogs will ignore this option since they do not respond to
2454                                    user actions and can only be closed programmatically, so any required function
2455                                    should be called by the same code after it closes the dialog.
2456 icon              String           A CSS class that provides a background image to be used as an icon for
2457                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2458 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2459 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2460 modal             Boolean          False to allow user interaction with the page while the message box is
2461                                    displayed (defaults to true)
2462 msg               String           A string that will replace the existing message box body text (defaults
2463                                    to the XHTML-compliant non-breaking space character '&#160;')
2464 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2465 progress          Boolean          True to display a progress bar (defaults to false)
2466 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2467 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2468 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2469 title             String           The title text
2470 value             String           The string value to set into the active textbox element if displayed
2471 wait              Boolean          True to display a progress bar (defaults to false)
2472 width             Number           The width of the dialog in pixels
2473 </pre>
2474          *
2475          * Example usage:
2476          * <pre><code>
2477 Roo.Msg.show({
2478    title: 'Address',
2479    msg: 'Please enter your address:',
2480    width: 300,
2481    buttons: Roo.MessageBox.OKCANCEL,
2482    multiline: true,
2483    fn: saveAddress,
2484    animEl: 'addAddressBtn'
2485 });
2486 </code></pre>
2487          * @param {Object} config Configuration options
2488          * @return {Roo.MessageBox} This message box
2489          */
2490         show : function(options)
2491         {
2492             
2493             // this causes nightmares if you show one dialog after another
2494             // especially on callbacks..
2495              
2496             if(this.isVisible()){
2497                 
2498                 this.hide();
2499                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2500                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2501                 Roo.log("New Dialog Message:" +  options.msg )
2502                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2503                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2504                 
2505             }
2506             var d = this.getDialog();
2507             opt = options;
2508             d.setTitle(opt.title || "&#160;");
2509             d.close.setDisplayed(opt.closable !== false);
2510             activeTextEl = textboxEl;
2511             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2512             if(opt.prompt){
2513                 if(opt.multiline){
2514                     textboxEl.hide();
2515                     textareaEl.show();
2516                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2517                         opt.multiline : this.defaultTextHeight);
2518                     activeTextEl = textareaEl;
2519                 }else{
2520                     textboxEl.show();
2521                     textareaEl.hide();
2522                 }
2523             }else{
2524                 textboxEl.hide();
2525                 textareaEl.hide();
2526             }
2527             progressEl.setDisplayed(opt.progress === true);
2528             this.updateProgress(0);
2529             activeTextEl.dom.value = opt.value || "";
2530             if(opt.prompt){
2531                 dlg.setDefaultButton(activeTextEl);
2532             }else{
2533                 var bs = opt.buttons;
2534                 var db = null;
2535                 if(bs && bs.ok){
2536                     db = buttons["ok"];
2537                 }else if(bs && bs.yes){
2538                     db = buttons["yes"];
2539                 }
2540                 dlg.setDefaultButton(db);
2541             }
2542             bwidth = updateButtons(opt.buttons);
2543             this.updateText(opt.msg);
2544             if(opt.cls){
2545                 d.el.addClass(opt.cls);
2546             }
2547             d.proxyDrag = opt.proxyDrag === true;
2548             d.modal = opt.modal !== false;
2549             d.mask = opt.modal !== false ? mask : false;
2550             if(!d.isVisible()){
2551                 // force it to the end of the z-index stack so it gets a cursor in FF
2552                 document.body.appendChild(dlg.el.dom);
2553                 d.animateTarget = null;
2554                 d.show(options.animEl);
2555             }
2556             return this;
2557         },
2558
2559         /**
2560          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2561          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2562          * and closing the message box when the process is complete.
2563          * @param {String} title The title bar text
2564          * @param {String} msg The message box body text
2565          * @return {Roo.MessageBox} This message box
2566          */
2567         progress : function(title, msg){
2568             this.show({
2569                 title : title,
2570                 msg : msg,
2571                 buttons: false,
2572                 progress:true,
2573                 closable:false,
2574                 minWidth: this.minProgressWidth,
2575                 modal : true
2576             });
2577             return this;
2578         },
2579
2580         /**
2581          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2582          * If a callback function is passed it will be called after the user clicks the button, and the
2583          * id of the button that was clicked will be passed as the only parameter to the callback
2584          * (could also be the top-right close button).
2585          * @param {String} title The title bar text
2586          * @param {String} msg The message box body text
2587          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2588          * @param {Object} scope (optional) The scope of the callback function
2589          * @return {Roo.MessageBox} This message box
2590          */
2591         alert : function(title, msg, fn, scope){
2592             this.show({
2593                 title : title,
2594                 msg : msg,
2595                 buttons: this.OK,
2596                 fn: fn,
2597                 scope : scope,
2598                 modal : true
2599             });
2600             return this;
2601         },
2602
2603         /**
2604          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2605          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2606          * You are responsible for closing the message box when the process is complete.
2607          * @param {String} msg The message box body text
2608          * @param {String} title (optional) The title bar text
2609          * @return {Roo.MessageBox} This message box
2610          */
2611         wait : function(msg, title){
2612             this.show({
2613                 title : title,
2614                 msg : msg,
2615                 buttons: false,
2616                 closable:false,
2617                 progress:true,
2618                 modal:true,
2619                 width:300,
2620                 wait:true
2621             });
2622             waitTimer = Roo.TaskMgr.start({
2623                 run: function(i){
2624                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2625                 },
2626                 interval: 1000
2627             });
2628             return this;
2629         },
2630
2631         /**
2632          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2633          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2634          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2635          * @param {String} title The title bar text
2636          * @param {String} msg The message box body text
2637          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2638          * @param {Object} scope (optional) The scope of the callback function
2639          * @return {Roo.MessageBox} This message box
2640          */
2641         confirm : function(title, msg, fn, scope){
2642             this.show({
2643                 title : title,
2644                 msg : msg,
2645                 buttons: this.YESNO,
2646                 fn: fn,
2647                 scope : scope,
2648                 modal : true
2649             });
2650             return this;
2651         },
2652
2653         /**
2654          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2655          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2656          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2657          * (could also be the top-right close button) and the text that was entered will be passed as the two
2658          * parameters to the callback.
2659          * @param {String} title The title bar text
2660          * @param {String} msg The message box body text
2661          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2662          * @param {Object} scope (optional) The scope of the callback function
2663          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2664          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2665          * @return {Roo.MessageBox} This message box
2666          */
2667         prompt : function(title, msg, fn, scope, multiline){
2668             this.show({
2669                 title : title,
2670                 msg : msg,
2671                 buttons: this.OKCANCEL,
2672                 fn: fn,
2673                 minWidth:250,
2674                 scope : scope,
2675                 prompt:true,
2676                 multiline: multiline,
2677                 modal : true
2678             });
2679             return this;
2680         },
2681
2682         /**
2683          * Button config that displays a single OK button
2684          * @type Object
2685          */
2686         OK : {ok:true},
2687         /**
2688          * Button config that displays Yes and No buttons
2689          * @type Object
2690          */
2691         YESNO : {yes:true, no:true},
2692         /**
2693          * Button config that displays OK and Cancel buttons
2694          * @type Object
2695          */
2696         OKCANCEL : {ok:true, cancel:true},
2697         /**
2698          * Button config that displays Yes, No and Cancel buttons
2699          * @type Object
2700          */
2701         YESNOCANCEL : {yes:true, no:true, cancel:true},
2702
2703         /**
2704          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2705          * @type Number
2706          */
2707         defaultTextHeight : 75,
2708         /**
2709          * The maximum width in pixels of the message box (defaults to 600)
2710          * @type Number
2711          */
2712         maxWidth : 600,
2713         /**
2714          * The minimum width in pixels of the message box (defaults to 100)
2715          * @type Number
2716          */
2717         minWidth : 100,
2718         /**
2719          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2720          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2721          * @type Number
2722          */
2723         minProgressWidth : 250,
2724         /**
2725          * An object containing the default button text strings that can be overriden for localized language support.
2726          * Supported properties are: ok, cancel, yes and no.
2727          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2728          * @type Object
2729          */
2730         buttonText : {
2731             ok : "OK",
2732             cancel : "Cancel",
2733             yes : "Yes",
2734             no : "No"
2735         }
2736     };
2737 }();
2738
2739 /**
2740  * Shorthand for {@link Roo.MessageBox}
2741  */
2742 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2743 Roo.Msg = Roo.Msg || Roo.MessageBox;
2744 /*
2745  * - LGPL
2746  *
2747  * navbar
2748  * 
2749  */
2750
2751 /**
2752  * @class Roo.bootstrap.Navbar
2753  * @extends Roo.bootstrap.Component
2754  * Bootstrap Navbar class
2755
2756  * @constructor
2757  * Create a new Navbar
2758  * @param {Object} config The config object
2759  */
2760
2761
2762 Roo.bootstrap.Navbar = function(config){
2763     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2764     
2765 };
2766
2767 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2768     
2769     
2770    
2771     // private
2772     navItems : false,
2773     loadMask : false,
2774     
2775     
2776     getAutoCreate : function(){
2777         
2778         
2779         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2780         
2781     },
2782     
2783     initEvents :function ()
2784     {
2785         //Roo.log(this.el.select('.navbar-toggle',true));
2786         this.el.select('.navbar-toggle',true).on('click', function() {
2787            // Roo.log('click');
2788             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2789         }, this);
2790         
2791         var mark = {
2792             tag: "div",
2793             cls:"x-dlg-mask"
2794         }
2795         
2796         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2797         
2798         var size = this.el.getSize();
2799         this.maskEl.setSize(size.width, size.height);
2800         this.maskEl.enableDisplayMode("block");
2801         this.maskEl.hide();
2802         
2803         if(this.loadMask){
2804             this.maskEl.show();
2805         }
2806     },
2807     
2808     
2809     getChildContainer : function()
2810     {
2811         if (this.el.select('.collapse').getCount()) {
2812             return this.el.select('.collapse',true).first();
2813         }
2814         
2815         return this.el;
2816     },
2817     
2818     mask : function()
2819     {
2820         this.maskEl.show();
2821     },
2822     
2823     unmask : function()
2824     {
2825         this.maskEl.hide();
2826     }
2827     
2828     
2829     
2830 });
2831
2832
2833
2834  
2835
2836  /*
2837  * - LGPL
2838  *
2839  * navbar
2840  * 
2841  */
2842
2843 /**
2844  * @class Roo.bootstrap.NavSimplebar
2845  * @extends Roo.bootstrap.Navbar
2846  * Bootstrap Sidebar class
2847  *
2848  * @cfg {Boolean} inverse is inverted color
2849  * 
2850  * @cfg {String} type (nav | pills | tabs)
2851  * @cfg {Boolean} arrangement stacked | justified
2852  * @cfg {String} align (left | right) alignment
2853  * 
2854  * @cfg {Boolean} main (true|false) main nav bar? default false
2855  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2856  * 
2857  * @cfg {String} tag (header|footer|nav|div) default is nav 
2858
2859  * 
2860  * 
2861  * 
2862  * @constructor
2863  * Create a new Sidebar
2864  * @param {Object} config The config object
2865  */
2866
2867
2868 Roo.bootstrap.NavSimplebar = function(config){
2869     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2870 };
2871
2872 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2873     
2874     inverse: false,
2875     
2876     type: false,
2877     arrangement: '',
2878     align : false,
2879     
2880     
2881     
2882     main : false,
2883     
2884     
2885     tag : false,
2886     
2887     
2888     getAutoCreate : function(){
2889         
2890         
2891         var cfg = {
2892             tag : this.tag || 'div',
2893             cls : 'navbar'
2894         };
2895           
2896         
2897         cfg.cn = [
2898             {
2899                 cls: 'nav',
2900                 tag : 'ul'
2901             }
2902         ];
2903         
2904          
2905         this.type = this.type || 'nav';
2906         if (['tabs','pills'].indexOf(this.type)!==-1) {
2907             cfg.cn[0].cls += ' nav-' + this.type
2908         
2909         
2910         } else {
2911             if (this.type!=='nav') {
2912                 Roo.log('nav type must be nav/tabs/pills')
2913             }
2914             cfg.cn[0].cls += ' navbar-nav'
2915         }
2916         
2917         
2918         
2919         
2920         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2921             cfg.cn[0].cls += ' nav-' + this.arrangement;
2922         }
2923         
2924         
2925         if (this.align === 'right') {
2926             cfg.cn[0].cls += ' navbar-right';
2927         }
2928         
2929         if (this.inverse) {
2930             cfg.cls += ' navbar-inverse';
2931             
2932         }
2933         
2934         
2935         return cfg;
2936     
2937         
2938     }
2939     
2940     
2941     
2942 });
2943
2944
2945
2946  
2947
2948  
2949        /*
2950  * - LGPL
2951  *
2952  * navbar
2953  * 
2954  */
2955
2956 /**
2957  * @class Roo.bootstrap.NavHeaderbar
2958  * @extends Roo.bootstrap.NavSimplebar
2959  * Bootstrap Sidebar class
2960  *
2961  * @cfg {String} brand what is brand
2962  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2963  * @cfg {String} brand_href href of the brand
2964  * 
2965  * @constructor
2966  * Create a new Sidebar
2967  * @param {Object} config The config object
2968  */
2969
2970
2971 Roo.bootstrap.NavHeaderbar = function(config){
2972     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
2973 };
2974
2975 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
2976     
2977     position: '',
2978     brand: '',
2979     brand_href: false,
2980     
2981     
2982     getAutoCreate : function(){
2983         
2984         
2985         
2986         var   cfg = {
2987             tag: this.nav || 'nav',
2988             cls: 'navbar',
2989             role: 'navigation',
2990             cn: [
2991                 {
2992                     tag: 'div',
2993                     cls: 'navbar-header',
2994                     cn: [
2995                         {
2996                         tag: 'button',
2997                         type: 'button',
2998                         cls: 'navbar-toggle',
2999                         'data-toggle': 'collapse',
3000                         cn: [
3001                             {
3002                                 tag: 'span',
3003                                 cls: 'sr-only',
3004                                 html: 'Toggle navigation'
3005                             },
3006                             {
3007                                 tag: 'span',
3008                                 cls: 'icon-bar'
3009                             },
3010                             {
3011                                 tag: 'span',
3012                                 cls: 'icon-bar'
3013                             },
3014                             {
3015                                 tag: 'span',
3016                                 cls: 'icon-bar'
3017                             }
3018                         ]
3019                         }
3020                     ]
3021                 },
3022                 {
3023                 tag: 'div',
3024                 cls: 'collapse navbar-collapse'
3025                 }
3026             ]
3027         };
3028         
3029         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3030         
3031         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3032             cfg.cls += ' navbar-' + this.position;
3033             
3034             // tag can override this..
3035             
3036             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3037         }
3038         
3039         if (this.brand !== '') {
3040             cfg.cn[0].cn.push({
3041                 tag: 'a',
3042                 href: this.brand_href ? this.brand_href : '#',
3043                 cls: 'navbar-brand',
3044                 cn: [
3045                 this.brand
3046                 ]
3047             });
3048         }
3049         
3050         if(this.main){
3051             cfg.cls += ' main-nav';
3052         }
3053         
3054         
3055         return cfg;
3056
3057         
3058     }
3059     
3060     
3061     
3062 });
3063
3064
3065
3066  
3067
3068  /*
3069  * - LGPL
3070  *
3071  * navbar
3072  * 
3073  */
3074
3075 /**
3076  * @class Roo.bootstrap.NavSidebar
3077  * @extends Roo.bootstrap.Navbar
3078  * Bootstrap Sidebar class
3079  * 
3080  * @constructor
3081  * Create a new Sidebar
3082  * @param {Object} config The config object
3083  */
3084
3085
3086 Roo.bootstrap.NavSidebar = function(config){
3087     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3088 };
3089
3090 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3091     
3092     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3093     
3094     getAutoCreate : function(){
3095         
3096         
3097         return  {
3098             tag: 'div',
3099             cls: 'sidebar sidebar-nav'
3100         };
3101     
3102         
3103     }
3104     
3105     
3106     
3107 });
3108
3109
3110
3111  
3112
3113  /*
3114  * - LGPL
3115  *
3116  * nav group
3117  * 
3118  */
3119
3120 /**
3121  * @class Roo.bootstrap.NavGroup
3122  * @extends Roo.bootstrap.Component
3123  * Bootstrap NavGroup class
3124  * @cfg {String} align left | right
3125  * @cfg {Boolean} inverse false | true
3126  * @cfg {String} type (nav|pills|tab) default nav
3127  * @cfg {String} navId - reference Id for navbar.
3128
3129  * 
3130  * @constructor
3131  * Create a new nav group
3132  * @param {Object} config The config object
3133  */
3134
3135 Roo.bootstrap.NavGroup = function(config){
3136     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3137     this.navItems = [];
3138     Roo.bootstrap.NavGroup.register(this);
3139      this.addEvents({
3140         /**
3141              * @event changed
3142              * Fires when the active item changes
3143              * @param {Roo.bootstrap.NavGroup} this
3144              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3145              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3146          */
3147         'changed': true
3148      });
3149     
3150 };
3151
3152 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3153     
3154     align: '',
3155     inverse: false,
3156     form: false,
3157     type: 'nav',
3158     navId : '',
3159     // private
3160     
3161     navItems : false,
3162     
3163     getAutoCreate : function()
3164     {
3165         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3166         
3167         cfg = {
3168             tag : 'ul',
3169             cls: 'nav' 
3170         }
3171         
3172         if (['tabs','pills'].indexOf(this.type)!==-1) {
3173             cfg.cls += ' nav-' + this.type
3174         } else {
3175             if (this.type!=='nav') {
3176                 Roo.log('nav type must be nav/tabs/pills')
3177             }
3178             cfg.cls += ' navbar-nav'
3179         }
3180         
3181         if (this.parent().sidebar) {
3182             cfg = {
3183                 tag: 'ul',
3184                 cls: 'dashboard-menu sidebar-menu'
3185             }
3186             
3187             return cfg;
3188         }
3189         
3190         if (this.form === true) {
3191             cfg = {
3192                 tag: 'form',
3193                 cls: 'navbar-form'
3194             }
3195             
3196             if (this.align === 'right') {
3197                 cfg.cls += ' navbar-right';
3198             } else {
3199                 cfg.cls += ' navbar-left';
3200             }
3201         }
3202         
3203         if (this.align === 'right') {
3204             cfg.cls += ' navbar-right';
3205         }
3206         
3207         if (this.inverse) {
3208             cfg.cls += ' navbar-inverse';
3209             
3210         }
3211         
3212         
3213         return cfg;
3214     },
3215     
3216     setActiveItem : function(item)
3217     {
3218         var prev = false;
3219         Roo.each(this.navItems, function(v){
3220             if (v == item) {
3221                 return ;
3222             }
3223             if (v.isActive()) {
3224                 v.setActive(false, true);
3225                 prev = v;
3226                 
3227             }
3228             
3229         });
3230
3231         item.setActive(true, true);
3232         this.fireEvent('changed', this, item, prev);
3233         
3234         
3235     },
3236     
3237     
3238     register : function(item)
3239     {
3240         this.navItems.push( item);
3241         item.navId = this.navId;
3242     
3243     },
3244     getNavItem: function(tabId)
3245     {
3246         var ret = false;
3247         Roo.each(this.navItems, function(e) {
3248             if (e.tabId == tabId) {
3249                ret =  e;
3250                return false;
3251             }
3252             return true;
3253             
3254         });
3255         return ret;
3256     }
3257 });
3258
3259  
3260 Roo.apply(Roo.bootstrap.NavGroup, {
3261     
3262     groups: {},
3263     
3264     register : function(navgrp)
3265     {
3266         this.groups[navgrp.navId] = navgrp;
3267         
3268     },
3269     get: function(navId) {
3270         return this.groups[navId];
3271     }
3272     
3273     
3274     
3275 });
3276
3277  /*
3278  * - LGPL
3279  *
3280  * row
3281  * 
3282  */
3283
3284 /**
3285  * @class Roo.bootstrap.Navbar.Item
3286  * @extends Roo.bootstrap.Component
3287  * Bootstrap Navbar.Button class
3288  * @cfg {String} href  link to
3289  * @cfg {String} html content of button
3290  * @cfg {String} badge text inside badge
3291  * @cfg {String} glyphicon name of glyphicon
3292  * @cfg {String} icon name of font awesome icon
3293  * @cfg {Boolean} active Is item active
3294  * @cfg {Boolean} preventDefault (true | false) default false
3295  * @cfg {String} tabId the tab that this item activates.
3296   
3297  * @constructor
3298  * Create a new Navbar Button
3299  * @param {Object} config The config object
3300  */
3301 Roo.bootstrap.Navbar.Item = function(config){
3302     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3303     this.addEvents({
3304         // raw events
3305         /**
3306          * @event click
3307          * The raw click event for the entire grid.
3308          * @param {Roo.EventObject} e
3309          */
3310         "click" : true,
3311          /**
3312             * @event changed
3313             * Fires when the active item active state changes
3314             * @param {Roo.bootstrap.Navbar.Item} this
3315             * @param {boolean} state the new state
3316              
3317          */
3318         'changed': true
3319     });
3320    
3321 };
3322
3323 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
3324     
3325     href: false,
3326     html: '',
3327     badge: '',
3328     icon: false,
3329     glyphicon: false,
3330     active: false,
3331     preventDefault : false,
3332     tabId : false,
3333     
3334     getAutoCreate : function(){
3335         
3336         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3337         
3338         if (this.parent().parent().sidebar === true) {
3339             cfg = {
3340                 tag: 'li',
3341                 cls: '',
3342                 cn: [
3343                     {
3344                     tag: 'p',
3345                     cls: ''
3346                     }
3347                 ]
3348             }
3349             
3350             if (this.html) {
3351                 cfg.cn[0].html = this.html;
3352             }
3353             
3354             if (this.active) {
3355                 this.cls += ' active';
3356             }
3357             
3358             if (this.menu) {
3359                 cfg.cn[0].cls += ' dropdown-toggle';
3360                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3361             }
3362             
3363             if (this.href) {
3364                 cfg.cn[0].tag = 'a',
3365                 cfg.cn[0].href = this.href;
3366             }
3367             
3368             if (this.glyphicon) {
3369                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3370             }
3371                 
3372             if (this.icon) {
3373                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3374             }
3375             
3376             return cfg;
3377         }
3378         
3379         cfg = {
3380             tag: 'li',
3381                 cls: 'nav-item'
3382         }
3383             
3384         if (this.active) {
3385             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3386         }
3387             
3388         cfg.cn = [
3389             {
3390                 tag: 'p',
3391                 html: 'Text'
3392             }
3393         ];
3394         
3395         if (this.glyphicon) {
3396             if(cfg.html){cfg.html = ' ' + this.html};
3397             cfg.cn=[
3398                 {
3399                     tag: 'span',
3400                     cls: 'glyphicon glyphicon-' + this.glyphicon
3401                 }
3402             ];
3403         }
3404         
3405         cfg.cn[0].html = this.html || cfg.cn[0].html ;
3406         
3407         if (this.menu) {
3408             cfg.cn[0].tag='a';
3409             cfg.cn[0].href='#';
3410             cfg.cn[0].html += " <span class='caret'></span>";
3411         //}else if (!this.href) {
3412         //    cfg.cn[0].tag='p';
3413         //    cfg.cn[0].cls='navbar-text';
3414         } else {
3415             cfg.cn[0].tag='a';
3416             cfg.cn[0].href=this.href||'#';
3417             cfg.cn[0].html=this.html;
3418         }
3419         
3420         if (this.badge !== '') {
3421             
3422             cfg.cn[0].cn=[
3423             cfg.cn[0].html + ' ',
3424             {
3425                 tag: 'span',
3426                 cls: 'badge',
3427                 html: this.badge
3428             }
3429             ];
3430             cfg.cn[0].html=''
3431         }
3432          
3433         if (this.icon) {
3434             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3435         }
3436         
3437         return cfg;
3438     },
3439     initEvents: function() {
3440        // Roo.log('init events?');
3441        // Roo.log(this.el.dom);
3442         this.el.select('a',true).on('click', this.onClick, this);
3443         // at this point parent should be available..
3444         this.parent().register(this);
3445     },
3446     
3447     onClick : function(e)
3448     {
3449         if(this.preventDefault){
3450             e.preventDefault();
3451         }
3452         
3453         if (typeof (this.menu) != 'undefined') {
3454             this.menu.parentType = this.xtype;
3455             this.menu.triggerEl = this.el;
3456             this.addxtype(Roo.apply({}, this.menu));
3457         }
3458         
3459         if(this.fireEvent('click', this, e) === false){
3460             return;
3461         };
3462         
3463         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3464              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3465                 this.parent().setActiveItem(this);
3466             }
3467             
3468         } 
3469     },
3470     
3471     isActive: function () {
3472         return this.active
3473     },
3474     setActive : function(state, fire)
3475     {
3476         this.active = state;
3477         if (!state ) {
3478             this.el.removeClass('active');
3479         } else if (!this.el.hasClass('active')) {
3480             this.el.addClass('active');
3481         }
3482         if (fire) {
3483             this.fireEvent('changed', this, state);
3484         }
3485         
3486         
3487     }
3488      // this should not be here...
3489  
3490 });
3491  
3492
3493  /*
3494  * - LGPL
3495  *
3496  * row
3497  * 
3498  */
3499
3500 /**
3501  * @class Roo.bootstrap.NavItem
3502  * @extends Roo.bootstrap.Component
3503  * Bootstrap Navbar.NavItem class
3504  * @cfg {String} href  link to
3505  * @cfg {String} html content of button
3506  * @cfg {String} badge text inside badge
3507  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3508  * @cfg {String} glyphicon name of glyphicon
3509  * @cfg {String} icon name of font awesome icon
3510  * @cfg {Boolean} active Is item active
3511  * @cfg {Boolean} preventDefault (true | false) default false
3512  * @cfg {String} tabId the tab that this item activates.
3513   
3514  * @constructor
3515  * Create a new Navbar Item
3516  * @param {Object} config The config object
3517  */
3518 Roo.bootstrap.NavItem = function(config){
3519     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3520     this.addEvents({
3521         // raw events
3522         /**
3523          * @event click
3524          * The raw click event for the entire grid.
3525          * @param {Roo.EventObject} e
3526          */
3527         "click" : true,
3528          /**
3529             * @event changed
3530             * Fires when the active item active state changes
3531             * @param {Roo.bootstrap.NavItem} this
3532             * @param {boolean} state the new state
3533              
3534          */
3535         'changed': true
3536     });
3537    
3538 };
3539
3540 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3541     
3542     href: false,
3543     html: '',
3544     badge: '',
3545     icon: false,
3546     glyphicon: false,
3547     active: false,
3548     preventDefault : false,
3549     tabId : false,
3550     
3551     getAutoCreate : function(){
3552          
3553         var cfg = {
3554             tag: 'li',
3555             cls: 'nav-item',
3556             cn : [
3557                 {
3558                     tag: 'a',
3559                     href : this.href || "#",
3560                     html: this.html || ''
3561                 }
3562             ]
3563         }
3564             
3565         if (this.active) {
3566             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3567         }
3568             
3569         // glyphicon and icon go before content..
3570         if (this.glyphicon || this.icon) {
3571              if (this.icon) {
3572                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html + '</span>'
3573             } else {
3574                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span>'  + cfg.cn[0].html;
3575             }
3576         }
3577         
3578         
3579         
3580         if (this.menu) {
3581             
3582             cfg.cn[0].html += " <span class='caret'></span>";
3583          
3584         }
3585         
3586         if (this.badge !== '') {
3587              
3588             cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3589         }
3590         
3591         
3592         
3593         return cfg;
3594     },
3595     initEvents: function() {
3596        // Roo.log('init events?');
3597        // Roo.log(this.el.dom);
3598        if (typeof (this.menu) != 'undefined') {
3599             this.menu.parentType = this.xtype;
3600             this.menu.triggerEl = this.el;
3601             this.addxtype(Roo.apply({}, this.menu));
3602         }
3603
3604        
3605         this.el.select('a',true).on('click', this.onClick, this);
3606         // at this point parent should be available..
3607         this.parent().register(this);
3608     },
3609     
3610     onClick : function(e)
3611     {
3612         if(this.preventDefault){
3613             e.preventDefault();
3614         }
3615         
3616         if(this.fireEvent('click', this, e) === false){
3617             return;
3618         };
3619         
3620         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3621              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3622                 this.parent().setActiveItem(this);
3623             }
3624             
3625             
3626             
3627         } 
3628     },
3629     
3630     isActive: function () {
3631         return this.active
3632     },
3633     setActive : function(state, fire)
3634     {
3635         this.active = state;
3636         if (!state ) {
3637             this.el.removeClass('active');
3638         } else if (!this.el.hasClass('active')) {
3639             this.el.addClass('active');
3640         }
3641         if (fire) {
3642             this.fireEvent('changed', this, state);
3643         }
3644         
3645         
3646     }
3647      // this should not be here...
3648  
3649 });
3650  
3651
3652  /*
3653  * - LGPL
3654  *
3655  * sidebar item
3656  *
3657  *  li
3658  *    <span> icon </span>
3659  *    <span> text </span>
3660  *    <span>badge </span>
3661  */
3662
3663 /**
3664  * @class Roo.bootstrap.NavSidebarItem
3665  * @extends Roo.bootstrap.NavItem
3666  * Bootstrap Navbar.NavSidebarItem class
3667  * @constructor
3668  * Create a new Navbar Button
3669  * @param {Object} config The config object
3670  */
3671 Roo.bootstrap.NavSidebarItem = function(config){
3672     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3673     this.addEvents({
3674         // raw events
3675         /**
3676          * @event click
3677          * The raw click event for the entire grid.
3678          * @param {Roo.EventObject} e
3679          */
3680         "click" : true,
3681          /**
3682             * @event changed
3683             * Fires when the active item active state changes
3684             * @param {Roo.bootstrap.NavSidebarItem} this
3685             * @param {boolean} state the new state
3686              
3687          */
3688         'changed': true
3689     });
3690    
3691 };
3692
3693 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3694     
3695     
3696     getAutoCreate : function(){
3697         
3698         
3699         var a = {
3700                 tag: 'a',
3701                 href : this.href || '#',
3702                 cls: '',
3703                 html : '',
3704                 cn : []
3705         };
3706         var cfg = {
3707             tag: 'li',
3708             cls: '',
3709             cn: [ a ]
3710         }
3711         var span = {
3712             tag: 'span',
3713             html : this.html || ''
3714         }
3715         
3716         
3717         if (this.active) {
3718             cfg.cls += ' active';
3719         }
3720         
3721         // left icon..
3722         if (this.glyphicon || this.icon) {
3723             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3724             a.cn.push({ tag : 'i', cls : c }) ;
3725         }
3726         // html..
3727         a.cn.push(span);
3728         // then badge..
3729         if (this.badge !== '') {
3730             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3731         }
3732         // fi
3733         if (this.menu) {
3734             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3735             a.cls += 'dropdown-toggle treeview' ;
3736             
3737         }
3738         
3739         
3740         
3741         return cfg;
3742          
3743            
3744     }
3745    
3746      
3747  
3748 });
3749  
3750
3751  /*
3752  * - LGPL
3753  *
3754  * row
3755  * 
3756  */
3757
3758 /**
3759  * @class Roo.bootstrap.Row
3760  * @extends Roo.bootstrap.Component
3761  * Bootstrap Row class (contains columns...)
3762  * 
3763  * @constructor
3764  * Create a new Row
3765  * @param {Object} config The config object
3766  */
3767
3768 Roo.bootstrap.Row = function(config){
3769     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3770 };
3771
3772 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3773     
3774     getAutoCreate : function(){
3775        return {
3776             cls: 'row clearfix'
3777        };
3778     }
3779     
3780     
3781 });
3782
3783  
3784
3785  /*
3786  * - LGPL
3787  *
3788  * element
3789  * 
3790  */
3791
3792 /**
3793  * @class Roo.bootstrap.Element
3794  * @extends Roo.bootstrap.Component
3795  * Bootstrap Element class
3796  * @cfg {String} html contents of the element
3797  * @cfg {String} tag tag of the element
3798  * @cfg {String} cls class of the element
3799  * 
3800  * @constructor
3801  * Create a new Element
3802  * @param {Object} config The config object
3803  */
3804
3805 Roo.bootstrap.Element = function(config){
3806     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3807 };
3808
3809 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3810     
3811     tag: 'div',
3812     cls: '',
3813     html: '',
3814      
3815     
3816     getAutoCreate : function(){
3817         
3818         var cfg = {
3819             tag: this.tag,
3820             cls: this.cls,
3821             html: this.html
3822         }
3823         
3824         
3825         
3826         return cfg;
3827     }
3828    
3829 });
3830
3831  
3832
3833  /*
3834  * - LGPL
3835  *
3836  * pagination
3837  * 
3838  */
3839
3840 /**
3841  * @class Roo.bootstrap.Pagination
3842  * @extends Roo.bootstrap.Component
3843  * Bootstrap Pagination class
3844  * @cfg {String} size xs | sm | md | lg
3845  * @cfg {Boolean} inverse false | true
3846  * 
3847  * @constructor
3848  * Create a new Pagination
3849  * @param {Object} config The config object
3850  */
3851
3852 Roo.bootstrap.Pagination = function(config){
3853     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3854 };
3855
3856 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3857     
3858     cls: false,
3859     size: false,
3860     inverse: false,
3861     
3862     getAutoCreate : function(){
3863         var cfg = {
3864             tag: 'ul',
3865                 cls: 'pagination'
3866         };
3867         if (this.inverse) {
3868             cfg.cls += ' inverse';
3869         }
3870         if (this.html) {
3871             cfg.html=this.html;
3872         }
3873         if (this.cls) {
3874             cfg.cls += " " + this.cls;
3875         }
3876         return cfg;
3877     }
3878    
3879 });
3880
3881  
3882
3883  /*
3884  * - LGPL
3885  *
3886  * Pagination item
3887  * 
3888  */
3889
3890
3891 /**
3892  * @class Roo.bootstrap.PaginationItem
3893  * @extends Roo.bootstrap.Component
3894  * Bootstrap PaginationItem class
3895  * @cfg {String} html text
3896  * @cfg {String} href the link
3897  * @cfg {Boolean} preventDefault (true | false) default true
3898  * @cfg {Boolean} active (true | false) default false
3899  * 
3900  * 
3901  * @constructor
3902  * Create a new PaginationItem
3903  * @param {Object} config The config object
3904  */
3905
3906
3907 Roo.bootstrap.PaginationItem = function(config){
3908     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3909     this.addEvents({
3910         // raw events
3911         /**
3912          * @event click
3913          * The raw click event for the entire grid.
3914          * @param {Roo.EventObject} e
3915          */
3916         "click" : true
3917     });
3918 };
3919
3920 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3921     
3922     href : false,
3923     html : false,
3924     preventDefault: true,
3925     active : false,
3926     cls : false,
3927     
3928     getAutoCreate : function(){
3929         var cfg= {
3930             tag: 'li',
3931             cn: [
3932                 {
3933                     tag : 'a',
3934                     href : this.href ? this.href : '#',
3935                     html : this.html ? this.html : ''
3936                 }
3937             ]
3938         };
3939         
3940         if(this.cls){
3941             cfg.cls = this.cls;
3942         }
3943         
3944         if(this.active){
3945             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3946         }
3947         
3948         return cfg;
3949     },
3950     
3951     initEvents: function() {
3952         
3953         this.el.on('click', this.onClick, this);
3954         
3955     },
3956     onClick : function(e)
3957     {
3958         Roo.log('PaginationItem on click ');
3959         if(this.preventDefault){
3960             e.preventDefault();
3961         }
3962         
3963         this.fireEvent('click', this, e);
3964     }
3965    
3966 });
3967
3968  
3969
3970  /*
3971  * - LGPL
3972  *
3973  * slider
3974  * 
3975  */
3976
3977
3978 /**
3979  * @class Roo.bootstrap.Slider
3980  * @extends Roo.bootstrap.Component
3981  * Bootstrap Slider class
3982  *    
3983  * @constructor
3984  * Create a new Slider
3985  * @param {Object} config The config object
3986  */
3987
3988 Roo.bootstrap.Slider = function(config){
3989     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3990 };
3991
3992 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3993     
3994     getAutoCreate : function(){
3995         
3996         var cfg = {
3997             tag: 'div',
3998             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3999             cn: [
4000                 {
4001                     tag: 'a',
4002                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4003                 }
4004             ]
4005         }
4006         
4007         return cfg;
4008     }
4009    
4010 });
4011
4012  /*
4013  * - LGPL
4014  *
4015  * table
4016  * 
4017  */
4018
4019 /**
4020  * @class Roo.bootstrap.Table
4021  * @extends Roo.bootstrap.Component
4022  * Bootstrap Table class
4023  * @cfg {String} cls table class
4024  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4025  * @cfg {String} bgcolor Specifies the background color for a table
4026  * @cfg {Number} border Specifies whether the table cells should have borders or not
4027  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4028  * @cfg {Number} cellspacing Specifies the space between cells
4029  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4030  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4031  * @cfg {String} sortable Specifies that the table should be sortable
4032  * @cfg {String} summary Specifies a summary of the content of a table
4033  * @cfg {Number} width Specifies the width of a table
4034  * 
4035  * @cfg {boolean} striped Should the rows be alternative striped
4036  * @cfg {boolean} bordered Add borders to the table
4037  * @cfg {boolean} hover Add hover highlighting
4038  * @cfg {boolean} condensed Format condensed
4039  * @cfg {boolean} responsive Format condensed
4040  * @cfg {Boolean} loadMask (true|false) default false
4041  *
4042  
4043  
4044  * 
4045  * @constructor
4046  * Create a new Table
4047  * @param {Object} config The config object
4048  */
4049
4050 Roo.bootstrap.Table = function(config){
4051     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4052     
4053     if (this.sm) {
4054         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4055         this.sm = this.selModel;
4056         this.sm.xmodule = this.xmodule || false;
4057     }
4058     if (this.cm && typeof(this.cm.config) == 'undefined') {
4059         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
4060         this.cm = this.colModel;
4061         this.cm.xmodule = this.xmodule || false;
4062     }
4063     if (this.store) {
4064         this.store= Roo.factory(this.store, Roo.data);
4065         this.ds = this.store;
4066         this.ds.xmodule = this.xmodule || false;
4067          
4068     }
4069 };
4070
4071 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4072     
4073     cls: false,
4074     align: false,
4075     bgcolor: false,
4076     border: false,
4077     cellpadding: false,
4078     cellspacing: false,
4079     frame: false,
4080     rules: false,
4081     sortable: false,
4082     summary: false,
4083     width: false,
4084     striped : false,
4085     bordered: false,
4086     hover:  false,
4087     condensed : false,
4088     responsive : false,
4089     sm : false,
4090     cm : false,
4091     store : false,
4092     loadMask : false,
4093     
4094     getAutoCreate : function(){
4095         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4096         
4097         cfg = {
4098             tag: 'table',
4099             cls : 'table',
4100             cn : []
4101         }
4102             
4103         if (this.striped) {
4104             cfg.cls += ' table-striped';
4105         }
4106         if (this.hover) {
4107             cfg.cls += ' table-hover';
4108         }
4109         if (this.bordered) {
4110             cfg.cls += ' table-bordered';
4111         }
4112         if (this.condensed) {
4113             cfg.cls += ' table-condensed';
4114         }
4115         if (this.responsive) {
4116             cfg.cls += ' table-responsive';
4117         }
4118         
4119           
4120         
4121         
4122         if (this.cls) {
4123             cfg.cls+=  ' ' +this.cls;
4124         }
4125         
4126         // this lot should be simplifed...
4127         
4128         if (this.align) {
4129             cfg.align=this.align;
4130         }
4131         if (this.bgcolor) {
4132             cfg.bgcolor=this.bgcolor;
4133         }
4134         if (this.border) {
4135             cfg.border=this.border;
4136         }
4137         if (this.cellpadding) {
4138             cfg.cellpadding=this.cellpadding;
4139         }
4140         if (this.cellspacing) {
4141             cfg.cellspacing=this.cellspacing;
4142         }
4143         if (this.frame) {
4144             cfg.frame=this.frame;
4145         }
4146         if (this.rules) {
4147             cfg.rules=this.rules;
4148         }
4149         if (this.sortable) {
4150             cfg.sortable=this.sortable;
4151         }
4152         if (this.summary) {
4153             cfg.summary=this.summary;
4154         }
4155         if (this.width) {
4156             cfg.width=this.width;
4157         }
4158         
4159         if(this.store || this.cm){
4160             cfg.cn.push(this.renderHeader());
4161             cfg.cn.push(this.renderBody());
4162             cfg.cn.push(this.renderFooter());
4163             
4164             cfg.cls+=  ' TableGrid';
4165         }
4166         
4167         return cfg;
4168     },
4169 //    
4170 //    initTableGrid : function()
4171 //    {
4172 //        var cfg = {};
4173 //        
4174 //        var header = {
4175 //            tag: 'thead',
4176 //            cn : []
4177 //        };
4178 //        
4179 //        var cm = this.cm;
4180 //        
4181 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4182 //            header.cn.push({
4183 //                tag: 'th',
4184 //                html: cm.getColumnHeader(i)
4185 //            })
4186 //        }
4187 //        
4188 //        cfg.push(header);
4189 //        
4190 //        return cfg;
4191 //        
4192 //        
4193 //    },
4194     
4195     initEvents : function()
4196     {   
4197         if(!this.store || !this.cm){
4198             return;
4199         }
4200         
4201         Roo.log('initEvents with ds!!!!');
4202         
4203         var _this = this;
4204         
4205         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4206             e.on('click', _this.sort, _this);
4207         });
4208 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
4209 //        this.maskEl.enableDisplayMode("block");
4210 //        this.maskEl.show();
4211         
4212         this.parent().el.setStyle('position', 'relative');
4213         
4214         var mark = {
4215             tag: "div",
4216             cls:"x-dlg-mask",
4217             style: "text-align:center",
4218             cn: [
4219                 {
4220                     tag: "div",
4221                     style: "background-color:white;width:50%;margin:100 auto",
4222                     cn: [
4223                         {
4224                             tag: "img",
4225                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
4226                         },
4227                         {
4228                             tag: "span",
4229                             html: "Loading"
4230                         }
4231                         
4232                     ]
4233                 }
4234             ]
4235         }
4236         this.maskEl = Roo.DomHelper.append(this.parent().el, mark, true);
4237         
4238         var size = this.parent().el.getSize();
4239         
4240         this.maskEl.setSize(size.width, 300); // we will fix the height at the beginning...
4241         
4242         this.maskEl.enableDisplayMode("block");
4243         
4244         this.store.on('load', this.onLoad, this);
4245         this.store.on('beforeload', this.onBeforeLoad, this);
4246         
4247         this.store.load();
4248         
4249         
4250         
4251     },
4252     
4253     sort : function(e,el)
4254     {
4255         var col = Roo.get(el)
4256         
4257         if(!col.hasClass('sortable')){
4258             return;
4259         }
4260         
4261         var sort = col.attr('sort');
4262         var dir = 'ASC';
4263         
4264         if(col.hasClass('glyphicon-arrow-up')){
4265             dir = 'DESC';
4266         }
4267         
4268         this.store.sortInfo = {field : sort, direction : dir};
4269         
4270         this.store.load();
4271     },
4272     
4273     renderHeader : function()
4274     {
4275         var header = {
4276             tag: 'thead',
4277             cn : []
4278         };
4279         
4280         var cm = this.cm;
4281         
4282         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4283             
4284             var config = cm.config[i];
4285             
4286             if(typeof(config.hidden) != 'undefined' && config.hidden){
4287                 continue;
4288             }
4289                     
4290             var c = {
4291                 tag: 'th',
4292                 html: cm.getColumnHeader(i)
4293             };
4294             
4295             if(typeof(config.dataIndex) != 'undefined'){
4296                 c.sort = config.dataIndex;
4297             }
4298             
4299             if(typeof(config.sortable) != 'undefined' && config.sortable){
4300                 c.cls = 'sortable';
4301             }
4302             
4303             if(typeof(config.width) != 'undefined'){
4304                 c.style = 'width:' + config.width + 'px';
4305             }
4306             
4307             header.cn.push(c)
4308         }
4309         
4310         return header;
4311     },
4312     
4313     renderBody : function()
4314     {
4315         var body = {
4316             tag: 'tbody',
4317             cn : []
4318         };
4319         
4320         return body;
4321     },
4322     
4323     renderFooter : function()
4324     {
4325         var footer = {
4326             tag: 'tfoot',
4327             cn : []
4328         };
4329         
4330         return footer;
4331     },
4332     
4333     onLoad : function()
4334     {
4335         Roo.log('ds onload');
4336         
4337         var _this = this;
4338         var cm = this.cm;
4339         
4340         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4341             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4342             
4343             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4344                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4345             }
4346             
4347             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4348                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4349             }
4350         });
4351         
4352         var tbody = this.el.select('tbody', true).first();
4353         
4354         var renders = [];
4355                     
4356         if(this.store.getCount() > 0){
4357             this.store.data.each(function(d){
4358                 var row = {
4359                     tag : 'tr',
4360                     cn : []
4361                 };
4362                 
4363                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4364                     var config = cm.config[i];
4365                     
4366                     if(typeof(config.hidden) != 'undefined' && config.hidden){
4367                         continue;
4368                     }
4369                     
4370                     var renderer = cm.getRenderer(i);
4371                     var value = '';
4372                     var id = Roo.id();
4373                     
4374                     if(typeof(renderer) !== 'undefined'){
4375                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
4376                     }
4377                     
4378                     if(typeof(value) === 'object'){
4379                         renders.push({
4380                             id : id,
4381                             cfg : value 
4382                         })
4383                     }
4384                     
4385                     var td = {
4386                         tag: 'td',
4387                         id: id,
4388                         html: (typeof(value) === 'object') ? '' : value
4389                     };
4390                     
4391                     if(typeof(config.width) != 'undefined'){
4392                         td.style = 'width:' +  config.width + 'px';
4393                     }
4394                     
4395                     row.cn.push(td);
4396                    
4397                 }
4398                 
4399                 tbody.createChild(row);
4400                 
4401             });
4402         }
4403         
4404         
4405         if(renders.length){
4406             var _this = this;
4407             Roo.each(renders, function(r){
4408                 _this.renderColumn(r);
4409             })
4410         }
4411
4412         if(this.loadMask){
4413             this.maskEl.hide();
4414         }
4415     },
4416     
4417     onBeforeLoad : function()
4418     {
4419         Roo.log('ds onBeforeLoad');
4420         
4421         this.clear();
4422         
4423         if(this.loadMask){
4424             this.maskEl.show();
4425         }
4426     },
4427     
4428     clear : function()
4429     {
4430         this.el.select('tbody', true).first().dom.innerHTML = '';
4431     },
4432     
4433     getSelectionModel : function(){
4434         if(!this.selModel){
4435             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
4436         }
4437         return this.selModel;
4438     },
4439     
4440     renderColumn : function(r)
4441     {
4442         var _this = this;
4443         r.cfg.render(Roo.get(r.id));
4444         
4445         if(r.cfg.cn){
4446             Roo.each(r.cfg.cn, function(c){
4447                 var child = {
4448                     id: r.id,
4449                     cfg: c
4450                 }
4451                 _this.renderColumn(child);
4452             })
4453         }
4454     }
4455    
4456 });
4457
4458  
4459
4460  /*
4461  * - LGPL
4462  *
4463  * table cell
4464  * 
4465  */
4466
4467 /**
4468  * @class Roo.bootstrap.TableCell
4469  * @extends Roo.bootstrap.Component
4470  * Bootstrap TableCell class
4471  * @cfg {String} html cell contain text
4472  * @cfg {String} cls cell class
4473  * @cfg {String} tag cell tag (td|th) default td
4474  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
4475  * @cfg {String} align Aligns the content in a cell
4476  * @cfg {String} axis Categorizes cells
4477  * @cfg {String} bgcolor Specifies the background color of a cell
4478  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4479  * @cfg {Number} colspan Specifies the number of columns a cell should span
4480  * @cfg {String} headers Specifies one or more header cells a cell is related to
4481  * @cfg {Number} height Sets the height of a cell
4482  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
4483  * @cfg {Number} rowspan Sets the number of rows a cell should span
4484  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
4485  * @cfg {String} valign Vertical aligns the content in a cell
4486  * @cfg {Number} width Specifies the width of a cell
4487  * 
4488  * @constructor
4489  * Create a new TableCell
4490  * @param {Object} config The config object
4491  */
4492
4493 Roo.bootstrap.TableCell = function(config){
4494     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
4495 };
4496
4497 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
4498     
4499     html: false,
4500     cls: false,
4501     tag: false,
4502     abbr: false,
4503     align: false,
4504     axis: false,
4505     bgcolor: false,
4506     charoff: false,
4507     colspan: false,
4508     headers: false,
4509     height: false,
4510     nowrap: false,
4511     rowspan: false,
4512     scope: false,
4513     valign: false,
4514     width: false,
4515     
4516     
4517     getAutoCreate : function(){
4518         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
4519         
4520         cfg = {
4521             tag: 'td'
4522         }
4523         
4524         if(this.tag){
4525             cfg.tag = this.tag;
4526         }
4527         
4528         if (this.html) {
4529             cfg.html=this.html
4530         }
4531         if (this.cls) {
4532             cfg.cls=this.cls
4533         }
4534         if (this.abbr) {
4535             cfg.abbr=this.abbr
4536         }
4537         if (this.align) {
4538             cfg.align=this.align
4539         }
4540         if (this.axis) {
4541             cfg.axis=this.axis
4542         }
4543         if (this.bgcolor) {
4544             cfg.bgcolor=this.bgcolor
4545         }
4546         if (this.charoff) {
4547             cfg.charoff=this.charoff
4548         }
4549         if (this.colspan) {
4550             cfg.colspan=this.colspan
4551         }
4552         if (this.headers) {
4553             cfg.headers=this.headers
4554         }
4555         if (this.height) {
4556             cfg.height=this.height
4557         }
4558         if (this.nowrap) {
4559             cfg.nowrap=this.nowrap
4560         }
4561         if (this.rowspan) {
4562             cfg.rowspan=this.rowspan
4563         }
4564         if (this.scope) {
4565             cfg.scope=this.scope
4566         }
4567         if (this.valign) {
4568             cfg.valign=this.valign
4569         }
4570         if (this.width) {
4571             cfg.width=this.width
4572         }
4573         
4574         
4575         return cfg;
4576     }
4577    
4578 });
4579
4580  
4581
4582  /*
4583  * - LGPL
4584  *
4585  * table row
4586  * 
4587  */
4588
4589 /**
4590  * @class Roo.bootstrap.TableRow
4591  * @extends Roo.bootstrap.Component
4592  * Bootstrap TableRow class
4593  * @cfg {String} cls row class
4594  * @cfg {String} align Aligns the content in a table row
4595  * @cfg {String} bgcolor Specifies a background color for a table row
4596  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4597  * @cfg {String} valign Vertical aligns the content in a table row
4598  * 
4599  * @constructor
4600  * Create a new TableRow
4601  * @param {Object} config The config object
4602  */
4603
4604 Roo.bootstrap.TableRow = function(config){
4605     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4606 };
4607
4608 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4609     
4610     cls: false,
4611     align: false,
4612     bgcolor: false,
4613     charoff: false,
4614     valign: false,
4615     
4616     getAutoCreate : function(){
4617         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4618         
4619         cfg = {
4620             tag: 'tr'
4621         }
4622             
4623         if(this.cls){
4624             cfg.cls = this.cls;
4625         }
4626         if(this.align){
4627             cfg.align = this.align;
4628         }
4629         if(this.bgcolor){
4630             cfg.bgcolor = this.bgcolor;
4631         }
4632         if(this.charoff){
4633             cfg.charoff = this.charoff;
4634         }
4635         if(this.valign){
4636             cfg.valign = this.valign;
4637         }
4638         
4639         return cfg;
4640     }
4641    
4642 });
4643
4644  
4645
4646  /*
4647  * - LGPL
4648  *
4649  * table body
4650  * 
4651  */
4652
4653 /**
4654  * @class Roo.bootstrap.TableBody
4655  * @extends Roo.bootstrap.Component
4656  * Bootstrap TableBody class
4657  * @cfg {String} cls element class
4658  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4659  * @cfg {String} align Aligns the content inside the element
4660  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4661  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4662  * 
4663  * @constructor
4664  * Create a new TableBody
4665  * @param {Object} config The config object
4666  */
4667
4668 Roo.bootstrap.TableBody = function(config){
4669     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4670 };
4671
4672 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4673     
4674     cls: false,
4675     tag: false,
4676     align: false,
4677     charoff: false,
4678     valign: false,
4679     
4680     getAutoCreate : function(){
4681         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4682         
4683         cfg = {
4684             tag: 'tbody'
4685         }
4686             
4687         if (this.cls) {
4688             cfg.cls=this.cls
4689         }
4690         if(this.tag){
4691             cfg.tag = this.tag;
4692         }
4693         
4694         if(this.align){
4695             cfg.align = this.align;
4696         }
4697         if(this.charoff){
4698             cfg.charoff = this.charoff;
4699         }
4700         if(this.valign){
4701             cfg.valign = this.valign;
4702         }
4703         
4704         return cfg;
4705     }
4706     
4707     
4708 //    initEvents : function()
4709 //    {
4710 //        
4711 //        if(!this.store){
4712 //            return;
4713 //        }
4714 //        
4715 //        this.store = Roo.factory(this.store, Roo.data);
4716 //        this.store.on('load', this.onLoad, this);
4717 //        
4718 //        this.store.load();
4719 //        
4720 //    },
4721 //    
4722 //    onLoad: function () 
4723 //    {   
4724 //        this.fireEvent('load', this);
4725 //    }
4726 //    
4727 //   
4728 });
4729
4730  
4731
4732  /*
4733  * Based on:
4734  * Ext JS Library 1.1.1
4735  * Copyright(c) 2006-2007, Ext JS, LLC.
4736  *
4737  * Originally Released Under LGPL - original licence link has changed is not relivant.
4738  *
4739  * Fork - LGPL
4740  * <script type="text/javascript">
4741  */
4742
4743 // as we use this in bootstrap.
4744 Roo.namespace('Roo.form');
4745  /**
4746  * @class Roo.form.Action
4747  * Internal Class used to handle form actions
4748  * @constructor
4749  * @param {Roo.form.BasicForm} el The form element or its id
4750  * @param {Object} config Configuration options
4751  */
4752
4753  
4754  
4755 // define the action interface
4756 Roo.form.Action = function(form, options){
4757     this.form = form;
4758     this.options = options || {};
4759 };
4760 /**
4761  * Client Validation Failed
4762  * @const 
4763  */
4764 Roo.form.Action.CLIENT_INVALID = 'client';
4765 /**
4766  * Server Validation Failed
4767  * @const 
4768  */
4769 Roo.form.Action.SERVER_INVALID = 'server';
4770  /**
4771  * Connect to Server Failed
4772  * @const 
4773  */
4774 Roo.form.Action.CONNECT_FAILURE = 'connect';
4775 /**
4776  * Reading Data from Server Failed
4777  * @const 
4778  */
4779 Roo.form.Action.LOAD_FAILURE = 'load';
4780
4781 Roo.form.Action.prototype = {
4782     type : 'default',
4783     failureType : undefined,
4784     response : undefined,
4785     result : undefined,
4786
4787     // interface method
4788     run : function(options){
4789
4790     },
4791
4792     // interface method
4793     success : function(response){
4794
4795     },
4796
4797     // interface method
4798     handleResponse : function(response){
4799
4800     },
4801
4802     // default connection failure
4803     failure : function(response){
4804         
4805         this.response = response;
4806         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4807         this.form.afterAction(this, false);
4808     },
4809
4810     processResponse : function(response){
4811         this.response = response;
4812         if(!response.responseText){
4813             return true;
4814         }
4815         this.result = this.handleResponse(response);
4816         return this.result;
4817     },
4818
4819     // utility functions used internally
4820     getUrl : function(appendParams){
4821         var url = this.options.url || this.form.url || this.form.el.dom.action;
4822         if(appendParams){
4823             var p = this.getParams();
4824             if(p){
4825                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4826             }
4827         }
4828         return url;
4829     },
4830
4831     getMethod : function(){
4832         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4833     },
4834
4835     getParams : function(){
4836         var bp = this.form.baseParams;
4837         var p = this.options.params;
4838         if(p){
4839             if(typeof p == "object"){
4840                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4841             }else if(typeof p == 'string' && bp){
4842                 p += '&' + Roo.urlEncode(bp);
4843             }
4844         }else if(bp){
4845             p = Roo.urlEncode(bp);
4846         }
4847         return p;
4848     },
4849
4850     createCallback : function(){
4851         return {
4852             success: this.success,
4853             failure: this.failure,
4854             scope: this,
4855             timeout: (this.form.timeout*1000),
4856             upload: this.form.fileUpload ? this.success : undefined
4857         };
4858     }
4859 };
4860
4861 Roo.form.Action.Submit = function(form, options){
4862     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4863 };
4864
4865 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4866     type : 'submit',
4867
4868     haveProgress : false,
4869     uploadComplete : false,
4870     
4871     // uploadProgress indicator.
4872     uploadProgress : function()
4873     {
4874         if (!this.form.progressUrl) {
4875             return;
4876         }
4877         
4878         if (!this.haveProgress) {
4879             Roo.MessageBox.progress("Uploading", "Uploading");
4880         }
4881         if (this.uploadComplete) {
4882            Roo.MessageBox.hide();
4883            return;
4884         }
4885         
4886         this.haveProgress = true;
4887    
4888         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4889         
4890         var c = new Roo.data.Connection();
4891         c.request({
4892             url : this.form.progressUrl,
4893             params: {
4894                 id : uid
4895             },
4896             method: 'GET',
4897             success : function(req){
4898                //console.log(data);
4899                 var rdata = false;
4900                 var edata;
4901                 try  {
4902                    rdata = Roo.decode(req.responseText)
4903                 } catch (e) {
4904                     Roo.log("Invalid data from server..");
4905                     Roo.log(edata);
4906                     return;
4907                 }
4908                 if (!rdata || !rdata.success) {
4909                     Roo.log(rdata);
4910                     Roo.MessageBox.alert(Roo.encode(rdata));
4911                     return;
4912                 }
4913                 var data = rdata.data;
4914                 
4915                 if (this.uploadComplete) {
4916                    Roo.MessageBox.hide();
4917                    return;
4918                 }
4919                    
4920                 if (data){
4921                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4922                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4923                     );
4924                 }
4925                 this.uploadProgress.defer(2000,this);
4926             },
4927        
4928             failure: function(data) {
4929                 Roo.log('progress url failed ');
4930                 Roo.log(data);
4931             },
4932             scope : this
4933         });
4934            
4935     },
4936     
4937     
4938     run : function()
4939     {
4940         // run get Values on the form, so it syncs any secondary forms.
4941         this.form.getValues();
4942         
4943         var o = this.options;
4944         var method = this.getMethod();
4945         var isPost = method == 'POST';
4946         if(o.clientValidation === false || this.form.isValid()){
4947             
4948             if (this.form.progressUrl) {
4949                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4950                     (new Date() * 1) + '' + Math.random());
4951                     
4952             } 
4953             
4954             
4955             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4956                 form:this.form.el.dom,
4957                 url:this.getUrl(!isPost),
4958                 method: method,
4959                 params:isPost ? this.getParams() : null,
4960                 isUpload: this.form.fileUpload
4961             }));
4962             
4963             this.uploadProgress();
4964
4965         }else if (o.clientValidation !== false){ // client validation failed
4966             this.failureType = Roo.form.Action.CLIENT_INVALID;
4967             this.form.afterAction(this, false);
4968         }
4969     },
4970
4971     success : function(response)
4972     {
4973         this.uploadComplete= true;
4974         if (this.haveProgress) {
4975             Roo.MessageBox.hide();
4976         }
4977         
4978         
4979         var result = this.processResponse(response);
4980         if(result === true || result.success){
4981             this.form.afterAction(this, true);
4982             return;
4983         }
4984         if(result.errors){
4985             this.form.markInvalid(result.errors);
4986             this.failureType = Roo.form.Action.SERVER_INVALID;
4987         }
4988         this.form.afterAction(this, false);
4989     },
4990     failure : function(response)
4991     {
4992         this.uploadComplete= true;
4993         if (this.haveProgress) {
4994             Roo.MessageBox.hide();
4995         }
4996         
4997         this.response = response;
4998         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4999         this.form.afterAction(this, false);
5000     },
5001     
5002     handleResponse : function(response){
5003         if(this.form.errorReader){
5004             var rs = this.form.errorReader.read(response);
5005             var errors = [];
5006             if(rs.records){
5007                 for(var i = 0, len = rs.records.length; i < len; i++) {
5008                     var r = rs.records[i];
5009                     errors[i] = r.data;
5010                 }
5011             }
5012             if(errors.length < 1){
5013                 errors = null;
5014             }
5015             return {
5016                 success : rs.success,
5017                 errors : errors
5018             };
5019         }
5020         var ret = false;
5021         try {
5022             ret = Roo.decode(response.responseText);
5023         } catch (e) {
5024             ret = {
5025                 success: false,
5026                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5027                 errors : []
5028             };
5029         }
5030         return ret;
5031         
5032     }
5033 });
5034
5035
5036 Roo.form.Action.Load = function(form, options){
5037     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5038     this.reader = this.form.reader;
5039 };
5040
5041 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5042     type : 'load',
5043
5044     run : function(){
5045         
5046         Roo.Ajax.request(Roo.apply(
5047                 this.createCallback(), {
5048                     method:this.getMethod(),
5049                     url:this.getUrl(false),
5050                     params:this.getParams()
5051         }));
5052     },
5053
5054     success : function(response){
5055         
5056         var result = this.processResponse(response);
5057         if(result === true || !result.success || !result.data){
5058             this.failureType = Roo.form.Action.LOAD_FAILURE;
5059             this.form.afterAction(this, false);
5060             return;
5061         }
5062         this.form.clearInvalid();
5063         this.form.setValues(result.data);
5064         this.form.afterAction(this, true);
5065     },
5066
5067     handleResponse : function(response){
5068         if(this.form.reader){
5069             var rs = this.form.reader.read(response);
5070             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5071             return {
5072                 success : rs.success,
5073                 data : data
5074             };
5075         }
5076         return Roo.decode(response.responseText);
5077     }
5078 });
5079
5080 Roo.form.Action.ACTION_TYPES = {
5081     'load' : Roo.form.Action.Load,
5082     'submit' : Roo.form.Action.Submit
5083 };/*
5084  * - LGPL
5085  *
5086  * form
5087  * 
5088  */
5089
5090 /**
5091  * @class Roo.bootstrap.Form
5092  * @extends Roo.bootstrap.Component
5093  * Bootstrap Form class
5094  * @cfg {String} method  GET | POST (default POST)
5095  * @cfg {String} labelAlign top | left (default top)
5096   * @cfg {String} align left  | right - for navbars
5097
5098  * 
5099  * @constructor
5100  * Create a new Form
5101  * @param {Object} config The config object
5102  */
5103
5104
5105 Roo.bootstrap.Form = function(config){
5106     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5107     this.addEvents({
5108         /**
5109          * @event clientvalidation
5110          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5111          * @param {Form} this
5112          * @param {Boolean} valid true if the form has passed client-side validation
5113          */
5114         clientvalidation: true,
5115         /**
5116          * @event beforeaction
5117          * Fires before any action is performed. Return false to cancel the action.
5118          * @param {Form} this
5119          * @param {Action} action The action to be performed
5120          */
5121         beforeaction: true,
5122         /**
5123          * @event actionfailed
5124          * Fires when an action fails.
5125          * @param {Form} this
5126          * @param {Action} action The action that failed
5127          */
5128         actionfailed : true,
5129         /**
5130          * @event actioncomplete
5131          * Fires when an action is completed.
5132          * @param {Form} this
5133          * @param {Action} action The action that completed
5134          */
5135         actioncomplete : true
5136     });
5137     
5138 };
5139
5140 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5141       
5142      /**
5143      * @cfg {String} method
5144      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5145      */
5146     method : 'POST',
5147     /**
5148      * @cfg {String} url
5149      * The URL to use for form actions if one isn't supplied in the action options.
5150      */
5151     /**
5152      * @cfg {Boolean} fileUpload
5153      * Set to true if this form is a file upload.
5154      */
5155      
5156     /**
5157      * @cfg {Object} baseParams
5158      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5159      */
5160       
5161     /**
5162      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5163      */
5164     timeout: 30,
5165     /**
5166      * @cfg {Sting} align (left|right) for navbar forms
5167      */
5168     align : 'left',
5169
5170     // private
5171     activeAction : null,
5172  
5173     /**
5174      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5175      * element by passing it or its id or mask the form itself by passing in true.
5176      * @type Mixed
5177      */
5178     waitMsgTarget : false,
5179     
5180      
5181     
5182     /**
5183      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5184      * element by passing it or its id or mask the form itself by passing in true.
5185      * @type Mixed
5186      */
5187     
5188     getAutoCreate : function(){
5189         
5190         var cfg = {
5191             tag: 'form',
5192             method : this.method || 'POST',
5193             id : this.id || Roo.id(),
5194             cls : ''
5195         }
5196         if (this.parent().xtype.match(/^Nav/)) {
5197             cfg.cls = 'navbar-form navbar-' + this.align;
5198             
5199         }
5200         
5201         if (this.labelAlign == 'left' ) {
5202             cfg.cls += ' form-horizontal';
5203         }
5204         
5205         
5206         return cfg;
5207     },
5208     initEvents : function()
5209     {
5210         this.el.on('submit', this.onSubmit, this);
5211         
5212         
5213     },
5214     // private
5215     onSubmit : function(e){
5216         e.stopEvent();
5217     },
5218     
5219      /**
5220      * Returns true if client-side validation on the form is successful.
5221      * @return Boolean
5222      */
5223     isValid : function(){
5224         var items = this.getItems();
5225         var valid = true;
5226         items.each(function(f){
5227            if(!f.validate()){
5228                valid = false;
5229                
5230            }
5231         });
5232         return valid;
5233     },
5234     /**
5235      * Returns true if any fields in this form have changed since their original load.
5236      * @return Boolean
5237      */
5238     isDirty : function(){
5239         var dirty = false;
5240         var items = this.getItems();
5241         items.each(function(f){
5242            if(f.isDirty()){
5243                dirty = true;
5244                return false;
5245            }
5246            return true;
5247         });
5248         return dirty;
5249     },
5250      /**
5251      * Performs a predefined action (submit or load) or custom actions you define on this form.
5252      * @param {String} actionName The name of the action type
5253      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
5254      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5255      * accept other config options):
5256      * <pre>
5257 Property          Type             Description
5258 ----------------  ---------------  ----------------------------------------------------------------------------------
5259 url               String           The url for the action (defaults to the form's url)
5260 method            String           The form method to use (defaults to the form's method, or POST if not defined)
5261 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
5262 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
5263                                    validate the form on the client (defaults to false)
5264      * </pre>
5265      * @return {BasicForm} this
5266      */
5267     doAction : function(action, options){
5268         if(typeof action == 'string'){
5269             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5270         }
5271         if(this.fireEvent('beforeaction', this, action) !== false){
5272             this.beforeAction(action);
5273             action.run.defer(100, action);
5274         }
5275         return this;
5276     },
5277     
5278     // private
5279     beforeAction : function(action){
5280         var o = action.options;
5281         
5282         // not really supported yet.. ??
5283         
5284         //if(this.waitMsgTarget === true){
5285             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
5286         //}else if(this.waitMsgTarget){
5287         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
5288         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
5289         //}else {
5290         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
5291        // }
5292          
5293     },
5294
5295     // private
5296     afterAction : function(action, success){
5297         this.activeAction = null;
5298         var o = action.options;
5299         
5300         //if(this.waitMsgTarget === true){
5301             this.el.unmask();
5302         //}else if(this.waitMsgTarget){
5303         //    this.waitMsgTarget.unmask();
5304         //}else{
5305         //    Roo.MessageBox.updateProgress(1);
5306         //    Roo.MessageBox.hide();
5307        // }
5308         // 
5309         if(success){
5310             if(o.reset){
5311                 this.reset();
5312             }
5313             Roo.callback(o.success, o.scope, [this, action]);
5314             this.fireEvent('actioncomplete', this, action);
5315             
5316         }else{
5317             
5318             // failure condition..
5319             // we have a scenario where updates need confirming.
5320             // eg. if a locking scenario exists..
5321             // we look for { errors : { needs_confirm : true }} in the response.
5322             if (
5323                 (typeof(action.result) != 'undefined')  &&
5324                 (typeof(action.result.errors) != 'undefined')  &&
5325                 (typeof(action.result.errors.needs_confirm) != 'undefined')
5326            ){
5327                 var _t = this;
5328                 Roo.log("not supported yet");
5329                  /*
5330                 
5331                 Roo.MessageBox.confirm(
5332                     "Change requires confirmation",
5333                     action.result.errorMsg,
5334                     function(r) {
5335                         if (r != 'yes') {
5336                             return;
5337                         }
5338                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
5339                     }
5340                     
5341                 );
5342                 */
5343                 
5344                 
5345                 return;
5346             }
5347             
5348             Roo.callback(o.failure, o.scope, [this, action]);
5349             // show an error message if no failed handler is set..
5350             if (!this.hasListener('actionfailed')) {
5351                 Roo.log("need to add dialog support");
5352                 /*
5353                 Roo.MessageBox.alert("Error",
5354                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5355                         action.result.errorMsg :
5356                         "Saving Failed, please check your entries or try again"
5357                 );
5358                 */
5359             }
5360             
5361             this.fireEvent('actionfailed', this, action);
5362         }
5363         
5364     },
5365     /**
5366      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5367      * @param {String} id The value to search for
5368      * @return Field
5369      */
5370     findField : function(id){
5371         var items = this.getItems();
5372         var field = items.get(id);
5373         if(!field){
5374              items.each(function(f){
5375                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5376                     field = f;
5377                     return false;
5378                 }
5379                 return true;
5380             });
5381         }
5382         return field || null;
5383     },
5384      /**
5385      * Mark fields in this form invalid in bulk.
5386      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5387      * @return {BasicForm} this
5388      */
5389     markInvalid : function(errors){
5390         if(errors instanceof Array){
5391             for(var i = 0, len = errors.length; i < len; i++){
5392                 var fieldError = errors[i];
5393                 var f = this.findField(fieldError.id);
5394                 if(f){
5395                     f.markInvalid(fieldError.msg);
5396                 }
5397             }
5398         }else{
5399             var field, id;
5400             for(id in errors){
5401                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
5402                     field.markInvalid(errors[id]);
5403                 }
5404             }
5405         }
5406         //Roo.each(this.childForms || [], function (f) {
5407         //    f.markInvalid(errors);
5408         //});
5409         
5410         return this;
5411     },
5412
5413     /**
5414      * Set values for fields in this form in bulk.
5415      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
5416      * @return {BasicForm} this
5417      */
5418     setValues : function(values){
5419         if(values instanceof Array){ // array of objects
5420             for(var i = 0, len = values.length; i < len; i++){
5421                 var v = values[i];
5422                 var f = this.findField(v.id);
5423                 if(f){
5424                     f.setValue(v.value);
5425                     if(this.trackResetOnLoad){
5426                         f.originalValue = f.getValue();
5427                     }
5428                 }
5429             }
5430         }else{ // object hash
5431             var field, id;
5432             for(id in values){
5433                 if(typeof values[id] != 'function' && (field = this.findField(id))){
5434                     
5435                     if (field.setFromData && 
5436                         field.valueField && 
5437                         field.displayField &&
5438                         // combos' with local stores can 
5439                         // be queried via setValue()
5440                         // to set their value..
5441                         (field.store && !field.store.isLocal)
5442                         ) {
5443                         // it's a combo
5444                         var sd = { };
5445                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
5446                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
5447                         field.setFromData(sd);
5448                         
5449                     } else {
5450                         field.setValue(values[id]);
5451                     }
5452                     
5453                     
5454                     if(this.trackResetOnLoad){
5455                         field.originalValue = field.getValue();
5456                     }
5457                 }
5458             }
5459         }
5460          
5461         //Roo.each(this.childForms || [], function (f) {
5462         //    f.setValues(values);
5463         //});
5464                 
5465         return this;
5466     },
5467
5468     /**
5469      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
5470      * they are returned as an array.
5471      * @param {Boolean} asString
5472      * @return {Object}
5473      */
5474     getValues : function(asString){
5475         //if (this.childForms) {
5476             // copy values from the child forms
5477         //    Roo.each(this.childForms, function (f) {
5478         //        this.setValues(f.getValues());
5479         //    }, this);
5480         //}
5481         
5482         
5483         
5484         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
5485         if(asString === true){
5486             return fs;
5487         }
5488         return Roo.urlDecode(fs);
5489     },
5490     
5491     /**
5492      * Returns the fields in this form as an object with key/value pairs. 
5493      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
5494      * @return {Object}
5495      */
5496     getFieldValues : function(with_hidden)
5497     {
5498         var items = this.getItems();
5499         var ret = {};
5500         items.each(function(f){
5501             if (!f.getName()) {
5502                 return;
5503             }
5504             var v = f.getValue();
5505             if (f.inputType =='radio') {
5506                 if (typeof(ret[f.getName()]) == 'undefined') {
5507                     ret[f.getName()] = ''; // empty..
5508                 }
5509                 
5510                 if (!f.el.dom.checked) {
5511                     return;
5512                     
5513                 }
5514                 v = f.el.dom.value;
5515                 
5516             }
5517             
5518             // not sure if this supported any more..
5519             if ((typeof(v) == 'object') && f.getRawValue) {
5520                 v = f.getRawValue() ; // dates..
5521             }
5522             // combo boxes where name != hiddenName...
5523             if (f.name != f.getName()) {
5524                 ret[f.name] = f.getRawValue();
5525             }
5526             ret[f.getName()] = v;
5527         });
5528         
5529         return ret;
5530     },
5531
5532     /**
5533      * Clears all invalid messages in this form.
5534      * @return {BasicForm} this
5535      */
5536     clearInvalid : function(){
5537         var items = this.getItems();
5538         
5539         items.each(function(f){
5540            f.clearInvalid();
5541         });
5542         
5543         
5544         
5545         return this;
5546     },
5547
5548     /**
5549      * Resets this form.
5550      * @return {BasicForm} this
5551      */
5552     reset : function(){
5553         var items = this.getItems();
5554         items.each(function(f){
5555             f.reset();
5556         });
5557         
5558         Roo.each(this.childForms || [], function (f) {
5559             f.reset();
5560         });
5561        
5562         
5563         return this;
5564     },
5565     getItems : function()
5566     {
5567         var r=new Roo.util.MixedCollection(false, function(o){
5568             return o.id || (o.id = Roo.id());
5569         });
5570         var iter = function(el) {
5571             if (el.inputEl) {
5572                 r.add(el);
5573             }
5574             if (!el.items) {
5575                 return;
5576             }
5577             Roo.each(el.items,function(e) {
5578                 iter(e);
5579             });
5580             
5581             
5582         };
5583         iter(this);
5584         return r;
5585         
5586         
5587         
5588         
5589     }
5590     
5591 });
5592
5593  
5594 /*
5595  * Based on:
5596  * Ext JS Library 1.1.1
5597  * Copyright(c) 2006-2007, Ext JS, LLC.
5598  *
5599  * Originally Released Under LGPL - original licence link has changed is not relivant.
5600  *
5601  * Fork - LGPL
5602  * <script type="text/javascript">
5603  */
5604 /**
5605  * @class Roo.form.VTypes
5606  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5607  * @singleton
5608  */
5609 Roo.form.VTypes = function(){
5610     // closure these in so they are only created once.
5611     var alpha = /^[a-zA-Z_]+$/;
5612     var alphanum = /^[a-zA-Z0-9_]+$/;
5613     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5614     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5615
5616     // All these messages and functions are configurable
5617     return {
5618         /**
5619          * The function used to validate email addresses
5620          * @param {String} value The email address
5621          */
5622         'email' : function(v){
5623             return email.test(v);
5624         },
5625         /**
5626          * The error text to display when the email validation function returns false
5627          * @type String
5628          */
5629         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5630         /**
5631          * The keystroke filter mask to be applied on email input
5632          * @type RegExp
5633          */
5634         'emailMask' : /[a-z0-9_\.\-@]/i,
5635
5636         /**
5637          * The function used to validate URLs
5638          * @param {String} value The URL
5639          */
5640         'url' : function(v){
5641             return url.test(v);
5642         },
5643         /**
5644          * The error text to display when the url validation function returns false
5645          * @type String
5646          */
5647         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5648         
5649         /**
5650          * The function used to validate alpha values
5651          * @param {String} value The value
5652          */
5653         'alpha' : function(v){
5654             return alpha.test(v);
5655         },
5656         /**
5657          * The error text to display when the alpha validation function returns false
5658          * @type String
5659          */
5660         'alphaText' : 'This field should only contain letters and _',
5661         /**
5662          * The keystroke filter mask to be applied on alpha input
5663          * @type RegExp
5664          */
5665         'alphaMask' : /[a-z_]/i,
5666
5667         /**
5668          * The function used to validate alphanumeric values
5669          * @param {String} value The value
5670          */
5671         'alphanum' : function(v){
5672             return alphanum.test(v);
5673         },
5674         /**
5675          * The error text to display when the alphanumeric validation function returns false
5676          * @type String
5677          */
5678         'alphanumText' : 'This field should only contain letters, numbers and _',
5679         /**
5680          * The keystroke filter mask to be applied on alphanumeric input
5681          * @type RegExp
5682          */
5683         'alphanumMask' : /[a-z0-9_]/i
5684     };
5685 }();/*
5686  * - LGPL
5687  *
5688  * Input
5689  * 
5690  */
5691
5692 /**
5693  * @class Roo.bootstrap.Input
5694  * @extends Roo.bootstrap.Component
5695  * Bootstrap Input class
5696  * @cfg {Boolean} disabled is it disabled
5697  * @cfg {String} fieldLabel - the label associated
5698  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5699  * @cfg {String} name name of the input
5700  * @cfg {string} fieldLabel - the label associated
5701  * @cfg {string}  inputType - input / file submit ...
5702  * @cfg {string} placeholder - placeholder to put in text.
5703  * @cfg {string}  before - input group add on before
5704  * @cfg {string} after - input group add on after
5705  * @cfg {string} size - (lg|sm) or leave empty..
5706  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5707  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5708  * @cfg {Number} md colspan out of 12 for computer-sized screens
5709  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5710  * @cfg {string} value default value of the input
5711  * @cfg {Number} labelWidth set the width of label (0-12)
5712  * @cfg {String} labelAlign (top|left)
5713  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5714  * 
5715  * 
5716  * @constructor
5717  * Create a new Input
5718  * @param {Object} config The config object
5719  */
5720
5721 Roo.bootstrap.Input = function(config){
5722     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5723    
5724         this.addEvents({
5725             /**
5726              * @event focus
5727              * Fires when this field receives input focus.
5728              * @param {Roo.form.Field} this
5729              */
5730             focus : true,
5731             /**
5732              * @event blur
5733              * Fires when this field loses input focus.
5734              * @param {Roo.form.Field} this
5735              */
5736             blur : true,
5737             /**
5738              * @event specialkey
5739              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5740              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5741              * @param {Roo.form.Field} this
5742              * @param {Roo.EventObject} e The event object
5743              */
5744             specialkey : true,
5745             /**
5746              * @event change
5747              * Fires just before the field blurs if the field value has changed.
5748              * @param {Roo.form.Field} this
5749              * @param {Mixed} newValue The new value
5750              * @param {Mixed} oldValue The original value
5751              */
5752             change : true,
5753             /**
5754              * @event invalid
5755              * Fires after the field has been marked as invalid.
5756              * @param {Roo.form.Field} this
5757              * @param {String} msg The validation message
5758              */
5759             invalid : true,
5760             /**
5761              * @event valid
5762              * Fires after the field has been validated with no errors.
5763              * @param {Roo.form.Field} this
5764              */
5765             valid : true,
5766              /**
5767              * @event keyup
5768              * Fires after the key up
5769              * @param {Roo.form.Field} this
5770              * @param {Roo.EventObject}  e The event Object
5771              */
5772             keyup : true
5773         });
5774 };
5775
5776 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5777      /**
5778      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5779       automatic validation (defaults to "keyup").
5780      */
5781     validationEvent : "keyup",
5782      /**
5783      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5784      */
5785     validateOnBlur : true,
5786     /**
5787      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5788      */
5789     validationDelay : 250,
5790      /**
5791      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5792      */
5793     focusClass : "x-form-focus",  // not needed???
5794     
5795        
5796     /**
5797      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5798      */
5799     invalidClass : "has-error",
5800     
5801     /**
5802      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5803      */
5804     selectOnFocus : false,
5805     
5806      /**
5807      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5808      */
5809     maskRe : null,
5810        /**
5811      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5812      */
5813     vtype : null,
5814     
5815       /**
5816      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5817      */
5818     disableKeyFilter : false,
5819     
5820        /**
5821      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5822      */
5823     disabled : false,
5824      /**
5825      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5826      */
5827     allowBlank : true,
5828     /**
5829      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5830      */
5831     blankText : "This field is required",
5832     
5833      /**
5834      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5835      */
5836     minLength : 0,
5837     /**
5838      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5839      */
5840     maxLength : Number.MAX_VALUE,
5841     /**
5842      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5843      */
5844     minLengthText : "The minimum length for this field is {0}",
5845     /**
5846      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5847      */
5848     maxLengthText : "The maximum length for this field is {0}",
5849   
5850     
5851     /**
5852      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5853      * If available, this function will be called only after the basic validators all return true, and will be passed the
5854      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5855      */
5856     validator : null,
5857     /**
5858      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5859      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5860      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5861      */
5862     regex : null,
5863     /**
5864      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5865      */
5866     regexText : "",
5867     
5868     
5869     
5870     fieldLabel : '',
5871     inputType : 'text',
5872     
5873     name : false,
5874     placeholder: false,
5875     before : false,
5876     after : false,
5877     size : false,
5878     // private
5879     hasFocus : false,
5880     preventMark: false,
5881     isFormField : true,
5882     value : '',
5883     labelWidth : 2,
5884     labelAlign : false,
5885     readOnly : false,
5886     
5887     parentLabelAlign : function()
5888     {
5889         var parent = this;
5890         while (parent.parent()) {
5891             parent = parent.parent();
5892             if (typeof(parent.labelAlign) !='undefined') {
5893                 return parent.labelAlign;
5894             }
5895         }
5896         return 'left';
5897         
5898     },
5899     
5900     getAutoCreate : function(){
5901         
5902         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5903         
5904         var id = Roo.id();
5905         
5906         var cfg = {};
5907         
5908         if(this.inputType != 'hidden'){
5909             cfg.cls = 'form-group' //input-group
5910         }
5911         
5912         var input =  {
5913             tag: 'input',
5914             id : id,
5915             type : this.inputType,
5916             value : this.value,
5917             cls : 'form-control',
5918             placeholder : this.placeholder || ''
5919             
5920         };
5921         
5922         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5923             input.maxLength = this.maxLength;
5924         }
5925         
5926         if (this.disabled) {
5927             input.disabled=true;
5928         }
5929         
5930         if (this.readOnly) {
5931             input.readonly=true;
5932         }
5933         
5934         if (this.name) {
5935             input.name = this.name;
5936         }
5937         if (this.size) {
5938             input.cls += ' input-' + this.size;
5939         }
5940         var settings=this;
5941         ['xs','sm','md','lg'].map(function(size){
5942             if (settings[size]) {
5943                 cfg.cls += ' col-' + size + '-' + settings[size];
5944             }
5945         });
5946         
5947         var inputblock = input;
5948         
5949         if (this.before || this.after) {
5950             
5951             inputblock = {
5952                 cls : 'input-group',
5953                 cn :  [] 
5954             };
5955             if (this.before && typeof(this.before) == 'string') {
5956                 
5957                 inputblock.cn.push({
5958                     tag :'span',
5959                     cls : 'roo-input-before input-group-addon',
5960                     html : this.before
5961                 });
5962             }
5963             if (this.before && typeof(this.before) == 'object') {
5964                 this.before = Roo.factory(this.before);
5965                 Roo.log(this.before);
5966                 inputblock.cn.push({
5967                     tag :'span',
5968                     cls : 'roo-input-before input-group-' +
5969                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5970                 });
5971             }
5972             
5973             inputblock.cn.push(input);
5974             
5975             if (this.after && typeof(this.after) == 'string') {
5976                 inputblock.cn.push({
5977                     tag :'span',
5978                     cls : 'roo-input-after input-group-addon',
5979                     html : this.after
5980                 });
5981             }
5982             if (this.after && typeof(this.after) == 'object') {
5983                 this.after = Roo.factory(this.after);
5984                 Roo.log(this.after);
5985                 inputblock.cn.push({
5986                     tag :'span',
5987                     cls : 'roo-input-after input-group-' +
5988                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5989                 });
5990             }
5991         };
5992         
5993         if (align ==='left' && this.fieldLabel.length) {
5994                 Roo.log("left and has label");
5995                 cfg.cn = [
5996                     
5997                     {
5998                         tag: 'label',
5999                         'for' :  id,
6000                         cls : 'control-label col-sm-' + this.labelWidth,
6001                         html : this.fieldLabel
6002                         
6003                     },
6004                     {
6005                         cls : "col-sm-" + (12 - this.labelWidth), 
6006                         cn: [
6007                             inputblock
6008                         ]
6009                     }
6010                     
6011                 ];
6012         } else if ( this.fieldLabel.length) {
6013                 Roo.log(" label");
6014                  cfg.cn = [
6015                    
6016                     {
6017                         tag: 'label',
6018                         //cls : 'input-group-addon',
6019                         html : this.fieldLabel
6020                         
6021                     },
6022                     
6023                     inputblock
6024                     
6025                 ];
6026
6027         } else {
6028             
6029                 Roo.log(" no label && no align");
6030                 cfg.cn = [
6031                     
6032                         inputblock
6033                     
6034                 ];
6035                 
6036                 
6037         };
6038         Roo.log('input-parentType: ' + this.parentType);
6039         
6040         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6041            cfg.cls += ' navbar-form';
6042            Roo.log(cfg);
6043         }
6044         
6045         return cfg;
6046         
6047     },
6048     /**
6049      * return the real input element.
6050      */
6051     inputEl: function ()
6052     {
6053         return this.el.select('input.form-control',true).first();
6054     },
6055     setDisabled : function(v)
6056     {
6057         var i  = this.inputEl().dom;
6058         if (!v) {
6059             i.removeAttribute('disabled');
6060             return;
6061             
6062         }
6063         i.setAttribute('disabled','true');
6064     },
6065     initEvents : function()
6066     {
6067         
6068         this.inputEl().on("keydown" , this.fireKey,  this);
6069         this.inputEl().on("focus", this.onFocus,  this);
6070         this.inputEl().on("blur", this.onBlur,  this);
6071         
6072         this.inputEl().relayEvent('keyup', this);
6073
6074         // reference to original value for reset
6075         this.originalValue = this.getValue();
6076         //Roo.form.TextField.superclass.initEvents.call(this);
6077         if(this.validationEvent == 'keyup'){
6078             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6079             this.inputEl().on('keyup', this.filterValidation, this);
6080         }
6081         else if(this.validationEvent !== false){
6082             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6083         }
6084         
6085         if(this.selectOnFocus){
6086             this.on("focus", this.preFocus, this);
6087             
6088         }
6089         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6090             this.inputEl().on("keypress", this.filterKeys, this);
6091         }
6092        /* if(this.grow){
6093             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6094             this.el.on("click", this.autoSize,  this);
6095         }
6096         */
6097         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6098             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6099         }
6100         
6101         if (typeof(this.before) == 'object') {
6102             this.before.render(this.el.select('.roo-input-before',true).first());
6103         }
6104         if (typeof(this.after) == 'object') {
6105             this.after.render(this.el.select('.roo-input-after',true).first());
6106         }
6107         
6108         
6109     },
6110     filterValidation : function(e){
6111         if(!e.isNavKeyPress()){
6112             this.validationTask.delay(this.validationDelay);
6113         }
6114     },
6115      /**
6116      * Validates the field value
6117      * @return {Boolean} True if the value is valid, else false
6118      */
6119     validate : function(){
6120         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6121         if(this.disabled || this.validateValue(this.getRawValue())){
6122             this.clearInvalid();
6123             return true;
6124         }
6125         return false;
6126     },
6127     
6128     
6129     /**
6130      * Validates a value according to the field's validation rules and marks the field as invalid
6131      * if the validation fails
6132      * @param {Mixed} value The value to validate
6133      * @return {Boolean} True if the value is valid, else false
6134      */
6135     validateValue : function(value){
6136         if(value.length < 1)  { // if it's blank
6137              if(this.allowBlank){
6138                 this.clearInvalid();
6139                 return true;
6140              }else{
6141                 this.markInvalid(this.blankText);
6142                 return false;
6143              }
6144         }
6145         if(value.length < this.minLength){
6146             this.markInvalid(String.format(this.minLengthText, this.minLength));
6147             return false;
6148         }
6149         if(value.length > this.maxLength){
6150             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6151             return false;
6152         }
6153         if(this.vtype){
6154             var vt = Roo.form.VTypes;
6155             if(!vt[this.vtype](value, this)){
6156                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6157                 return false;
6158             }
6159         }
6160         if(typeof this.validator == "function"){
6161             var msg = this.validator(value);
6162             if(msg !== true){
6163                 this.markInvalid(msg);
6164                 return false;
6165             }
6166         }
6167         if(this.regex && !this.regex.test(value)){
6168             this.markInvalid(this.regexText);
6169             return false;
6170         }
6171         return true;
6172     },
6173
6174     
6175     
6176      // private
6177     fireKey : function(e){
6178         //Roo.log('field ' + e.getKey());
6179         if(e.isNavKeyPress()){
6180             this.fireEvent("specialkey", this, e);
6181         }
6182     },
6183     focus : function (selectText){
6184         if(this.rendered){
6185             this.inputEl().focus();
6186             if(selectText === true){
6187                 this.inputEl().dom.select();
6188             }
6189         }
6190         return this;
6191     } ,
6192     
6193     onFocus : function(){
6194         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6195            // this.el.addClass(this.focusClass);
6196         }
6197         if(!this.hasFocus){
6198             this.hasFocus = true;
6199             this.startValue = this.getValue();
6200             this.fireEvent("focus", this);
6201         }
6202     },
6203     
6204     beforeBlur : Roo.emptyFn,
6205
6206     
6207     // private
6208     onBlur : function(){
6209         this.beforeBlur();
6210         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6211             //this.el.removeClass(this.focusClass);
6212         }
6213         this.hasFocus = false;
6214         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6215             this.validate();
6216         }
6217         var v = this.getValue();
6218         if(String(v) !== String(this.startValue)){
6219             this.fireEvent('change', this, v, this.startValue);
6220         }
6221         this.fireEvent("blur", this);
6222     },
6223     
6224     /**
6225      * Resets the current field value to the originally loaded value and clears any validation messages
6226      */
6227     reset : function(){
6228         this.setValue(this.originalValue);
6229         this.clearInvalid();
6230     },
6231      /**
6232      * Returns the name of the field
6233      * @return {Mixed} name The name field
6234      */
6235     getName: function(){
6236         return this.name;
6237     },
6238      /**
6239      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
6240      * @return {Mixed} value The field value
6241      */
6242     getValue : function(){
6243         return this.inputEl().getValue();
6244     },
6245     /**
6246      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
6247      * @return {Mixed} value The field value
6248      */
6249     getRawValue : function(){
6250         var v = this.inputEl().getValue();
6251         
6252         return v;
6253     },
6254     
6255     /**
6256      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
6257      * @param {Mixed} value The value to set
6258      */
6259     setRawValue : function(v){
6260         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6261     },
6262     
6263     selectText : function(start, end){
6264         var v = this.getRawValue();
6265         if(v.length > 0){
6266             start = start === undefined ? 0 : start;
6267             end = end === undefined ? v.length : end;
6268             var d = this.inputEl().dom;
6269             if(d.setSelectionRange){
6270                 d.setSelectionRange(start, end);
6271             }else if(d.createTextRange){
6272                 var range = d.createTextRange();
6273                 range.moveStart("character", start);
6274                 range.moveEnd("character", v.length-end);
6275                 range.select();
6276             }
6277         }
6278     },
6279     
6280     /**
6281      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
6282      * @param {Mixed} value The value to set
6283      */
6284     setValue : function(v){
6285         this.value = v;
6286         if(this.rendered){
6287             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6288             this.validate();
6289         }
6290     },
6291     
6292     /*
6293     processValue : function(value){
6294         if(this.stripCharsRe){
6295             var newValue = value.replace(this.stripCharsRe, '');
6296             if(newValue !== value){
6297                 this.setRawValue(newValue);
6298                 return newValue;
6299             }
6300         }
6301         return value;
6302     },
6303   */
6304     preFocus : function(){
6305         
6306         if(this.selectOnFocus){
6307             this.inputEl().dom.select();
6308         }
6309     },
6310     filterKeys : function(e){
6311         var k = e.getKey();
6312         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
6313             return;
6314         }
6315         var c = e.getCharCode(), cc = String.fromCharCode(c);
6316         if(Roo.isIE && (e.isSpecialKey() || !cc)){
6317             return;
6318         }
6319         if(!this.maskRe.test(cc)){
6320             e.stopEvent();
6321         }
6322     },
6323      /**
6324      * Clear any invalid styles/messages for this field
6325      */
6326     clearInvalid : function(){
6327         
6328         if(!this.el || this.preventMark){ // not rendered
6329             return;
6330         }
6331         this.el.removeClass(this.invalidClass);
6332         /*
6333         switch(this.msgTarget){
6334             case 'qtip':
6335                 this.el.dom.qtip = '';
6336                 break;
6337             case 'title':
6338                 this.el.dom.title = '';
6339                 break;
6340             case 'under':
6341                 if(this.errorEl){
6342                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
6343                 }
6344                 break;
6345             case 'side':
6346                 if(this.errorIcon){
6347                     this.errorIcon.dom.qtip = '';
6348                     this.errorIcon.hide();
6349                     this.un('resize', this.alignErrorIcon, this);
6350                 }
6351                 break;
6352             default:
6353                 var t = Roo.getDom(this.msgTarget);
6354                 t.innerHTML = '';
6355                 t.style.display = 'none';
6356                 break;
6357         }
6358         */
6359         this.fireEvent('valid', this);
6360     },
6361      /**
6362      * Mark this field as invalid
6363      * @param {String} msg The validation message
6364      */
6365     markInvalid : function(msg){
6366         if(!this.el  || this.preventMark){ // not rendered
6367             return;
6368         }
6369         this.el.addClass(this.invalidClass);
6370         /*
6371         msg = msg || this.invalidText;
6372         switch(this.msgTarget){
6373             case 'qtip':
6374                 this.el.dom.qtip = msg;
6375                 this.el.dom.qclass = 'x-form-invalid-tip';
6376                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6377                     Roo.QuickTips.enable();
6378                 }
6379                 break;
6380             case 'title':
6381                 this.el.dom.title = msg;
6382                 break;
6383             case 'under':
6384                 if(!this.errorEl){
6385                     var elp = this.el.findParent('.x-form-element', 5, true);
6386                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
6387                     this.errorEl.setWidth(elp.getWidth(true)-20);
6388                 }
6389                 this.errorEl.update(msg);
6390                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
6391                 break;
6392             case 'side':
6393                 if(!this.errorIcon){
6394                     var elp = this.el.findParent('.x-form-element', 5, true);
6395                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
6396                 }
6397                 this.alignErrorIcon();
6398                 this.errorIcon.dom.qtip = msg;
6399                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
6400                 this.errorIcon.show();
6401                 this.on('resize', this.alignErrorIcon, this);
6402                 break;
6403             default:
6404                 var t = Roo.getDom(this.msgTarget);
6405                 t.innerHTML = msg;
6406                 t.style.display = this.msgDisplay;
6407                 break;
6408         }
6409         */
6410         this.fireEvent('invalid', this, msg);
6411     },
6412     // private
6413     SafariOnKeyDown : function(event)
6414     {
6415         // this is a workaround for a password hang bug on chrome/ webkit.
6416         
6417         var isSelectAll = false;
6418         
6419         if(this.inputEl().dom.selectionEnd > 0){
6420             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
6421         }
6422         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
6423             event.preventDefault();
6424             this.setValue('');
6425             return;
6426         }
6427         
6428         if(isSelectAll){ // backspace and delete key
6429             
6430             event.preventDefault();
6431             // this is very hacky as keydown always get's upper case.
6432             //
6433             var cc = String.fromCharCode(event.getCharCode());
6434             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
6435             
6436         }
6437     },
6438     adjustWidth : function(tag, w){
6439         tag = tag.toLowerCase();
6440         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
6441             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
6442                 if(tag == 'input'){
6443                     return w + 2;
6444                 }
6445                 if(tag == 'textarea'){
6446                     return w-2;
6447                 }
6448             }else if(Roo.isOpera){
6449                 if(tag == 'input'){
6450                     return w + 2;
6451                 }
6452                 if(tag == 'textarea'){
6453                     return w-2;
6454                 }
6455             }
6456         }
6457         return w;
6458     }
6459     
6460 });
6461
6462  
6463 /*
6464  * - LGPL
6465  *
6466  * Input
6467  * 
6468  */
6469
6470 /**
6471  * @class Roo.bootstrap.TextArea
6472  * @extends Roo.bootstrap.Input
6473  * Bootstrap TextArea class
6474  * @cfg {Number} cols Specifies the visible width of a text area
6475  * @cfg {Number} rows Specifies the visible number of lines in a text area
6476  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
6477  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
6478  * @cfg {string} html text
6479  * 
6480  * @constructor
6481  * Create a new TextArea
6482  * @param {Object} config The config object
6483  */
6484
6485 Roo.bootstrap.TextArea = function(config){
6486     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
6487    
6488 };
6489
6490 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
6491      
6492     cols : false,
6493     rows : 5,
6494     readOnly : false,
6495     warp : 'soft',
6496     resize : false,
6497     value: false,
6498     html: false,
6499     
6500     getAutoCreate : function(){
6501         
6502         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6503         
6504         var id = Roo.id();
6505         
6506         var cfg = {};
6507         
6508         var input =  {
6509             tag: 'textarea',
6510             id : id,
6511             warp : this.warp,
6512             rows : this.rows,
6513             value : this.value || '',
6514             html: this.html || '',
6515             cls : 'form-control',
6516             placeholder : this.placeholder || '' 
6517             
6518         };
6519         
6520         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6521             input.maxLength = this.maxLength;
6522         }
6523         
6524         if(this.resize){
6525             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
6526         }
6527         
6528         if(this.cols){
6529             input.cols = this.cols;
6530         }
6531         
6532         if (this.readOnly) {
6533             input.readonly = true;
6534         }
6535         
6536         if (this.name) {
6537             input.name = this.name;
6538         }
6539         
6540         if (this.size) {
6541             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
6542         }
6543         
6544         var settings=this;
6545         ['xs','sm','md','lg'].map(function(size){
6546             if (settings[size]) {
6547                 cfg.cls += ' col-' + size + '-' + settings[size];
6548             }
6549         });
6550         
6551         var inputblock = input;
6552         
6553         if (this.before || this.after) {
6554             
6555             inputblock = {
6556                 cls : 'input-group',
6557                 cn :  [] 
6558             };
6559             if (this.before) {
6560                 inputblock.cn.push({
6561                     tag :'span',
6562                     cls : 'input-group-addon',
6563                     html : this.before
6564                 });
6565             }
6566             inputblock.cn.push(input);
6567             if (this.after) {
6568                 inputblock.cn.push({
6569                     tag :'span',
6570                     cls : 'input-group-addon',
6571                     html : this.after
6572                 });
6573             }
6574             
6575         }
6576         
6577         if (align ==='left' && this.fieldLabel.length) {
6578                 Roo.log("left and has label");
6579                 cfg.cn = [
6580                     
6581                     {
6582                         tag: 'label',
6583                         'for' :  id,
6584                         cls : 'control-label col-sm-' + this.labelWidth,
6585                         html : this.fieldLabel
6586                         
6587                     },
6588                     {
6589                         cls : "col-sm-" + (12 - this.labelWidth), 
6590                         cn: [
6591                             inputblock
6592                         ]
6593                     }
6594                     
6595                 ];
6596         } else if ( this.fieldLabel.length) {
6597                 Roo.log(" label");
6598                  cfg.cn = [
6599                    
6600                     {
6601                         tag: 'label',
6602                         //cls : 'input-group-addon',
6603                         html : this.fieldLabel
6604                         
6605                     },
6606                     
6607                     inputblock
6608                     
6609                 ];
6610
6611         } else {
6612             
6613                    Roo.log(" no label && no align");
6614                 cfg.cn = [
6615                     
6616                         inputblock
6617                     
6618                 ];
6619                 
6620                 
6621         }
6622         
6623         if (this.disabled) {
6624             input.disabled=true;
6625         }
6626         
6627         return cfg;
6628         
6629     },
6630     /**
6631      * return the real textarea element.
6632      */
6633     inputEl: function ()
6634     {
6635         return this.el.select('textarea.form-control',true).first();
6636     }
6637 });
6638
6639  
6640 /*
6641  * - LGPL
6642  *
6643  * trigger field - base class for combo..
6644  * 
6645  */
6646  
6647 /**
6648  * @class Roo.bootstrap.TriggerField
6649  * @extends Roo.bootstrap.Input
6650  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6651  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6652  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6653  * for which you can provide a custom implementation.  For example:
6654  * <pre><code>
6655 var trigger = new Roo.bootstrap.TriggerField();
6656 trigger.onTriggerClick = myTriggerFn;
6657 trigger.applyTo('my-field');
6658 </code></pre>
6659  *
6660  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6661  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6662  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6663  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6664  * @constructor
6665  * Create a new TriggerField.
6666  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6667  * to the base TextField)
6668  */
6669 Roo.bootstrap.TriggerField = function(config){
6670     this.mimicing = false;
6671     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6672 };
6673
6674 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6675     /**
6676      * @cfg {String} triggerClass A CSS class to apply to the trigger
6677      */
6678      /**
6679      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6680      */
6681     hideTrigger:false,
6682
6683     /** @cfg {Boolean} grow @hide */
6684     /** @cfg {Number} growMin @hide */
6685     /** @cfg {Number} growMax @hide */
6686
6687     /**
6688      * @hide 
6689      * @method
6690      */
6691     autoSize: Roo.emptyFn,
6692     // private
6693     monitorTab : true,
6694     // private
6695     deferHeight : true,
6696
6697     
6698     actionMode : 'wrap',
6699     
6700     
6701     
6702     getAutoCreate : function(){
6703        
6704         var parent = this.parent();
6705         
6706         var align = this.labelAlign || this.parentLabelAlign();
6707         
6708         var id = Roo.id();
6709         
6710         var cfg = {
6711             cls: 'form-group' //input-group
6712         };
6713         
6714         
6715         var input =  {
6716             tag: 'input',
6717             id : id,
6718             type : this.inputType,
6719             cls : 'form-control',
6720             autocomplete: 'off',
6721             placeholder : this.placeholder || '' 
6722             
6723         };
6724         if (this.name) {
6725             input.name = this.name;
6726         }
6727         if (this.size) {
6728             input.cls += ' input-' + this.size;
6729         }
6730         
6731         if (this.disabled) {
6732             input.disabled=true;
6733         }
6734         
6735         var inputblock = input;
6736         
6737         if (this.before || this.after) {
6738             
6739             inputblock = {
6740                 cls : 'input-group',
6741                 cn :  [] 
6742             };
6743             if (this.before) {
6744                 inputblock.cn.push({
6745                     tag :'span',
6746                     cls : 'input-group-addon',
6747                     html : this.before
6748                 });
6749             }
6750             inputblock.cn.push(input);
6751             if (this.after) {
6752                 inputblock.cn.push({
6753                     tag :'span',
6754                     cls : 'input-group-addon',
6755                     html : this.after
6756                 });
6757             }
6758             
6759         };
6760         
6761         var box = {
6762             tag: 'div',
6763             cn: [
6764                 {
6765                     tag: 'input',
6766                     type : 'hidden',
6767                     cls: 'form-hidden-field'
6768                 },
6769                 inputblock
6770             ]
6771             
6772         };
6773         
6774         if(this.multiple){
6775             Roo.log('multiple');
6776             
6777             box = {
6778                 tag: 'div',
6779                 cn: [
6780                     {
6781                         tag: 'input',
6782                         type : 'hidden',
6783                         cls: 'form-hidden-field'
6784                     },
6785                     {
6786                         tag: 'ul',
6787                         cls: 'select2-choices',
6788                         cn:[
6789                             {
6790                                 tag: 'li',
6791                                 cls: 'select2-search-field',
6792                                 cn: [
6793
6794                                     inputblock
6795                                 ]
6796                             }
6797                         ]
6798                     }
6799                 ]
6800             }
6801         };
6802         
6803         var combobox = {
6804             cls: 'select2-container input-group',
6805             cn: [
6806                 box,
6807                 {
6808                     tag: 'ul',
6809                     cls: 'typeahead typeahead-long dropdown-menu',
6810                     style: 'display:none'
6811                 }
6812             ]
6813         };
6814         
6815         if(!this.multiple){
6816             combobox.cn.push({
6817                 tag :'span',
6818                 cls : 'input-group-addon btn dropdown-toggle',
6819                 cn : [
6820                     {
6821                         tag: 'span',
6822                         cls: 'caret'
6823                     },
6824                     {
6825                         tag: 'span',
6826                         cls: 'combobox-clear',
6827                         cn  : [
6828                             {
6829                                 tag : 'i',
6830                                 cls: 'icon-remove'
6831                             }
6832                         ]
6833                     }
6834                 ]
6835
6836             })
6837         }
6838         
6839         if(this.multiple){
6840             combobox.cls += ' select2-container-multi';
6841         }
6842         
6843         if (align ==='left' && this.fieldLabel.length) {
6844             
6845                 Roo.log("left and has label");
6846                 cfg.cn = [
6847                     
6848                     {
6849                         tag: 'label',
6850                         'for' :  id,
6851                         cls : 'control-label col-sm-' + this.labelWidth,
6852                         html : this.fieldLabel
6853                         
6854                     },
6855                     {
6856                         cls : "col-sm-" + (12 - this.labelWidth), 
6857                         cn: [
6858                             combobox
6859                         ]
6860                     }
6861                     
6862                 ];
6863         } else if ( this.fieldLabel.length) {
6864                 Roo.log(" label");
6865                  cfg.cn = [
6866                    
6867                     {
6868                         tag: 'label',
6869                         //cls : 'input-group-addon',
6870                         html : this.fieldLabel
6871                         
6872                     },
6873                     
6874                     combobox
6875                     
6876                 ];
6877
6878         } else {
6879             
6880                 Roo.log(" no label && no align");
6881                 cfg = combobox
6882                      
6883                 
6884         }
6885          
6886         var settings=this;
6887         ['xs','sm','md','lg'].map(function(size){
6888             if (settings[size]) {
6889                 cfg.cls += ' col-' + size + '-' + settings[size];
6890             }
6891         });
6892         
6893         return cfg;
6894         
6895     },
6896     
6897     
6898     
6899     // private
6900     onResize : function(w, h){
6901 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6902 //        if(typeof w == 'number'){
6903 //            var x = w - this.trigger.getWidth();
6904 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6905 //            this.trigger.setStyle('left', x+'px');
6906 //        }
6907     },
6908
6909     // private
6910     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6911
6912     // private
6913     getResizeEl : function(){
6914         return this.inputEl();
6915     },
6916
6917     // private
6918     getPositionEl : function(){
6919         return this.inputEl();
6920     },
6921
6922     // private
6923     alignErrorIcon : function(){
6924         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6925     },
6926
6927     // private
6928     initEvents : function(){
6929         
6930         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6931         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6932         if(!this.multiple){
6933             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6934             if(this.hideTrigger){
6935                 this.trigger.setDisplayed(false);
6936             }
6937             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6938         }
6939         
6940         if(this.multiple){
6941             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6942         }
6943         
6944         //this.trigger.addClassOnOver('x-form-trigger-over');
6945         //this.trigger.addClassOnClick('x-form-trigger-click');
6946         
6947         //if(!this.width){
6948         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6949         //}
6950     },
6951
6952     // private
6953     initTrigger : function(){
6954        
6955     },
6956
6957     // private
6958     onDestroy : function(){
6959         if(this.trigger){
6960             this.trigger.removeAllListeners();
6961           //  this.trigger.remove();
6962         }
6963         //if(this.wrap){
6964         //    this.wrap.remove();
6965         //}
6966         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6967     },
6968
6969     // private
6970     onFocus : function(){
6971         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6972         /*
6973         if(!this.mimicing){
6974             this.wrap.addClass('x-trigger-wrap-focus');
6975             this.mimicing = true;
6976             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6977             if(this.monitorTab){
6978                 this.el.on("keydown", this.checkTab, this);
6979             }
6980         }
6981         */
6982     },
6983
6984     // private
6985     checkTab : function(e){
6986         if(e.getKey() == e.TAB){
6987             this.triggerBlur();
6988         }
6989     },
6990
6991     // private
6992     onBlur : function(){
6993         // do nothing
6994     },
6995
6996     // private
6997     mimicBlur : function(e, t){
6998         /*
6999         if(!this.wrap.contains(t) && this.validateBlur()){
7000             this.triggerBlur();
7001         }
7002         */
7003     },
7004
7005     // private
7006     triggerBlur : function(){
7007         this.mimicing = false;
7008         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7009         if(this.monitorTab){
7010             this.el.un("keydown", this.checkTab, this);
7011         }
7012         //this.wrap.removeClass('x-trigger-wrap-focus');
7013         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7014     },
7015
7016     // private
7017     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7018     validateBlur : function(e, t){
7019         return true;
7020     },
7021
7022     // private
7023     onDisable : function(){
7024         this.inputEl().dom.disabled = true;
7025         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7026         //if(this.wrap){
7027         //    this.wrap.addClass('x-item-disabled');
7028         //}
7029     },
7030
7031     // private
7032     onEnable : function(){
7033         this.inputEl().dom.disabled = false;
7034         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7035         //if(this.wrap){
7036         //    this.el.removeClass('x-item-disabled');
7037         //}
7038     },
7039
7040     // private
7041     onShow : function(){
7042         var ae = this.getActionEl();
7043         
7044         if(ae){
7045             ae.dom.style.display = '';
7046             ae.dom.style.visibility = 'visible';
7047         }
7048     },
7049
7050     // private
7051     
7052     onHide : function(){
7053         var ae = this.getActionEl();
7054         ae.dom.style.display = 'none';
7055     },
7056
7057     /**
7058      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7059      * by an implementing function.
7060      * @method
7061      * @param {EventObject} e
7062      */
7063     onTriggerClick : Roo.emptyFn
7064 });
7065  /*
7066  * Based on:
7067  * Ext JS Library 1.1.1
7068  * Copyright(c) 2006-2007, Ext JS, LLC.
7069  *
7070  * Originally Released Under LGPL - original licence link has changed is not relivant.
7071  *
7072  * Fork - LGPL
7073  * <script type="text/javascript">
7074  */
7075
7076
7077 /**
7078  * @class Roo.data.SortTypes
7079  * @singleton
7080  * Defines the default sorting (casting?) comparison functions used when sorting data.
7081  */
7082 Roo.data.SortTypes = {
7083     /**
7084      * Default sort that does nothing
7085      * @param {Mixed} s The value being converted
7086      * @return {Mixed} The comparison value
7087      */
7088     none : function(s){
7089         return s;
7090     },
7091     
7092     /**
7093      * The regular expression used to strip tags
7094      * @type {RegExp}
7095      * @property
7096      */
7097     stripTagsRE : /<\/?[^>]+>/gi,
7098     
7099     /**
7100      * Strips all HTML tags to sort on text only
7101      * @param {Mixed} s The value being converted
7102      * @return {String} The comparison value
7103      */
7104     asText : function(s){
7105         return String(s).replace(this.stripTagsRE, "");
7106     },
7107     
7108     /**
7109      * Strips all HTML tags to sort on text only - Case insensitive
7110      * @param {Mixed} s The value being converted
7111      * @return {String} The comparison value
7112      */
7113     asUCText : function(s){
7114         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7115     },
7116     
7117     /**
7118      * Case insensitive string
7119      * @param {Mixed} s The value being converted
7120      * @return {String} The comparison value
7121      */
7122     asUCString : function(s) {
7123         return String(s).toUpperCase();
7124     },
7125     
7126     /**
7127      * Date sorting
7128      * @param {Mixed} s The value being converted
7129      * @return {Number} The comparison value
7130      */
7131     asDate : function(s) {
7132         if(!s){
7133             return 0;
7134         }
7135         if(s instanceof Date){
7136             return s.getTime();
7137         }
7138         return Date.parse(String(s));
7139     },
7140     
7141     /**
7142      * Float sorting
7143      * @param {Mixed} s The value being converted
7144      * @return {Float} The comparison value
7145      */
7146     asFloat : function(s) {
7147         var val = parseFloat(String(s).replace(/,/g, ""));
7148         if(isNaN(val)) val = 0;
7149         return val;
7150     },
7151     
7152     /**
7153      * Integer sorting
7154      * @param {Mixed} s The value being converted
7155      * @return {Number} The comparison value
7156      */
7157     asInt : function(s) {
7158         var val = parseInt(String(s).replace(/,/g, ""));
7159         if(isNaN(val)) val = 0;
7160         return val;
7161     }
7162 };/*
7163  * Based on:
7164  * Ext JS Library 1.1.1
7165  * Copyright(c) 2006-2007, Ext JS, LLC.
7166  *
7167  * Originally Released Under LGPL - original licence link has changed is not relivant.
7168  *
7169  * Fork - LGPL
7170  * <script type="text/javascript">
7171  */
7172
7173 /**
7174 * @class Roo.data.Record
7175  * Instances of this class encapsulate both record <em>definition</em> information, and record
7176  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7177  * to access Records cached in an {@link Roo.data.Store} object.<br>
7178  * <p>
7179  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7180  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7181  * objects.<br>
7182  * <p>
7183  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7184  * @constructor
7185  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7186  * {@link #create}. The parameters are the same.
7187  * @param {Array} data An associative Array of data values keyed by the field name.
7188  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7189  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7190  * not specified an integer id is generated.
7191  */
7192 Roo.data.Record = function(data, id){
7193     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7194     this.data = data;
7195 };
7196
7197 /**
7198  * Generate a constructor for a specific record layout.
7199  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7200  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7201  * Each field definition object may contain the following properties: <ul>
7202  * <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,
7203  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7204  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7205  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7206  * is being used, then this is a string containing the javascript expression to reference the data relative to 
7207  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7208  * to the data item relative to the record element. If the mapping expression is the same as the field name,
7209  * this may be omitted.</p></li>
7210  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7211  * <ul><li>auto (Default, implies no conversion)</li>
7212  * <li>string</li>
7213  * <li>int</li>
7214  * <li>float</li>
7215  * <li>boolean</li>
7216  * <li>date</li></ul></p></li>
7217  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7218  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7219  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7220  * by the Reader into an object that will be stored in the Record. It is passed the
7221  * following parameters:<ul>
7222  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7223  * </ul></p></li>
7224  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7225  * </ul>
7226  * <br>usage:<br><pre><code>
7227 var TopicRecord = Roo.data.Record.create(
7228     {name: 'title', mapping: 'topic_title'},
7229     {name: 'author', mapping: 'username'},
7230     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7231     {name: 'lastPost', mapping: 'post_time', type: 'date'},
7232     {name: 'lastPoster', mapping: 'user2'},
7233     {name: 'excerpt', mapping: 'post_text'}
7234 );
7235
7236 var myNewRecord = new TopicRecord({
7237     title: 'Do my job please',
7238     author: 'noobie',
7239     totalPosts: 1,
7240     lastPost: new Date(),
7241     lastPoster: 'Animal',
7242     excerpt: 'No way dude!'
7243 });
7244 myStore.add(myNewRecord);
7245 </code></pre>
7246  * @method create
7247  * @static
7248  */
7249 Roo.data.Record.create = function(o){
7250     var f = function(){
7251         f.superclass.constructor.apply(this, arguments);
7252     };
7253     Roo.extend(f, Roo.data.Record);
7254     var p = f.prototype;
7255     p.fields = new Roo.util.MixedCollection(false, function(field){
7256         return field.name;
7257     });
7258     for(var i = 0, len = o.length; i < len; i++){
7259         p.fields.add(new Roo.data.Field(o[i]));
7260     }
7261     f.getField = function(name){
7262         return p.fields.get(name);  
7263     };
7264     return f;
7265 };
7266
7267 Roo.data.Record.AUTO_ID = 1000;
7268 Roo.data.Record.EDIT = 'edit';
7269 Roo.data.Record.REJECT = 'reject';
7270 Roo.data.Record.COMMIT = 'commit';
7271
7272 Roo.data.Record.prototype = {
7273     /**
7274      * Readonly flag - true if this record has been modified.
7275      * @type Boolean
7276      */
7277     dirty : false,
7278     editing : false,
7279     error: null,
7280     modified: null,
7281
7282     // private
7283     join : function(store){
7284         this.store = store;
7285     },
7286
7287     /**
7288      * Set the named field to the specified value.
7289      * @param {String} name The name of the field to set.
7290      * @param {Object} value The value to set the field to.
7291      */
7292     set : function(name, value){
7293         if(this.data[name] == value){
7294             return;
7295         }
7296         this.dirty = true;
7297         if(!this.modified){
7298             this.modified = {};
7299         }
7300         if(typeof this.modified[name] == 'undefined'){
7301             this.modified[name] = this.data[name];
7302         }
7303         this.data[name] = value;
7304         if(!this.editing && this.store){
7305             this.store.afterEdit(this);
7306         }       
7307     },
7308
7309     /**
7310      * Get the value of the named field.
7311      * @param {String} name The name of the field to get the value of.
7312      * @return {Object} The value of the field.
7313      */
7314     get : function(name){
7315         return this.data[name]; 
7316     },
7317
7318     // private
7319     beginEdit : function(){
7320         this.editing = true;
7321         this.modified = {}; 
7322     },
7323
7324     // private
7325     cancelEdit : function(){
7326         this.editing = false;
7327         delete this.modified;
7328     },
7329
7330     // private
7331     endEdit : function(){
7332         this.editing = false;
7333         if(this.dirty && this.store){
7334             this.store.afterEdit(this);
7335         }
7336     },
7337
7338     /**
7339      * Usually called by the {@link Roo.data.Store} which owns the Record.
7340      * Rejects all changes made to the Record since either creation, or the last commit operation.
7341      * Modified fields are reverted to their original values.
7342      * <p>
7343      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7344      * of reject operations.
7345      */
7346     reject : function(){
7347         var m = this.modified;
7348         for(var n in m){
7349             if(typeof m[n] != "function"){
7350                 this.data[n] = m[n];
7351             }
7352         }
7353         this.dirty = false;
7354         delete this.modified;
7355         this.editing = false;
7356         if(this.store){
7357             this.store.afterReject(this);
7358         }
7359     },
7360
7361     /**
7362      * Usually called by the {@link Roo.data.Store} which owns the Record.
7363      * Commits all changes made to the Record since either creation, or the last commit operation.
7364      * <p>
7365      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7366      * of commit operations.
7367      */
7368     commit : function(){
7369         this.dirty = false;
7370         delete this.modified;
7371         this.editing = false;
7372         if(this.store){
7373             this.store.afterCommit(this);
7374         }
7375     },
7376
7377     // private
7378     hasError : function(){
7379         return this.error != null;
7380     },
7381
7382     // private
7383     clearError : function(){
7384         this.error = null;
7385     },
7386
7387     /**
7388      * Creates a copy of this record.
7389      * @param {String} id (optional) A new record id if you don't want to use this record's id
7390      * @return {Record}
7391      */
7392     copy : function(newId) {
7393         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
7394     }
7395 };/*
7396  * Based on:
7397  * Ext JS Library 1.1.1
7398  * Copyright(c) 2006-2007, Ext JS, LLC.
7399  *
7400  * Originally Released Under LGPL - original licence link has changed is not relivant.
7401  *
7402  * Fork - LGPL
7403  * <script type="text/javascript">
7404  */
7405
7406
7407
7408 /**
7409  * @class Roo.data.Store
7410  * @extends Roo.util.Observable
7411  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
7412  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
7413  * <p>
7414  * 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
7415  * has no knowledge of the format of the data returned by the Proxy.<br>
7416  * <p>
7417  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
7418  * instances from the data object. These records are cached and made available through accessor functions.
7419  * @constructor
7420  * Creates a new Store.
7421  * @param {Object} config A config object containing the objects needed for the Store to access data,
7422  * and read the data into Records.
7423  */
7424 Roo.data.Store = function(config){
7425     this.data = new Roo.util.MixedCollection(false);
7426     this.data.getKey = function(o){
7427         return o.id;
7428     };
7429     this.baseParams = {};
7430     // private
7431     this.paramNames = {
7432         "start" : "start",
7433         "limit" : "limit",
7434         "sort" : "sort",
7435         "dir" : "dir",
7436         "multisort" : "_multisort"
7437     };
7438
7439     if(config && config.data){
7440         this.inlineData = config.data;
7441         delete config.data;
7442     }
7443
7444     Roo.apply(this, config);
7445     
7446     if(this.reader){ // reader passed
7447         this.reader = Roo.factory(this.reader, Roo.data);
7448         this.reader.xmodule = this.xmodule || false;
7449         if(!this.recordType){
7450             this.recordType = this.reader.recordType;
7451         }
7452         if(this.reader.onMetaChange){
7453             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
7454         }
7455     }
7456
7457     if(this.recordType){
7458         this.fields = this.recordType.prototype.fields;
7459     }
7460     this.modified = [];
7461
7462     this.addEvents({
7463         /**
7464          * @event datachanged
7465          * Fires when the data cache has changed, and a widget which is using this Store
7466          * as a Record cache should refresh its view.
7467          * @param {Store} this
7468          */
7469         datachanged : true,
7470         /**
7471          * @event metachange
7472          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
7473          * @param {Store} this
7474          * @param {Object} meta The JSON metadata
7475          */
7476         metachange : true,
7477         /**
7478          * @event add
7479          * Fires when Records have been added to the Store
7480          * @param {Store} this
7481          * @param {Roo.data.Record[]} records The array of Records added
7482          * @param {Number} index The index at which the record(s) were added
7483          */
7484         add : true,
7485         /**
7486          * @event remove
7487          * Fires when a Record has been removed from the Store
7488          * @param {Store} this
7489          * @param {Roo.data.Record} record The Record that was removed
7490          * @param {Number} index The index at which the record was removed
7491          */
7492         remove : true,
7493         /**
7494          * @event update
7495          * Fires when a Record has been updated
7496          * @param {Store} this
7497          * @param {Roo.data.Record} record The Record that was updated
7498          * @param {String} operation The update operation being performed.  Value may be one of:
7499          * <pre><code>
7500  Roo.data.Record.EDIT
7501  Roo.data.Record.REJECT
7502  Roo.data.Record.COMMIT
7503          * </code></pre>
7504          */
7505         update : true,
7506         /**
7507          * @event clear
7508          * Fires when the data cache has been cleared.
7509          * @param {Store} this
7510          */
7511         clear : true,
7512         /**
7513          * @event beforeload
7514          * Fires before a request is made for a new data object.  If the beforeload handler returns false
7515          * the load action will be canceled.
7516          * @param {Store} this
7517          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7518          */
7519         beforeload : true,
7520         /**
7521          * @event beforeloadadd
7522          * Fires after a new set of Records has been loaded.
7523          * @param {Store} this
7524          * @param {Roo.data.Record[]} records The Records that were loaded
7525          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7526          */
7527         beforeloadadd : true,
7528         /**
7529          * @event load
7530          * Fires after a new set of Records has been loaded, before they are added to the store.
7531          * @param {Store} this
7532          * @param {Roo.data.Record[]} records The Records that were loaded
7533          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7534          * @params {Object} return from reader
7535          */
7536         load : true,
7537         /**
7538          * @event loadexception
7539          * Fires if an exception occurs in the Proxy during loading.
7540          * Called with the signature of the Proxy's "loadexception" event.
7541          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
7542          * 
7543          * @param {Proxy} 
7544          * @param {Object} return from JsonData.reader() - success, totalRecords, records
7545          * @param {Object} load options 
7546          * @param {Object} jsonData from your request (normally this contains the Exception)
7547          */
7548         loadexception : true
7549     });
7550     
7551     if(this.proxy){
7552         this.proxy = Roo.factory(this.proxy, Roo.data);
7553         this.proxy.xmodule = this.xmodule || false;
7554         this.relayEvents(this.proxy,  ["loadexception"]);
7555     }
7556     this.sortToggle = {};
7557     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
7558
7559     Roo.data.Store.superclass.constructor.call(this);
7560
7561     if(this.inlineData){
7562         this.loadData(this.inlineData);
7563         delete this.inlineData;
7564     }
7565 };
7566
7567 Roo.extend(Roo.data.Store, Roo.util.Observable, {
7568      /**
7569     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
7570     * without a remote query - used by combo/forms at present.
7571     */
7572     
7573     /**
7574     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7575     */
7576     /**
7577     * @cfg {Array} data Inline data to be loaded when the store is initialized.
7578     */
7579     /**
7580     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7581     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7582     */
7583     /**
7584     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7585     * on any HTTP request
7586     */
7587     /**
7588     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7589     */
7590     /**
7591     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7592     */
7593     multiSort: false,
7594     /**
7595     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7596     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7597     */
7598     remoteSort : false,
7599
7600     /**
7601     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7602      * loaded or when a record is removed. (defaults to false).
7603     */
7604     pruneModifiedRecords : false,
7605
7606     // private
7607     lastOptions : null,
7608
7609     /**
7610      * Add Records to the Store and fires the add event.
7611      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7612      */
7613     add : function(records){
7614         records = [].concat(records);
7615         for(var i = 0, len = records.length; i < len; i++){
7616             records[i].join(this);
7617         }
7618         var index = this.data.length;
7619         this.data.addAll(records);
7620         this.fireEvent("add", this, records, index);
7621     },
7622
7623     /**
7624      * Remove a Record from the Store and fires the remove event.
7625      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7626      */
7627     remove : function(record){
7628         var index = this.data.indexOf(record);
7629         this.data.removeAt(index);
7630         if(this.pruneModifiedRecords){
7631             this.modified.remove(record);
7632         }
7633         this.fireEvent("remove", this, record, index);
7634     },
7635
7636     /**
7637      * Remove all Records from the Store and fires the clear event.
7638      */
7639     removeAll : function(){
7640         this.data.clear();
7641         if(this.pruneModifiedRecords){
7642             this.modified = [];
7643         }
7644         this.fireEvent("clear", this);
7645     },
7646
7647     /**
7648      * Inserts Records to the Store at the given index and fires the add event.
7649      * @param {Number} index The start index at which to insert the passed Records.
7650      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7651      */
7652     insert : function(index, records){
7653         records = [].concat(records);
7654         for(var i = 0, len = records.length; i < len; i++){
7655             this.data.insert(index, records[i]);
7656             records[i].join(this);
7657         }
7658         this.fireEvent("add", this, records, index);
7659     },
7660
7661     /**
7662      * Get the index within the cache of the passed Record.
7663      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7664      * @return {Number} The index of the passed Record. Returns -1 if not found.
7665      */
7666     indexOf : function(record){
7667         return this.data.indexOf(record);
7668     },
7669
7670     /**
7671      * Get the index within the cache of the Record with the passed id.
7672      * @param {String} id The id of the Record to find.
7673      * @return {Number} The index of the Record. Returns -1 if not found.
7674      */
7675     indexOfId : function(id){
7676         return this.data.indexOfKey(id);
7677     },
7678
7679     /**
7680      * Get the Record with the specified id.
7681      * @param {String} id The id of the Record to find.
7682      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7683      */
7684     getById : function(id){
7685         return this.data.key(id);
7686     },
7687
7688     /**
7689      * Get the Record at the specified index.
7690      * @param {Number} index The index of the Record to find.
7691      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7692      */
7693     getAt : function(index){
7694         return this.data.itemAt(index);
7695     },
7696
7697     /**
7698      * Returns a range of Records between specified indices.
7699      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7700      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7701      * @return {Roo.data.Record[]} An array of Records
7702      */
7703     getRange : function(start, end){
7704         return this.data.getRange(start, end);
7705     },
7706
7707     // private
7708     storeOptions : function(o){
7709         o = Roo.apply({}, o);
7710         delete o.callback;
7711         delete o.scope;
7712         this.lastOptions = o;
7713     },
7714
7715     /**
7716      * Loads the Record cache from the configured Proxy using the configured Reader.
7717      * <p>
7718      * If using remote paging, then the first load call must specify the <em>start</em>
7719      * and <em>limit</em> properties in the options.params property to establish the initial
7720      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7721      * <p>
7722      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7723      * and this call will return before the new data has been loaded. Perform any post-processing
7724      * in a callback function, or in a "load" event handler.</strong>
7725      * <p>
7726      * @param {Object} options An object containing properties which control loading options:<ul>
7727      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7728      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7729      * passed the following arguments:<ul>
7730      * <li>r : Roo.data.Record[]</li>
7731      * <li>options: Options object from the load call</li>
7732      * <li>success: Boolean success indicator</li></ul></li>
7733      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7734      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7735      * </ul>
7736      */
7737     load : function(options){
7738         options = options || {};
7739         if(this.fireEvent("beforeload", this, options) !== false){
7740             this.storeOptions(options);
7741             var p = Roo.apply(options.params || {}, this.baseParams);
7742             // if meta was not loaded from remote source.. try requesting it.
7743             if (!this.reader.metaFromRemote) {
7744                 p._requestMeta = 1;
7745             }
7746             if(this.sortInfo && this.remoteSort){
7747                 var pn = this.paramNames;
7748                 p[pn["sort"]] = this.sortInfo.field;
7749                 p[pn["dir"]] = this.sortInfo.direction;
7750             }
7751             if (this.multiSort) {
7752                 var pn = this.paramNames;
7753                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7754             }
7755             
7756             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7757         }
7758     },
7759
7760     /**
7761      * Reloads the Record cache from the configured Proxy using the configured Reader and
7762      * the options from the last load operation performed.
7763      * @param {Object} options (optional) An object containing properties which may override the options
7764      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7765      * the most recently used options are reused).
7766      */
7767     reload : function(options){
7768         this.load(Roo.applyIf(options||{}, this.lastOptions));
7769     },
7770
7771     // private
7772     // Called as a callback by the Reader during a load operation.
7773     loadRecords : function(o, options, success){
7774         if(!o || success === false){
7775             if(success !== false){
7776                 this.fireEvent("load", this, [], options, o);
7777             }
7778             if(options.callback){
7779                 options.callback.call(options.scope || this, [], options, false);
7780             }
7781             return;
7782         }
7783         // if data returned failure - throw an exception.
7784         if (o.success === false) {
7785             // show a message if no listener is registered.
7786             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7787                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7788             }
7789             // loadmask wil be hooked into this..
7790             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7791             return;
7792         }
7793         var r = o.records, t = o.totalRecords || r.length;
7794         
7795         this.fireEvent("beforeloadadd", this, r, options, o);
7796         
7797         if(!options || options.add !== true){
7798             if(this.pruneModifiedRecords){
7799                 this.modified = [];
7800             }
7801             for(var i = 0, len = r.length; i < len; i++){
7802                 r[i].join(this);
7803             }
7804             if(this.snapshot){
7805                 this.data = this.snapshot;
7806                 delete this.snapshot;
7807             }
7808             this.data.clear();
7809             this.data.addAll(r);
7810             this.totalLength = t;
7811             this.applySort();
7812             this.fireEvent("datachanged", this);
7813         }else{
7814             this.totalLength = Math.max(t, this.data.length+r.length);
7815             this.add(r);
7816         }
7817         this.fireEvent("load", this, r, options, o);
7818         if(options.callback){
7819             options.callback.call(options.scope || this, r, options, true);
7820         }
7821     },
7822
7823
7824     /**
7825      * Loads data from a passed data block. A Reader which understands the format of the data
7826      * must have been configured in the constructor.
7827      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7828      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7829      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7830      */
7831     loadData : function(o, append){
7832         var r = this.reader.readRecords(o);
7833         this.loadRecords(r, {add: append}, true);
7834     },
7835
7836     /**
7837      * Gets the number of cached records.
7838      * <p>
7839      * <em>If using paging, this may not be the total size of the dataset. If the data object
7840      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7841      * the data set size</em>
7842      */
7843     getCount : function(){
7844         return this.data.length || 0;
7845     },
7846
7847     /**
7848      * Gets the total number of records in the dataset as returned by the server.
7849      * <p>
7850      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7851      * the dataset size</em>
7852      */
7853     getTotalCount : function(){
7854         return this.totalLength || 0;
7855     },
7856
7857     /**
7858      * Returns the sort state of the Store as an object with two properties:
7859      * <pre><code>
7860  field {String} The name of the field by which the Records are sorted
7861  direction {String} The sort order, "ASC" or "DESC"
7862      * </code></pre>
7863      */
7864     getSortState : function(){
7865         return this.sortInfo;
7866     },
7867
7868     // private
7869     applySort : function(){
7870         if(this.sortInfo && !this.remoteSort){
7871             var s = this.sortInfo, f = s.field;
7872             var st = this.fields.get(f).sortType;
7873             var fn = function(r1, r2){
7874                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7875                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7876             };
7877             this.data.sort(s.direction, fn);
7878             if(this.snapshot && this.snapshot != this.data){
7879                 this.snapshot.sort(s.direction, fn);
7880             }
7881         }
7882     },
7883
7884     /**
7885      * Sets the default sort column and order to be used by the next load operation.
7886      * @param {String} fieldName The name of the field to sort by.
7887      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7888      */
7889     setDefaultSort : function(field, dir){
7890         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7891     },
7892
7893     /**
7894      * Sort the Records.
7895      * If remote sorting is used, the sort is performed on the server, and the cache is
7896      * reloaded. If local sorting is used, the cache is sorted internally.
7897      * @param {String} fieldName The name of the field to sort by.
7898      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7899      */
7900     sort : function(fieldName, dir){
7901         var f = this.fields.get(fieldName);
7902         if(!dir){
7903             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7904             
7905             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7906                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7907             }else{
7908                 dir = f.sortDir;
7909             }
7910         }
7911         this.sortToggle[f.name] = dir;
7912         this.sortInfo = {field: f.name, direction: dir};
7913         if(!this.remoteSort){
7914             this.applySort();
7915             this.fireEvent("datachanged", this);
7916         }else{
7917             this.load(this.lastOptions);
7918         }
7919     },
7920
7921     /**
7922      * Calls the specified function for each of the Records in the cache.
7923      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7924      * Returning <em>false</em> aborts and exits the iteration.
7925      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7926      */
7927     each : function(fn, scope){
7928         this.data.each(fn, scope);
7929     },
7930
7931     /**
7932      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7933      * (e.g., during paging).
7934      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7935      */
7936     getModifiedRecords : function(){
7937         return this.modified;
7938     },
7939
7940     // private
7941     createFilterFn : function(property, value, anyMatch){
7942         if(!value.exec){ // not a regex
7943             value = String(value);
7944             if(value.length == 0){
7945                 return false;
7946             }
7947             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7948         }
7949         return function(r){
7950             return value.test(r.data[property]);
7951         };
7952     },
7953
7954     /**
7955      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7956      * @param {String} property A field on your records
7957      * @param {Number} start The record index to start at (defaults to 0)
7958      * @param {Number} end The last record index to include (defaults to length - 1)
7959      * @return {Number} The sum
7960      */
7961     sum : function(property, start, end){
7962         var rs = this.data.items, v = 0;
7963         start = start || 0;
7964         end = (end || end === 0) ? end : rs.length-1;
7965
7966         for(var i = start; i <= end; i++){
7967             v += (rs[i].data[property] || 0);
7968         }
7969         return v;
7970     },
7971
7972     /**
7973      * Filter the records by a specified property.
7974      * @param {String} field A field on your records
7975      * @param {String/RegExp} value Either a string that the field
7976      * should start with or a RegExp to test against the field
7977      * @param {Boolean} anyMatch True to match any part not just the beginning
7978      */
7979     filter : function(property, value, anyMatch){
7980         var fn = this.createFilterFn(property, value, anyMatch);
7981         return fn ? this.filterBy(fn) : this.clearFilter();
7982     },
7983
7984     /**
7985      * Filter by a function. The specified function will be called with each
7986      * record in this data source. If the function returns true the record is included,
7987      * otherwise it is filtered.
7988      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7989      * @param {Object} scope (optional) The scope of the function (defaults to this)
7990      */
7991     filterBy : function(fn, scope){
7992         this.snapshot = this.snapshot || this.data;
7993         this.data = this.queryBy(fn, scope||this);
7994         this.fireEvent("datachanged", this);
7995     },
7996
7997     /**
7998      * Query the records by a specified property.
7999      * @param {String} field A field on your records
8000      * @param {String/RegExp} value Either a string that the field
8001      * should start with or a RegExp to test against the field
8002      * @param {Boolean} anyMatch True to match any part not just the beginning
8003      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8004      */
8005     query : function(property, value, anyMatch){
8006         var fn = this.createFilterFn(property, value, anyMatch);
8007         return fn ? this.queryBy(fn) : this.data.clone();
8008     },
8009
8010     /**
8011      * Query by a function. The specified function will be called with each
8012      * record in this data source. If the function returns true the record is included
8013      * in the results.
8014      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8015      * @param {Object} scope (optional) The scope of the function (defaults to this)
8016       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8017      **/
8018     queryBy : function(fn, scope){
8019         var data = this.snapshot || this.data;
8020         return data.filterBy(fn, scope||this);
8021     },
8022
8023     /**
8024      * Collects unique values for a particular dataIndex from this store.
8025      * @param {String} dataIndex The property to collect
8026      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8027      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8028      * @return {Array} An array of the unique values
8029      **/
8030     collect : function(dataIndex, allowNull, bypassFilter){
8031         var d = (bypassFilter === true && this.snapshot) ?
8032                 this.snapshot.items : this.data.items;
8033         var v, sv, r = [], l = {};
8034         for(var i = 0, len = d.length; i < len; i++){
8035             v = d[i].data[dataIndex];
8036             sv = String(v);
8037             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8038                 l[sv] = true;
8039                 r[r.length] = v;
8040             }
8041         }
8042         return r;
8043     },
8044
8045     /**
8046      * Revert to a view of the Record cache with no filtering applied.
8047      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
8048      */
8049     clearFilter : function(suppressEvent){
8050         if(this.snapshot && this.snapshot != this.data){
8051             this.data = this.snapshot;
8052             delete this.snapshot;
8053             if(suppressEvent !== true){
8054                 this.fireEvent("datachanged", this);
8055             }
8056         }
8057     },
8058
8059     // private
8060     afterEdit : function(record){
8061         if(this.modified.indexOf(record) == -1){
8062             this.modified.push(record);
8063         }
8064         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8065     },
8066     
8067     // private
8068     afterReject : function(record){
8069         this.modified.remove(record);
8070         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8071     },
8072
8073     // private
8074     afterCommit : function(record){
8075         this.modified.remove(record);
8076         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8077     },
8078
8079     /**
8080      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8081      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8082      */
8083     commitChanges : function(){
8084         var m = this.modified.slice(0);
8085         this.modified = [];
8086         for(var i = 0, len = m.length; i < len; i++){
8087             m[i].commit();
8088         }
8089     },
8090
8091     /**
8092      * Cancel outstanding changes on all changed records.
8093      */
8094     rejectChanges : function(){
8095         var m = this.modified.slice(0);
8096         this.modified = [];
8097         for(var i = 0, len = m.length; i < len; i++){
8098             m[i].reject();
8099         }
8100     },
8101
8102     onMetaChange : function(meta, rtype, o){
8103         this.recordType = rtype;
8104         this.fields = rtype.prototype.fields;
8105         delete this.snapshot;
8106         this.sortInfo = meta.sortInfo || this.sortInfo;
8107         this.modified = [];
8108         this.fireEvent('metachange', this, this.reader.meta);
8109     },
8110     
8111     moveIndex : function(data, type)
8112     {
8113         var index = this.indexOf(data);
8114         
8115         var newIndex = index + type;
8116         
8117         this.remove(data);
8118         
8119         this.insert(newIndex, data);
8120         
8121     }
8122 });/*
8123  * Based on:
8124  * Ext JS Library 1.1.1
8125  * Copyright(c) 2006-2007, Ext JS, LLC.
8126  *
8127  * Originally Released Under LGPL - original licence link has changed is not relivant.
8128  *
8129  * Fork - LGPL
8130  * <script type="text/javascript">
8131  */
8132
8133 /**
8134  * @class Roo.data.SimpleStore
8135  * @extends Roo.data.Store
8136  * Small helper class to make creating Stores from Array data easier.
8137  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8138  * @cfg {Array} fields An array of field definition objects, or field name strings.
8139  * @cfg {Array} data The multi-dimensional array of data
8140  * @constructor
8141  * @param {Object} config
8142  */
8143 Roo.data.SimpleStore = function(config){
8144     Roo.data.SimpleStore.superclass.constructor.call(this, {
8145         isLocal : true,
8146         reader: new Roo.data.ArrayReader({
8147                 id: config.id
8148             },
8149             Roo.data.Record.create(config.fields)
8150         ),
8151         proxy : new Roo.data.MemoryProxy(config.data)
8152     });
8153     this.load();
8154 };
8155 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8156  * Based on:
8157  * Ext JS Library 1.1.1
8158  * Copyright(c) 2006-2007, Ext JS, LLC.
8159  *
8160  * Originally Released Under LGPL - original licence link has changed is not relivant.
8161  *
8162  * Fork - LGPL
8163  * <script type="text/javascript">
8164  */
8165
8166 /**
8167 /**
8168  * @extends Roo.data.Store
8169  * @class Roo.data.JsonStore
8170  * Small helper class to make creating Stores for JSON data easier. <br/>
8171 <pre><code>
8172 var store = new Roo.data.JsonStore({
8173     url: 'get-images.php',
8174     root: 'images',
8175     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8176 });
8177 </code></pre>
8178  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8179  * JsonReader and HttpProxy (unless inline data is provided).</b>
8180  * @cfg {Array} fields An array of field definition objects, or field name strings.
8181  * @constructor
8182  * @param {Object} config
8183  */
8184 Roo.data.JsonStore = function(c){
8185     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8186         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8187         reader: new Roo.data.JsonReader(c, c.fields)
8188     }));
8189 };
8190 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8191  * Based on:
8192  * Ext JS Library 1.1.1
8193  * Copyright(c) 2006-2007, Ext JS, LLC.
8194  *
8195  * Originally Released Under LGPL - original licence link has changed is not relivant.
8196  *
8197  * Fork - LGPL
8198  * <script type="text/javascript">
8199  */
8200
8201  
8202 Roo.data.Field = function(config){
8203     if(typeof config == "string"){
8204         config = {name: config};
8205     }
8206     Roo.apply(this, config);
8207     
8208     if(!this.type){
8209         this.type = "auto";
8210     }
8211     
8212     var st = Roo.data.SortTypes;
8213     // named sortTypes are supported, here we look them up
8214     if(typeof this.sortType == "string"){
8215         this.sortType = st[this.sortType];
8216     }
8217     
8218     // set default sortType for strings and dates
8219     if(!this.sortType){
8220         switch(this.type){
8221             case "string":
8222                 this.sortType = st.asUCString;
8223                 break;
8224             case "date":
8225                 this.sortType = st.asDate;
8226                 break;
8227             default:
8228                 this.sortType = st.none;
8229         }
8230     }
8231
8232     // define once
8233     var stripRe = /[\$,%]/g;
8234
8235     // prebuilt conversion function for this field, instead of
8236     // switching every time we're reading a value
8237     if(!this.convert){
8238         var cv, dateFormat = this.dateFormat;
8239         switch(this.type){
8240             case "":
8241             case "auto":
8242             case undefined:
8243                 cv = function(v){ return v; };
8244                 break;
8245             case "string":
8246                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8247                 break;
8248             case "int":
8249                 cv = function(v){
8250                     return v !== undefined && v !== null && v !== '' ?
8251                            parseInt(String(v).replace(stripRe, ""), 10) : '';
8252                     };
8253                 break;
8254             case "float":
8255                 cv = function(v){
8256                     return v !== undefined && v !== null && v !== '' ?
8257                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
8258                     };
8259                 break;
8260             case "bool":
8261             case "boolean":
8262                 cv = function(v){ return v === true || v === "true" || v == 1; };
8263                 break;
8264             case "date":
8265                 cv = function(v){
8266                     if(!v){
8267                         return '';
8268                     }
8269                     if(v instanceof Date){
8270                         return v;
8271                     }
8272                     if(dateFormat){
8273                         if(dateFormat == "timestamp"){
8274                             return new Date(v*1000);
8275                         }
8276                         return Date.parseDate(v, dateFormat);
8277                     }
8278                     var parsed = Date.parse(v);
8279                     return parsed ? new Date(parsed) : null;
8280                 };
8281              break;
8282             
8283         }
8284         this.convert = cv;
8285     }
8286 };
8287
8288 Roo.data.Field.prototype = {
8289     dateFormat: null,
8290     defaultValue: "",
8291     mapping: null,
8292     sortType : null,
8293     sortDir : "ASC"
8294 };/*
8295  * Based on:
8296  * Ext JS Library 1.1.1
8297  * Copyright(c) 2006-2007, Ext JS, LLC.
8298  *
8299  * Originally Released Under LGPL - original licence link has changed is not relivant.
8300  *
8301  * Fork - LGPL
8302  * <script type="text/javascript">
8303  */
8304  
8305 // Base class for reading structured data from a data source.  This class is intended to be
8306 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
8307
8308 /**
8309  * @class Roo.data.DataReader
8310  * Base class for reading structured data from a data source.  This class is intended to be
8311  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
8312  */
8313
8314 Roo.data.DataReader = function(meta, recordType){
8315     
8316     this.meta = meta;
8317     
8318     this.recordType = recordType instanceof Array ? 
8319         Roo.data.Record.create(recordType) : recordType;
8320 };
8321
8322 Roo.data.DataReader.prototype = {
8323      /**
8324      * Create an empty record
8325      * @param {Object} data (optional) - overlay some values
8326      * @return {Roo.data.Record} record created.
8327      */
8328     newRow :  function(d) {
8329         var da =  {};
8330         this.recordType.prototype.fields.each(function(c) {
8331             switch( c.type) {
8332                 case 'int' : da[c.name] = 0; break;
8333                 case 'date' : da[c.name] = new Date(); break;
8334                 case 'float' : da[c.name] = 0.0; break;
8335                 case 'boolean' : da[c.name] = false; break;
8336                 default : da[c.name] = ""; break;
8337             }
8338             
8339         });
8340         return new this.recordType(Roo.apply(da, d));
8341     }
8342     
8343 };/*
8344  * Based on:
8345  * Ext JS Library 1.1.1
8346  * Copyright(c) 2006-2007, Ext JS, LLC.
8347  *
8348  * Originally Released Under LGPL - original licence link has changed is not relivant.
8349  *
8350  * Fork - LGPL
8351  * <script type="text/javascript">
8352  */
8353
8354 /**
8355  * @class Roo.data.DataProxy
8356  * @extends Roo.data.Observable
8357  * This class is an abstract base class for implementations which provide retrieval of
8358  * unformatted data objects.<br>
8359  * <p>
8360  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8361  * (of the appropriate type which knows how to parse the data object) to provide a block of
8362  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8363  * <p>
8364  * Custom implementations must implement the load method as described in
8365  * {@link Roo.data.HttpProxy#load}.
8366  */
8367 Roo.data.DataProxy = function(){
8368     this.addEvents({
8369         /**
8370          * @event beforeload
8371          * Fires before a network request is made to retrieve a data object.
8372          * @param {Object} This DataProxy object.
8373          * @param {Object} params The params parameter to the load function.
8374          */
8375         beforeload : true,
8376         /**
8377          * @event load
8378          * Fires before the load method's callback is called.
8379          * @param {Object} This DataProxy object.
8380          * @param {Object} o The data object.
8381          * @param {Object} arg The callback argument object passed to the load function.
8382          */
8383         load : true,
8384         /**
8385          * @event loadexception
8386          * Fires if an Exception occurs during data retrieval.
8387          * @param {Object} This DataProxy object.
8388          * @param {Object} o The data object.
8389          * @param {Object} arg The callback argument object passed to the load function.
8390          * @param {Object} e The Exception.
8391          */
8392         loadexception : true
8393     });
8394     Roo.data.DataProxy.superclass.constructor.call(this);
8395 };
8396
8397 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
8398
8399     /**
8400      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
8401      */
8402 /*
8403  * Based on:
8404  * Ext JS Library 1.1.1
8405  * Copyright(c) 2006-2007, Ext JS, LLC.
8406  *
8407  * Originally Released Under LGPL - original licence link has changed is not relivant.
8408  *
8409  * Fork - LGPL
8410  * <script type="text/javascript">
8411  */
8412 /**
8413  * @class Roo.data.MemoryProxy
8414  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
8415  * to the Reader when its load method is called.
8416  * @constructor
8417  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
8418  */
8419 Roo.data.MemoryProxy = function(data){
8420     if (data.data) {
8421         data = data.data;
8422     }
8423     Roo.data.MemoryProxy.superclass.constructor.call(this);
8424     this.data = data;
8425 };
8426
8427 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
8428     /**
8429      * Load data from the requested source (in this case an in-memory
8430      * data object passed to the constructor), read the data object into
8431      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8432      * process that block using the passed callback.
8433      * @param {Object} params This parameter is not used by the MemoryProxy class.
8434      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8435      * object into a block of Roo.data.Records.
8436      * @param {Function} callback The function into which to pass the block of Roo.data.records.
8437      * The function must be passed <ul>
8438      * <li>The Record block object</li>
8439      * <li>The "arg" argument from the load function</li>
8440      * <li>A boolean success indicator</li>
8441      * </ul>
8442      * @param {Object} scope The scope in which to call the callback
8443      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8444      */
8445     load : function(params, reader, callback, scope, arg){
8446         params = params || {};
8447         var result;
8448         try {
8449             result = reader.readRecords(this.data);
8450         }catch(e){
8451             this.fireEvent("loadexception", this, arg, null, e);
8452             callback.call(scope, null, arg, false);
8453             return;
8454         }
8455         callback.call(scope, result, arg, true);
8456     },
8457     
8458     // private
8459     update : function(params, records){
8460         
8461     }
8462 });/*
8463  * Based on:
8464  * Ext JS Library 1.1.1
8465  * Copyright(c) 2006-2007, Ext JS, LLC.
8466  *
8467  * Originally Released Under LGPL - original licence link has changed is not relivant.
8468  *
8469  * Fork - LGPL
8470  * <script type="text/javascript">
8471  */
8472 /**
8473  * @class Roo.data.HttpProxy
8474  * @extends Roo.data.DataProxy
8475  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
8476  * configured to reference a certain URL.<br><br>
8477  * <p>
8478  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
8479  * from which the running page was served.<br><br>
8480  * <p>
8481  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
8482  * <p>
8483  * Be aware that to enable the browser to parse an XML document, the server must set
8484  * the Content-Type header in the HTTP response to "text/xml".
8485  * @constructor
8486  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
8487  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
8488  * will be used to make the request.
8489  */
8490 Roo.data.HttpProxy = function(conn){
8491     Roo.data.HttpProxy.superclass.constructor.call(this);
8492     // is conn a conn config or a real conn?
8493     this.conn = conn;
8494     this.useAjax = !conn || !conn.events;
8495   
8496 };
8497
8498 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
8499     // thse are take from connection...
8500     
8501     /**
8502      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
8503      */
8504     /**
8505      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
8506      * extra parameters to each request made by this object. (defaults to undefined)
8507      */
8508     /**
8509      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
8510      *  to each request made by this object. (defaults to undefined)
8511      */
8512     /**
8513      * @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)
8514      */
8515     /**
8516      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
8517      */
8518      /**
8519      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
8520      * @type Boolean
8521      */
8522   
8523
8524     /**
8525      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
8526      * @type Boolean
8527      */
8528     /**
8529      * Return the {@link Roo.data.Connection} object being used by this Proxy.
8530      * @return {Connection} The Connection object. This object may be used to subscribe to events on
8531      * a finer-grained basis than the DataProxy events.
8532      */
8533     getConnection : function(){
8534         return this.useAjax ? Roo.Ajax : this.conn;
8535     },
8536
8537     /**
8538      * Load data from the configured {@link Roo.data.Connection}, read the data object into
8539      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
8540      * process that block using the passed callback.
8541      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8542      * for the request to the remote server.
8543      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8544      * object into a block of Roo.data.Records.
8545      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8546      * The function must be passed <ul>
8547      * <li>The Record block object</li>
8548      * <li>The "arg" argument from the load function</li>
8549      * <li>A boolean success indicator</li>
8550      * </ul>
8551      * @param {Object} scope The scope in which to call the callback
8552      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8553      */
8554     load : function(params, reader, callback, scope, arg){
8555         if(this.fireEvent("beforeload", this, params) !== false){
8556             var  o = {
8557                 params : params || {},
8558                 request: {
8559                     callback : callback,
8560                     scope : scope,
8561                     arg : arg
8562                 },
8563                 reader: reader,
8564                 callback : this.loadResponse,
8565                 scope: this
8566             };
8567             if(this.useAjax){
8568                 Roo.applyIf(o, this.conn);
8569                 if(this.activeRequest){
8570                     Roo.Ajax.abort(this.activeRequest);
8571                 }
8572                 this.activeRequest = Roo.Ajax.request(o);
8573             }else{
8574                 this.conn.request(o);
8575             }
8576         }else{
8577             callback.call(scope||this, null, arg, false);
8578         }
8579     },
8580
8581     // private
8582     loadResponse : function(o, success, response){
8583         delete this.activeRequest;
8584         if(!success){
8585             this.fireEvent("loadexception", this, o, response);
8586             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8587             return;
8588         }
8589         var result;
8590         try {
8591             result = o.reader.read(response);
8592         }catch(e){
8593             this.fireEvent("loadexception", this, o, response, e);
8594             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8595             return;
8596         }
8597         
8598         this.fireEvent("load", this, o, o.request.arg);
8599         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8600     },
8601
8602     // private
8603     update : function(dataSet){
8604
8605     },
8606
8607     // private
8608     updateResponse : function(dataSet){
8609
8610     }
8611 });/*
8612  * Based on:
8613  * Ext JS Library 1.1.1
8614  * Copyright(c) 2006-2007, Ext JS, LLC.
8615  *
8616  * Originally Released Under LGPL - original licence link has changed is not relivant.
8617  *
8618  * Fork - LGPL
8619  * <script type="text/javascript">
8620  */
8621
8622 /**
8623  * @class Roo.data.ScriptTagProxy
8624  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8625  * other than the originating domain of the running page.<br><br>
8626  * <p>
8627  * <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
8628  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8629  * <p>
8630  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8631  * source code that is used as the source inside a &lt;script> tag.<br><br>
8632  * <p>
8633  * In order for the browser to process the returned data, the server must wrap the data object
8634  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8635  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8636  * depending on whether the callback name was passed:
8637  * <p>
8638  * <pre><code>
8639 boolean scriptTag = false;
8640 String cb = request.getParameter("callback");
8641 if (cb != null) {
8642     scriptTag = true;
8643     response.setContentType("text/javascript");
8644 } else {
8645     response.setContentType("application/x-json");
8646 }
8647 Writer out = response.getWriter();
8648 if (scriptTag) {
8649     out.write(cb + "(");
8650 }
8651 out.print(dataBlock.toJsonString());
8652 if (scriptTag) {
8653     out.write(");");
8654 }
8655 </pre></code>
8656  *
8657  * @constructor
8658  * @param {Object} config A configuration object.
8659  */
8660 Roo.data.ScriptTagProxy = function(config){
8661     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8662     Roo.apply(this, config);
8663     this.head = document.getElementsByTagName("head")[0];
8664 };
8665
8666 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8667
8668 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8669     /**
8670      * @cfg {String} url The URL from which to request the data object.
8671      */
8672     /**
8673      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8674      */
8675     timeout : 30000,
8676     /**
8677      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8678      * the server the name of the callback function set up by the load call to process the returned data object.
8679      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8680      * javascript output which calls this named function passing the data object as its only parameter.
8681      */
8682     callbackParam : "callback",
8683     /**
8684      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8685      * name to the request.
8686      */
8687     nocache : true,
8688
8689     /**
8690      * Load data from the configured URL, read the data object into
8691      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8692      * process that block using the passed callback.
8693      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8694      * for the request to the remote server.
8695      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8696      * object into a block of Roo.data.Records.
8697      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8698      * The function must be passed <ul>
8699      * <li>The Record block object</li>
8700      * <li>The "arg" argument from the load function</li>
8701      * <li>A boolean success indicator</li>
8702      * </ul>
8703      * @param {Object} scope The scope in which to call the callback
8704      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8705      */
8706     load : function(params, reader, callback, scope, arg){
8707         if(this.fireEvent("beforeload", this, params) !== false){
8708
8709             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8710
8711             var url = this.url;
8712             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8713             if(this.nocache){
8714                 url += "&_dc=" + (new Date().getTime());
8715             }
8716             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8717             var trans = {
8718                 id : transId,
8719                 cb : "stcCallback"+transId,
8720                 scriptId : "stcScript"+transId,
8721                 params : params,
8722                 arg : arg,
8723                 url : url,
8724                 callback : callback,
8725                 scope : scope,
8726                 reader : reader
8727             };
8728             var conn = this;
8729
8730             window[trans.cb] = function(o){
8731                 conn.handleResponse(o, trans);
8732             };
8733
8734             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8735
8736             if(this.autoAbort !== false){
8737                 this.abort();
8738             }
8739
8740             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8741
8742             var script = document.createElement("script");
8743             script.setAttribute("src", url);
8744             script.setAttribute("type", "text/javascript");
8745             script.setAttribute("id", trans.scriptId);
8746             this.head.appendChild(script);
8747
8748             this.trans = trans;
8749         }else{
8750             callback.call(scope||this, null, arg, false);
8751         }
8752     },
8753
8754     // private
8755     isLoading : function(){
8756         return this.trans ? true : false;
8757     },
8758
8759     /**
8760      * Abort the current server request.
8761      */
8762     abort : function(){
8763         if(this.isLoading()){
8764             this.destroyTrans(this.trans);
8765         }
8766     },
8767
8768     // private
8769     destroyTrans : function(trans, isLoaded){
8770         this.head.removeChild(document.getElementById(trans.scriptId));
8771         clearTimeout(trans.timeoutId);
8772         if(isLoaded){
8773             window[trans.cb] = undefined;
8774             try{
8775                 delete window[trans.cb];
8776             }catch(e){}
8777         }else{
8778             // if hasn't been loaded, wait for load to remove it to prevent script error
8779             window[trans.cb] = function(){
8780                 window[trans.cb] = undefined;
8781                 try{
8782                     delete window[trans.cb];
8783                 }catch(e){}
8784             };
8785         }
8786     },
8787
8788     // private
8789     handleResponse : function(o, trans){
8790         this.trans = false;
8791         this.destroyTrans(trans, true);
8792         var result;
8793         try {
8794             result = trans.reader.readRecords(o);
8795         }catch(e){
8796             this.fireEvent("loadexception", this, o, trans.arg, e);
8797             trans.callback.call(trans.scope||window, null, trans.arg, false);
8798             return;
8799         }
8800         this.fireEvent("load", this, o, trans.arg);
8801         trans.callback.call(trans.scope||window, result, trans.arg, true);
8802     },
8803
8804     // private
8805     handleFailure : function(trans){
8806         this.trans = false;
8807         this.destroyTrans(trans, false);
8808         this.fireEvent("loadexception", this, null, trans.arg);
8809         trans.callback.call(trans.scope||window, null, trans.arg, false);
8810     }
8811 });/*
8812  * Based on:
8813  * Ext JS Library 1.1.1
8814  * Copyright(c) 2006-2007, Ext JS, LLC.
8815  *
8816  * Originally Released Under LGPL - original licence link has changed is not relivant.
8817  *
8818  * Fork - LGPL
8819  * <script type="text/javascript">
8820  */
8821
8822 /**
8823  * @class Roo.data.JsonReader
8824  * @extends Roo.data.DataReader
8825  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8826  * based on mappings in a provided Roo.data.Record constructor.
8827  * 
8828  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8829  * in the reply previously. 
8830  * 
8831  * <p>
8832  * Example code:
8833  * <pre><code>
8834 var RecordDef = Roo.data.Record.create([
8835     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8836     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8837 ]);
8838 var myReader = new Roo.data.JsonReader({
8839     totalProperty: "results",    // The property which contains the total dataset size (optional)
8840     root: "rows",                // The property which contains an Array of row objects
8841     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8842 }, RecordDef);
8843 </code></pre>
8844  * <p>
8845  * This would consume a JSON file like this:
8846  * <pre><code>
8847 { 'results': 2, 'rows': [
8848     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8849     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8850 }
8851 </code></pre>
8852  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8853  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8854  * paged from the remote server.
8855  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8856  * @cfg {String} root name of the property which contains the Array of row objects.
8857  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8858  * @constructor
8859  * Create a new JsonReader
8860  * @param {Object} meta Metadata configuration options
8861  * @param {Object} recordType Either an Array of field definition objects,
8862  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8863  */
8864 Roo.data.JsonReader = function(meta, recordType){
8865     
8866     meta = meta || {};
8867     // set some defaults:
8868     Roo.applyIf(meta, {
8869         totalProperty: 'total',
8870         successProperty : 'success',
8871         root : 'data',
8872         id : 'id'
8873     });
8874     
8875     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8876 };
8877 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8878     
8879     /**
8880      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8881      * Used by Store query builder to append _requestMeta to params.
8882      * 
8883      */
8884     metaFromRemote : false,
8885     /**
8886      * This method is only used by a DataProxy which has retrieved data from a remote server.
8887      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8888      * @return {Object} data A data block which is used by an Roo.data.Store object as
8889      * a cache of Roo.data.Records.
8890      */
8891     read : function(response){
8892         var json = response.responseText;
8893        
8894         var o = /* eval:var:o */ eval("("+json+")");
8895         if(!o) {
8896             throw {message: "JsonReader.read: Json object not found"};
8897         }
8898         
8899         if(o.metaData){
8900             
8901             delete this.ef;
8902             this.metaFromRemote = true;
8903             this.meta = o.metaData;
8904             this.recordType = Roo.data.Record.create(o.metaData.fields);
8905             this.onMetaChange(this.meta, this.recordType, o);
8906         }
8907         return this.readRecords(o);
8908     },
8909
8910     // private function a store will implement
8911     onMetaChange : function(meta, recordType, o){
8912
8913     },
8914
8915     /**
8916          * @ignore
8917          */
8918     simpleAccess: function(obj, subsc) {
8919         return obj[subsc];
8920     },
8921
8922         /**
8923          * @ignore
8924          */
8925     getJsonAccessor: function(){
8926         var re = /[\[\.]/;
8927         return function(expr) {
8928             try {
8929                 return(re.test(expr))
8930                     ? new Function("obj", "return obj." + expr)
8931                     : function(obj){
8932                         return obj[expr];
8933                     };
8934             } catch(e){}
8935             return Roo.emptyFn;
8936         };
8937     }(),
8938
8939     /**
8940      * Create a data block containing Roo.data.Records from an XML document.
8941      * @param {Object} o An object which contains an Array of row objects in the property specified
8942      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8943      * which contains the total size of the dataset.
8944      * @return {Object} data A data block which is used by an Roo.data.Store object as
8945      * a cache of Roo.data.Records.
8946      */
8947     readRecords : function(o){
8948         /**
8949          * After any data loads, the raw JSON data is available for further custom processing.
8950          * @type Object
8951          */
8952         this.o = o;
8953         var s = this.meta, Record = this.recordType,
8954             f = Record.prototype.fields, fi = f.items, fl = f.length;
8955
8956 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8957         if (!this.ef) {
8958             if(s.totalProperty) {
8959                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8960                 }
8961                 if(s.successProperty) {
8962                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8963                 }
8964                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8965                 if (s.id) {
8966                         var g = this.getJsonAccessor(s.id);
8967                         this.getId = function(rec) {
8968                                 var r = g(rec);
8969                                 return (r === undefined || r === "") ? null : r;
8970                         };
8971                 } else {
8972                         this.getId = function(){return null;};
8973                 }
8974             this.ef = [];
8975             for(var jj = 0; jj < fl; jj++){
8976                 f = fi[jj];
8977                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8978                 this.ef[jj] = this.getJsonAccessor(map);
8979             }
8980         }
8981
8982         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8983         if(s.totalProperty){
8984             var vt = parseInt(this.getTotal(o), 10);
8985             if(!isNaN(vt)){
8986                 totalRecords = vt;
8987             }
8988         }
8989         if(s.successProperty){
8990             var vs = this.getSuccess(o);
8991             if(vs === false || vs === 'false'){
8992                 success = false;
8993             }
8994         }
8995         var records = [];
8996             for(var i = 0; i < c; i++){
8997                     var n = root[i];
8998                 var values = {};
8999                 var id = this.getId(n);
9000                 for(var j = 0; j < fl; j++){
9001                     f = fi[j];
9002                 var v = this.ef[j](n);
9003                 if (!f.convert) {
9004                     Roo.log('missing convert for ' + f.name);
9005                     Roo.log(f);
9006                     continue;
9007                 }
9008                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9009                 }
9010                 var record = new Record(values, id);
9011                 record.json = n;
9012                 records[i] = record;
9013             }
9014             return {
9015             raw : o,
9016                 success : success,
9017                 records : records,
9018                 totalRecords : totalRecords
9019             };
9020     }
9021 });/*
9022  * Based on:
9023  * Ext JS Library 1.1.1
9024  * Copyright(c) 2006-2007, Ext JS, LLC.
9025  *
9026  * Originally Released Under LGPL - original licence link has changed is not relivant.
9027  *
9028  * Fork - LGPL
9029  * <script type="text/javascript">
9030  */
9031
9032 /**
9033  * @class Roo.data.ArrayReader
9034  * @extends Roo.data.DataReader
9035  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9036  * Each element of that Array represents a row of data fields. The
9037  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9038  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9039  * <p>
9040  * Example code:.
9041  * <pre><code>
9042 var RecordDef = Roo.data.Record.create([
9043     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
9044     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
9045 ]);
9046 var myReader = new Roo.data.ArrayReader({
9047     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
9048 }, RecordDef);
9049 </code></pre>
9050  * <p>
9051  * This would consume an Array like this:
9052  * <pre><code>
9053 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9054   </code></pre>
9055  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9056  * @constructor
9057  * Create a new JsonReader
9058  * @param {Object} meta Metadata configuration options.
9059  * @param {Object} recordType Either an Array of field definition objects
9060  * as specified to {@link Roo.data.Record#create},
9061  * or an {@link Roo.data.Record} object
9062  * created using {@link Roo.data.Record#create}.
9063  */
9064 Roo.data.ArrayReader = function(meta, recordType){
9065     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9066 };
9067
9068 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9069     /**
9070      * Create a data block containing Roo.data.Records from an XML document.
9071      * @param {Object} o An Array of row objects which represents the dataset.
9072      * @return {Object} data A data block which is used by an Roo.data.Store object as
9073      * a cache of Roo.data.Records.
9074      */
9075     readRecords : function(o){
9076         var sid = this.meta ? this.meta.id : null;
9077         var recordType = this.recordType, fields = recordType.prototype.fields;
9078         var records = [];
9079         var root = o;
9080             for(var i = 0; i < root.length; i++){
9081                     var n = root[i];
9082                 var values = {};
9083                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9084                 for(var j = 0, jlen = fields.length; j < jlen; j++){
9085                 var f = fields.items[j];
9086                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9087                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9088                 v = f.convert(v);
9089                 values[f.name] = v;
9090             }
9091                 var record = new recordType(values, id);
9092                 record.json = n;
9093                 records[records.length] = record;
9094             }
9095             return {
9096                 records : records,
9097                 totalRecords : records.length
9098             };
9099     }
9100 });/*
9101  * - LGPL
9102  * * 
9103  */
9104
9105 /**
9106  * @class Roo.bootstrap.ComboBox
9107  * @extends Roo.bootstrap.TriggerField
9108  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9109  * @cfg {Boolean} append (true|false) default false
9110  * @constructor
9111  * Create a new ComboBox.
9112  * @param {Object} config Configuration options
9113  */
9114 Roo.bootstrap.ComboBox = function(config){
9115     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9116     this.addEvents({
9117         /**
9118          * @event expand
9119          * Fires when the dropdown list is expanded
9120              * @param {Roo.bootstrap.ComboBox} combo This combo box
9121              */
9122         'expand' : true,
9123         /**
9124          * @event collapse
9125          * Fires when the dropdown list is collapsed
9126              * @param {Roo.bootstrap.ComboBox} combo This combo box
9127              */
9128         'collapse' : true,
9129         /**
9130          * @event beforeselect
9131          * Fires before a list item is selected. Return false to cancel the selection.
9132              * @param {Roo.bootstrap.ComboBox} combo This combo box
9133              * @param {Roo.data.Record} record The data record returned from the underlying store
9134              * @param {Number} index The index of the selected item in the dropdown list
9135              */
9136         'beforeselect' : true,
9137         /**
9138          * @event select
9139          * Fires when a list item is selected
9140              * @param {Roo.bootstrap.ComboBox} combo This combo box
9141              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9142              * @param {Number} index The index of the selected item in the dropdown list
9143              */
9144         'select' : true,
9145         /**
9146          * @event beforequery
9147          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9148          * The event object passed has these properties:
9149              * @param {Roo.bootstrap.ComboBox} combo This combo box
9150              * @param {String} query The query
9151              * @param {Boolean} forceAll true to force "all" query
9152              * @param {Boolean} cancel true to cancel the query
9153              * @param {Object} e The query event object
9154              */
9155         'beforequery': true,
9156          /**
9157          * @event add
9158          * Fires when the 'add' icon is pressed (add a listener to enable add button)
9159              * @param {Roo.bootstrap.ComboBox} combo This combo box
9160              */
9161         'add' : true,
9162         /**
9163          * @event edit
9164          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9165              * @param {Roo.bootstrap.ComboBox} combo This combo box
9166              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9167              */
9168         'edit' : true,
9169         /**
9170          * @event remove
9171          * Fires when the remove value from the combobox array
9172              * @param {Roo.bootstrap.ComboBox} combo This combo box
9173              */
9174         'remove' : true
9175         
9176     });
9177     
9178     
9179     this.selectedIndex = -1;
9180     if(this.mode == 'local'){
9181         if(config.queryDelay === undefined){
9182             this.queryDelay = 10;
9183         }
9184         if(config.minChars === undefined){
9185             this.minChars = 0;
9186         }
9187     }
9188 };
9189
9190 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9191      
9192     /**
9193      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9194      * rendering into an Roo.Editor, defaults to false)
9195      */
9196     /**
9197      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9198      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9199      */
9200     /**
9201      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9202      */
9203     /**
9204      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9205      * the dropdown list (defaults to undefined, with no header element)
9206      */
9207
9208      /**
9209      * @cfg {String/Roo.Template} tpl The template to use to render the output
9210      */
9211      
9212      /**
9213      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9214      */
9215     listWidth: undefined,
9216     /**
9217      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9218      * mode = 'remote' or 'text' if mode = 'local')
9219      */
9220     displayField: undefined,
9221     /**
9222      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9223      * mode = 'remote' or 'value' if mode = 'local'). 
9224      * Note: use of a valueField requires the user make a selection
9225      * in order for a value to be mapped.
9226      */
9227     valueField: undefined,
9228     
9229     
9230     /**
9231      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9232      * field's data value (defaults to the underlying DOM element's name)
9233      */
9234     hiddenName: undefined,
9235     /**
9236      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9237      */
9238     listClass: '',
9239     /**
9240      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9241      */
9242     selectedClass: 'active',
9243     
9244     /**
9245      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9246      */
9247     shadow:'sides',
9248     /**
9249      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9250      * anchor positions (defaults to 'tl-bl')
9251      */
9252     listAlign: 'tl-bl?',
9253     /**
9254      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9255      */
9256     maxHeight: 300,
9257     /**
9258      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
9259      * query specified by the allQuery config option (defaults to 'query')
9260      */
9261     triggerAction: 'query',
9262     /**
9263      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9264      * (defaults to 4, does not apply if editable = false)
9265      */
9266     minChars : 4,
9267     /**
9268      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9269      * delay (typeAheadDelay) if it matches a known value (defaults to false)
9270      */
9271     typeAhead: false,
9272     /**
9273      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
9274      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
9275      */
9276     queryDelay: 500,
9277     /**
9278      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
9279      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
9280      */
9281     pageSize: 0,
9282     /**
9283      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
9284      * when editable = true (defaults to false)
9285      */
9286     selectOnFocus:false,
9287     /**
9288      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
9289      */
9290     queryParam: 'query',
9291     /**
9292      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
9293      * when mode = 'remote' (defaults to 'Loading...')
9294      */
9295     loadingText: 'Loading...',
9296     /**
9297      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
9298      */
9299     resizable: false,
9300     /**
9301      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
9302      */
9303     handleHeight : 8,
9304     /**
9305      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
9306      * traditional select (defaults to true)
9307      */
9308     editable: true,
9309     /**
9310      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
9311      */
9312     allQuery: '',
9313     /**
9314      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
9315      */
9316     mode: 'remote',
9317     /**
9318      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
9319      * listWidth has a higher value)
9320      */
9321     minListWidth : 70,
9322     /**
9323      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
9324      * allow the user to set arbitrary text into the field (defaults to false)
9325      */
9326     forceSelection:false,
9327     /**
9328      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
9329      * if typeAhead = true (defaults to 250)
9330      */
9331     typeAheadDelay : 250,
9332     /**
9333      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
9334      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
9335      */
9336     valueNotFoundText : undefined,
9337     /**
9338      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
9339      */
9340     blockFocus : false,
9341     
9342     /**
9343      * @cfg {Boolean} disableClear Disable showing of clear button.
9344      */
9345     disableClear : false,
9346     /**
9347      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
9348      */
9349     alwaysQuery : false,
9350     
9351     /**
9352      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
9353      */
9354     multiple : false,
9355     
9356     //private
9357     addicon : false,
9358     editicon: false,
9359     
9360     page: 0,
9361     hasQuery: false,
9362     append: false,
9363     loadNext: false,
9364     item: [],
9365     
9366     // element that contains real text value.. (when hidden is used..)
9367      
9368     // private
9369     initEvents: function(){
9370         
9371         if (!this.store) {
9372             throw "can not find store for combo";
9373         }
9374         this.store = Roo.factory(this.store, Roo.data);
9375         
9376         
9377         
9378         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9379         
9380         
9381         if(this.hiddenName){
9382             
9383             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
9384             
9385             this.hiddenField.dom.value =
9386                 this.hiddenValue !== undefined ? this.hiddenValue :
9387                 this.value !== undefined ? this.value : '';
9388
9389             // prevent input submission
9390             this.el.dom.removeAttribute('name');
9391             this.hiddenField.dom.setAttribute('name', this.hiddenName);
9392              
9393              
9394         }
9395         //if(Roo.isGecko){
9396         //    this.el.dom.setAttribute('autocomplete', 'off');
9397         //}
9398
9399         var cls = 'x-combo-list';
9400         this.list = this.el.select('ul.dropdown-menu',true).first();
9401
9402         //this.list = new Roo.Layer({
9403         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
9404         //});
9405         
9406         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
9407         this.list.setWidth(lw);
9408         
9409         this.list.on('mouseover', this.onViewOver, this);
9410         this.list.on('mousemove', this.onViewMove, this);
9411         
9412         this.list.on('scroll', this.onViewScroll, this);
9413         
9414         /*
9415         this.list.swallowEvent('mousewheel');
9416         this.assetHeight = 0;
9417
9418         if(this.title){
9419             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
9420             this.assetHeight += this.header.getHeight();
9421         }
9422
9423         this.innerList = this.list.createChild({cls:cls+'-inner'});
9424         this.innerList.on('mouseover', this.onViewOver, this);
9425         this.innerList.on('mousemove', this.onViewMove, this);
9426         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9427         
9428         if(this.allowBlank && !this.pageSize && !this.disableClear){
9429             this.footer = this.list.createChild({cls:cls+'-ft'});
9430             this.pageTb = new Roo.Toolbar(this.footer);
9431            
9432         }
9433         if(this.pageSize){
9434             this.footer = this.list.createChild({cls:cls+'-ft'});
9435             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
9436                     {pageSize: this.pageSize});
9437             
9438         }
9439         
9440         if (this.pageTb && this.allowBlank && !this.disableClear) {
9441             var _this = this;
9442             this.pageTb.add(new Roo.Toolbar.Fill(), {
9443                 cls: 'x-btn-icon x-btn-clear',
9444                 text: '&#160;',
9445                 handler: function()
9446                 {
9447                     _this.collapse();
9448                     _this.clearValue();
9449                     _this.onSelect(false, -1);
9450                 }
9451             });
9452         }
9453         if (this.footer) {
9454             this.assetHeight += this.footer.getHeight();
9455         }
9456         */
9457             
9458         if(!this.tpl){
9459             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
9460         }
9461
9462         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
9463             singleSelect:true, store: this.store, selectedClass: this.selectedClass
9464         });
9465         //this.view.wrapEl.setDisplayed(false);
9466         this.view.on('click', this.onViewClick, this);
9467         
9468         
9469         
9470         this.store.on('beforeload', this.onBeforeLoad, this);
9471         this.store.on('load', this.onLoad, this);
9472         this.store.on('loadexception', this.onLoadException, this);
9473         /*
9474         if(this.resizable){
9475             this.resizer = new Roo.Resizable(this.list,  {
9476                pinned:true, handles:'se'
9477             });
9478             this.resizer.on('resize', function(r, w, h){
9479                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
9480                 this.listWidth = w;
9481                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
9482                 this.restrictHeight();
9483             }, this);
9484             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
9485         }
9486         */
9487         if(!this.editable){
9488             this.editable = true;
9489             this.setEditable(false);
9490         }
9491         
9492         /*
9493         
9494         if (typeof(this.events.add.listeners) != 'undefined') {
9495             
9496             this.addicon = this.wrap.createChild(
9497                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
9498        
9499             this.addicon.on('click', function(e) {
9500                 this.fireEvent('add', this);
9501             }, this);
9502         }
9503         if (typeof(this.events.edit.listeners) != 'undefined') {
9504             
9505             this.editicon = this.wrap.createChild(
9506                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
9507             if (this.addicon) {
9508                 this.editicon.setStyle('margin-left', '40px');
9509             }
9510             this.editicon.on('click', function(e) {
9511                 
9512                 // we fire even  if inothing is selected..
9513                 this.fireEvent('edit', this, this.lastData );
9514                 
9515             }, this);
9516         }
9517         */
9518         
9519         this.keyNav = new Roo.KeyNav(this.inputEl(), {
9520             "up" : function(e){
9521                 this.inKeyMode = true;
9522                 this.selectPrev();
9523             },
9524
9525             "down" : function(e){
9526                 if(!this.isExpanded()){
9527                     this.onTriggerClick();
9528                 }else{
9529                     this.inKeyMode = true;
9530                     this.selectNext();
9531                 }
9532             },
9533
9534             "enter" : function(e){
9535                 this.onViewClick();
9536                 //return true;
9537             },
9538
9539             "esc" : function(e){
9540                 this.collapse();
9541             },
9542
9543             "tab" : function(e){
9544                 this.collapse();
9545                 
9546                 if(this.fireEvent("specialkey", this, e)){
9547                     this.onViewClick(false);
9548                 }
9549                 
9550                 return true;
9551             },
9552
9553             scope : this,
9554
9555             doRelay : function(foo, bar, hname){
9556                 if(hname == 'down' || this.scope.isExpanded()){
9557                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
9558                 }
9559                 return true;
9560             },
9561
9562             forceKeyDown: true
9563         });
9564         
9565         
9566         this.queryDelay = Math.max(this.queryDelay || 10,
9567                 this.mode == 'local' ? 10 : 250);
9568         
9569         
9570         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
9571         
9572         if(this.typeAhead){
9573             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
9574         }
9575         if(this.editable !== false){
9576             this.inputEl().on("keyup", this.onKeyUp, this);
9577         }
9578         if(this.forceSelection){
9579             this.inputEl().on('blur', this.doForce, this);
9580         }
9581         
9582         if(this.multiple){
9583             this.choices = this.el.select('ul.select2-choices', true).first();
9584             this.searchField = this.el.select('ul li.select2-search-field', true).first();
9585         }
9586     },
9587
9588     onDestroy : function(){
9589         if(this.view){
9590             this.view.setStore(null);
9591             this.view.el.removeAllListeners();
9592             this.view.el.remove();
9593             this.view.purgeListeners();
9594         }
9595         if(this.list){
9596             this.list.dom.innerHTML  = '';
9597         }
9598         if(this.store){
9599             this.store.un('beforeload', this.onBeforeLoad, this);
9600             this.store.un('load', this.onLoad, this);
9601             this.store.un('loadexception', this.onLoadException, this);
9602         }
9603         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9604     },
9605
9606     // private
9607     fireKey : function(e){
9608         if(e.isNavKeyPress() && !this.list.isVisible()){
9609             this.fireEvent("specialkey", this, e);
9610         }
9611     },
9612
9613     // private
9614     onResize: function(w, h){
9615 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9616 //        
9617 //        if(typeof w != 'number'){
9618 //            // we do not handle it!?!?
9619 //            return;
9620 //        }
9621 //        var tw = this.trigger.getWidth();
9622 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9623 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9624 //        var x = w - tw;
9625 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9626 //            
9627 //        //this.trigger.setStyle('left', x+'px');
9628 //        
9629 //        if(this.list && this.listWidth === undefined){
9630 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9631 //            this.list.setWidth(lw);
9632 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9633 //        }
9634         
9635     
9636         
9637     },
9638
9639     /**
9640      * Allow or prevent the user from directly editing the field text.  If false is passed,
9641      * the user will only be able to select from the items defined in the dropdown list.  This method
9642      * is the runtime equivalent of setting the 'editable' config option at config time.
9643      * @param {Boolean} value True to allow the user to directly edit the field text
9644      */
9645     setEditable : function(value){
9646         if(value == this.editable){
9647             return;
9648         }
9649         this.editable = value;
9650         if(!value){
9651             this.inputEl().dom.setAttribute('readOnly', true);
9652             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9653             this.inputEl().addClass('x-combo-noedit');
9654         }else{
9655             this.inputEl().dom.setAttribute('readOnly', false);
9656             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9657             this.inputEl().removeClass('x-combo-noedit');
9658         }
9659     },
9660
9661     // private
9662     
9663     onBeforeLoad : function(combo,opts){
9664         if(!this.hasFocus){
9665             return;
9666         }
9667          if (!opts.add) {
9668             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9669          }
9670         this.restrictHeight();
9671         this.selectedIndex = -1;
9672     },
9673
9674     // private
9675     onLoad : function(){
9676         
9677         this.hasQuery = false;
9678         
9679         if(!this.hasFocus){
9680             return;
9681         }
9682         
9683         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9684             this.loading.hide();
9685         }
9686         
9687         if(this.store.getCount() > 0){
9688             this.expand();
9689             this.restrictHeight();
9690             if(this.lastQuery == this.allQuery){
9691                 if(this.editable){
9692                     this.inputEl().dom.select();
9693                 }
9694                 if(!this.selectByValue(this.value, true)){
9695                     this.select(0, true);
9696                 }
9697             }else{
9698                 this.selectNext();
9699                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9700                     this.taTask.delay(this.typeAheadDelay);
9701                 }
9702             }
9703         }else{
9704             this.onEmptyResults();
9705         }
9706         
9707         //this.el.focus();
9708     },
9709     // private
9710     onLoadException : function()
9711     {
9712         this.hasQuery = false;
9713         
9714         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9715             this.loading.hide();
9716         }
9717         
9718         this.collapse();
9719         Roo.log(this.store.reader.jsonData);
9720         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9721             // fixme
9722             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9723         }
9724         
9725         
9726     },
9727     // private
9728     onTypeAhead : function(){
9729         if(this.store.getCount() > 0){
9730             var r = this.store.getAt(0);
9731             var newValue = r.data[this.displayField];
9732             var len = newValue.length;
9733             var selStart = this.getRawValue().length;
9734             
9735             if(selStart != len){
9736                 this.setRawValue(newValue);
9737                 this.selectText(selStart, newValue.length);
9738             }
9739         }
9740     },
9741
9742     // private
9743     onSelect : function(record, index){
9744         
9745         if(this.fireEvent('beforeselect', this, record, index) !== false){
9746         
9747             this.setFromData(index > -1 ? record.data : false);
9748             
9749             this.collapse();
9750             this.fireEvent('select', this, record, index);
9751         }
9752     },
9753
9754     /**
9755      * Returns the currently selected field value or empty string if no value is set.
9756      * @return {String} value The selected value
9757      */
9758     getValue : function(){
9759         
9760         if(this.multiple){
9761             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9762         }
9763         
9764         if(this.valueField){
9765             return typeof this.value != 'undefined' ? this.value : '';
9766         }else{
9767             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9768         }
9769     },
9770
9771     /**
9772      * Clears any text/value currently set in the field
9773      */
9774     clearValue : function(){
9775         if(this.hiddenField){
9776             this.hiddenField.dom.value = '';
9777         }
9778         this.value = '';
9779         this.setRawValue('');
9780         this.lastSelectionText = '';
9781         
9782     },
9783
9784     /**
9785      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9786      * will be displayed in the field.  If the value does not match the data value of an existing item,
9787      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9788      * Otherwise the field will be blank (although the value will still be set).
9789      * @param {String} value The value to match
9790      */
9791     setValue : function(v){
9792         if(this.multiple){
9793             this.syncValue();
9794             return;
9795         }
9796         
9797         var text = v;
9798         if(this.valueField){
9799             var r = this.findRecord(this.valueField, v);
9800             if(r){
9801                 text = r.data[this.displayField];
9802             }else if(this.valueNotFoundText !== undefined){
9803                 text = this.valueNotFoundText;
9804             }
9805         }
9806         this.lastSelectionText = text;
9807         if(this.hiddenField){
9808             this.hiddenField.dom.value = v;
9809         }
9810         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9811         this.value = v;
9812     },
9813     /**
9814      * @property {Object} the last set data for the element
9815      */
9816     
9817     lastData : false,
9818     /**
9819      * Sets the value of the field based on a object which is related to the record format for the store.
9820      * @param {Object} value the value to set as. or false on reset?
9821      */
9822     setFromData : function(o){
9823         
9824         if(this.multiple){
9825             this.addItem(o);
9826             return;
9827         }
9828             
9829         var dv = ''; // display value
9830         var vv = ''; // value value..
9831         this.lastData = o;
9832         if (this.displayField) {
9833             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9834         } else {
9835             // this is an error condition!!!
9836             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9837         }
9838         
9839         if(this.valueField){
9840             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9841         }
9842         
9843         if(this.hiddenField){
9844             this.hiddenField.dom.value = vv;
9845             
9846             this.lastSelectionText = dv;
9847             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9848             this.value = vv;
9849             return;
9850         }
9851         // no hidden field.. - we store the value in 'value', but still display
9852         // display field!!!!
9853         this.lastSelectionText = dv;
9854         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9855         this.value = vv;
9856         
9857         
9858     },
9859     // private
9860     reset : function(){
9861         // overridden so that last data is reset..
9862         this.setValue(this.originalValue);
9863         this.clearInvalid();
9864         this.lastData = false;
9865         if (this.view) {
9866             this.view.clearSelections();
9867         }
9868     },
9869     // private
9870     findRecord : function(prop, value){
9871         var record;
9872         if(this.store.getCount() > 0){
9873             this.store.each(function(r){
9874                 if(r.data[prop] == value){
9875                     record = r;
9876                     return false;
9877                 }
9878                 return true;
9879             });
9880         }
9881         return record;
9882     },
9883     
9884     getName: function()
9885     {
9886         // returns hidden if it's set..
9887         if (!this.rendered) {return ''};
9888         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9889         
9890     },
9891     // private
9892     onViewMove : function(e, t){
9893         this.inKeyMode = false;
9894     },
9895
9896     // private
9897     onViewOver : function(e, t){
9898         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9899             return;
9900         }
9901         var item = this.view.findItemFromChild(t);
9902         if(item){
9903             var index = this.view.indexOf(item);
9904             this.select(index, false);
9905         }
9906     },
9907
9908     // private
9909     onViewClick : function(doFocus)
9910     {
9911         var index = this.view.getSelectedIndexes()[0];
9912         var r = this.store.getAt(index);
9913         if(r){
9914             this.onSelect(r, index);
9915         }
9916         if(doFocus !== false && !this.blockFocus){
9917             this.inputEl().focus();
9918         }
9919     },
9920
9921     // private
9922     restrictHeight : function(){
9923         //this.innerList.dom.style.height = '';
9924         //var inner = this.innerList.dom;
9925         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9926         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9927         //this.list.beginUpdate();
9928         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9929         this.list.alignTo(this.inputEl(), this.listAlign);
9930         //this.list.endUpdate();
9931     },
9932
9933     // private
9934     onEmptyResults : function(){
9935         this.collapse();
9936     },
9937
9938     /**
9939      * Returns true if the dropdown list is expanded, else false.
9940      */
9941     isExpanded : function(){
9942         return this.list.isVisible();
9943     },
9944
9945     /**
9946      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9947      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9948      * @param {String} value The data value of the item to select
9949      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9950      * selected item if it is not currently in view (defaults to true)
9951      * @return {Boolean} True if the value matched an item in the list, else false
9952      */
9953     selectByValue : function(v, scrollIntoView){
9954         if(v !== undefined && v !== null){
9955             var r = this.findRecord(this.valueField || this.displayField, v);
9956             if(r){
9957                 this.select(this.store.indexOf(r), scrollIntoView);
9958                 return true;
9959             }
9960         }
9961         return false;
9962     },
9963
9964     /**
9965      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9966      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9967      * @param {Number} index The zero-based index of the list item to select
9968      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9969      * selected item if it is not currently in view (defaults to true)
9970      */
9971     select : function(index, scrollIntoView){
9972         this.selectedIndex = index;
9973         this.view.select(index);
9974         if(scrollIntoView !== false){
9975             var el = this.view.getNode(index);
9976             if(el){
9977                 //this.innerList.scrollChildIntoView(el, false);
9978                 
9979             }
9980         }
9981     },
9982
9983     // private
9984     selectNext : function(){
9985         var ct = this.store.getCount();
9986         if(ct > 0){
9987             if(this.selectedIndex == -1){
9988                 this.select(0);
9989             }else if(this.selectedIndex < ct-1){
9990                 this.select(this.selectedIndex+1);
9991             }
9992         }
9993     },
9994
9995     // private
9996     selectPrev : function(){
9997         var ct = this.store.getCount();
9998         if(ct > 0){
9999             if(this.selectedIndex == -1){
10000                 this.select(0);
10001             }else if(this.selectedIndex != 0){
10002                 this.select(this.selectedIndex-1);
10003             }
10004         }
10005     },
10006
10007     // private
10008     onKeyUp : function(e){
10009         if(this.editable !== false && !e.isSpecialKey()){
10010             this.lastKey = e.getKey();
10011             this.dqTask.delay(this.queryDelay);
10012         }
10013     },
10014
10015     // private
10016     validateBlur : function(){
10017         return !this.list || !this.list.isVisible();   
10018     },
10019
10020     // private
10021     initQuery : function(){
10022         this.doQuery(this.getRawValue());
10023     },
10024
10025     // private
10026     doForce : function(){
10027         if(this.inputEl().dom.value.length > 0){
10028             this.inputEl().dom.value =
10029                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
10030              
10031         }
10032     },
10033
10034     /**
10035      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
10036      * query allowing the query action to be canceled if needed.
10037      * @param {String} query The SQL query to execute
10038      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
10039      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
10040      * saved in the current store (defaults to false)
10041      */
10042     doQuery : function(q, forceAll){
10043         
10044         if(q === undefined || q === null){
10045             q = '';
10046         }
10047         var qe = {
10048             query: q,
10049             forceAll: forceAll,
10050             combo: this,
10051             cancel:false
10052         };
10053         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
10054             return false;
10055         }
10056         q = qe.query;
10057         
10058         forceAll = qe.forceAll;
10059         if(forceAll === true || (q.length >= this.minChars)){
10060             
10061             this.hasQuery = true;
10062             
10063             if(this.lastQuery != q || this.alwaysQuery){
10064                 this.lastQuery = q;
10065                 if(this.mode == 'local'){
10066                     this.selectedIndex = -1;
10067                     if(forceAll){
10068                         this.store.clearFilter();
10069                     }else{
10070                         this.store.filter(this.displayField, q);
10071                     }
10072                     this.onLoad();
10073                 }else{
10074                     this.store.baseParams[this.queryParam] = q;
10075                     
10076                     var options = {params : this.getParams(q)};
10077                     
10078                     if(this.loadNext){
10079                         options.add = true;
10080                         options.params.start = this.page * this.pageSize;
10081                     }
10082                     
10083                     this.store.load(options);
10084                     this.expand();
10085                 }
10086             }else{
10087                 this.selectedIndex = -1;
10088                 this.onLoad();   
10089             }
10090         }
10091         
10092         this.loadNext = false;
10093     },
10094
10095     // private
10096     getParams : function(q){
10097         var p = {};
10098         //p[this.queryParam] = q;
10099         
10100         if(this.pageSize){
10101             p.start = 0;
10102             p.limit = this.pageSize;
10103         }
10104         return p;
10105     },
10106
10107     /**
10108      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
10109      */
10110     collapse : function(){
10111         if(!this.isExpanded()){
10112             return;
10113         }
10114         
10115         this.list.hide();
10116         Roo.get(document).un('mousedown', this.collapseIf, this);
10117         Roo.get(document).un('mousewheel', this.collapseIf, this);
10118         if (!this.editable) {
10119             Roo.get(document).un('keydown', this.listKeyPress, this);
10120         }
10121         this.fireEvent('collapse', this);
10122     },
10123
10124     // private
10125     collapseIf : function(e){
10126         var in_combo  = e.within(this.el);
10127         var in_list =  e.within(this.list);
10128         
10129         if (in_combo || in_list) {
10130             //e.stopPropagation();
10131             return;
10132         }
10133
10134         this.collapse();
10135         
10136     },
10137
10138     /**
10139      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
10140      */
10141     expand : function(){
10142        
10143         if(this.isExpanded() || !this.hasFocus){
10144             return;
10145         }
10146          Roo.log('expand');
10147         this.list.alignTo(this.inputEl(), this.listAlign);
10148         this.list.show();
10149         Roo.get(document).on('mousedown', this.collapseIf, this);
10150         Roo.get(document).on('mousewheel', this.collapseIf, this);
10151         if (!this.editable) {
10152             Roo.get(document).on('keydown', this.listKeyPress, this);
10153         }
10154         
10155         this.fireEvent('expand', this);
10156     },
10157
10158     // private
10159     // Implements the default empty TriggerField.onTriggerClick function
10160     onTriggerClick : function()
10161     {
10162         Roo.log('trigger click');
10163         
10164         if(this.disabled){
10165             return;
10166         }
10167         
10168         this.page = 0;
10169         this.loadNext = false;
10170         
10171         if(this.isExpanded()){
10172             this.collapse();
10173             if (!this.blockFocus) {
10174                 this.inputEl().focus();
10175             }
10176             
10177         }else {
10178             this.hasFocus = true;
10179             if(this.triggerAction == 'all') {
10180                 this.doQuery(this.allQuery, true);
10181             } else {
10182                 this.doQuery(this.getRawValue());
10183             }
10184             if (!this.blockFocus) {
10185                 this.inputEl().focus();
10186             }
10187         }
10188     },
10189     listKeyPress : function(e)
10190     {
10191         //Roo.log('listkeypress');
10192         // scroll to first matching element based on key pres..
10193         if (e.isSpecialKey()) {
10194             return false;
10195         }
10196         var k = String.fromCharCode(e.getKey()).toUpperCase();
10197         //Roo.log(k);
10198         var match  = false;
10199         var csel = this.view.getSelectedNodes();
10200         var cselitem = false;
10201         if (csel.length) {
10202             var ix = this.view.indexOf(csel[0]);
10203             cselitem  = this.store.getAt(ix);
10204             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
10205                 cselitem = false;
10206             }
10207             
10208         }
10209         
10210         this.store.each(function(v) { 
10211             if (cselitem) {
10212                 // start at existing selection.
10213                 if (cselitem.id == v.id) {
10214                     cselitem = false;
10215                 }
10216                 return true;
10217             }
10218                 
10219             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
10220                 match = this.store.indexOf(v);
10221                 return false;
10222             }
10223             return true;
10224         }, this);
10225         
10226         if (match === false) {
10227             return true; // no more action?
10228         }
10229         // scroll to?
10230         this.view.select(match);
10231         var sn = Roo.get(this.view.getSelectedNodes()[0])
10232         //sn.scrollIntoView(sn.dom.parentNode, false);
10233     },
10234     
10235     onViewScroll : function(e, t){
10236         
10237         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
10238             return;
10239         }
10240         
10241         this.hasQuery = true;
10242         
10243         this.loading = this.list.select('.loading', true).first();
10244         
10245         if(this.loading === null){
10246             this.list.createChild({
10247                 tag: 'div',
10248                 cls: 'loading select2-more-results select2-active',
10249                 html: 'Loading more results...'
10250             })
10251             
10252             this.loading = this.list.select('.loading', true).first();
10253             
10254             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
10255             
10256             this.loading.hide();
10257         }
10258         
10259         this.loading.show();
10260         
10261         var _combo = this;
10262         
10263         this.page++;
10264         this.loadNext = true;
10265         
10266         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
10267         
10268         return;
10269     },
10270     
10271     addItem : function(o)
10272     {   
10273         var dv = ''; // display value
10274         
10275         if (this.displayField) {
10276             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10277         } else {
10278             // this is an error condition!!!
10279             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10280         }
10281         
10282         if(!dv.length){
10283             return;
10284         }
10285         
10286         var choice = this.choices.createChild({
10287             tag: 'li',
10288             cls: 'select2-search-choice',
10289             cn: [
10290                 {
10291                     tag: 'div',
10292                     html: dv
10293                 },
10294                 {
10295                     tag: 'a',
10296                     href: '#',
10297                     cls: 'select2-search-choice-close',
10298                     tabindex: '-1'
10299                 }
10300             ]
10301             
10302         }, this.searchField);
10303         
10304         var close = choice.select('a.select2-search-choice-close', true).first()
10305         
10306         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
10307         
10308         this.item.push(o);
10309         this.lastData = o;
10310         
10311         this.syncValue();
10312         
10313         this.inputEl().dom.value = '';
10314         
10315     },
10316     
10317     onRemoveItem : function(e, _self, o)
10318     {
10319         e.preventDefault();
10320         var index = this.item.indexOf(o.data) * 1;
10321         
10322         if( index < 0){
10323             Roo.log('not this item?!');
10324             return;
10325         }
10326         
10327         this.item.splice(index, 1);
10328         o.item.remove();
10329         
10330         this.syncValue();
10331         
10332         this.fireEvent('remove', this, e);
10333         
10334     },
10335     
10336     syncValue : function()
10337     {
10338         if(!this.item.length){
10339             this.clearValue();
10340             return;
10341         }
10342             
10343         var value = [];
10344         var _this = this;
10345         Roo.each(this.item, function(i){
10346             if(_this.valueField){
10347                 value.push(i[_this.valueField]);
10348                 return;
10349             }
10350
10351             value.push(i);
10352         });
10353
10354         this.value = value.join(',');
10355
10356         if(this.hiddenField){
10357             this.hiddenField.dom.value = this.value;
10358         }
10359     },
10360     
10361     clearItem : function()
10362     {
10363         if(!this.multiple){
10364             return;
10365         }
10366         
10367         this.item = [];
10368         
10369         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
10370            c.remove();
10371         });
10372         
10373         this.syncValue();
10374     }
10375     
10376     
10377
10378     /** 
10379     * @cfg {Boolean} grow 
10380     * @hide 
10381     */
10382     /** 
10383     * @cfg {Number} growMin 
10384     * @hide 
10385     */
10386     /** 
10387     * @cfg {Number} growMax 
10388     * @hide 
10389     */
10390     /**
10391      * @hide
10392      * @method autoSize
10393      */
10394 });
10395 /*
10396  * Based on:
10397  * Ext JS Library 1.1.1
10398  * Copyright(c) 2006-2007, Ext JS, LLC.
10399  *
10400  * Originally Released Under LGPL - original licence link has changed is not relivant.
10401  *
10402  * Fork - LGPL
10403  * <script type="text/javascript">
10404  */
10405
10406 /**
10407  * @class Roo.View
10408  * @extends Roo.util.Observable
10409  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
10410  * This class also supports single and multi selection modes. <br>
10411  * Create a data model bound view:
10412  <pre><code>
10413  var store = new Roo.data.Store(...);
10414
10415  var view = new Roo.View({
10416     el : "my-element",
10417     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
10418  
10419     singleSelect: true,
10420     selectedClass: "ydataview-selected",
10421     store: store
10422  });
10423
10424  // listen for node click?
10425  view.on("click", function(vw, index, node, e){
10426  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
10427  });
10428
10429  // load XML data
10430  dataModel.load("foobar.xml");
10431  </code></pre>
10432  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
10433  * <br><br>
10434  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
10435  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
10436  * 
10437  * Note: old style constructor is still suported (container, template, config)
10438  * 
10439  * @constructor
10440  * Create a new View
10441  * @param {Object} config The config object
10442  * 
10443  */
10444 Roo.View = function(config, depreciated_tpl, depreciated_config){
10445     
10446     if (typeof(depreciated_tpl) == 'undefined') {
10447         // new way.. - universal constructor.
10448         Roo.apply(this, config);
10449         this.el  = Roo.get(this.el);
10450     } else {
10451         // old format..
10452         this.el  = Roo.get(config);
10453         this.tpl = depreciated_tpl;
10454         Roo.apply(this, depreciated_config);
10455     }
10456     this.wrapEl  = this.el.wrap().wrap();
10457     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
10458     
10459     
10460     if(typeof(this.tpl) == "string"){
10461         this.tpl = new Roo.Template(this.tpl);
10462     } else {
10463         // support xtype ctors..
10464         this.tpl = new Roo.factory(this.tpl, Roo);
10465     }
10466     
10467     
10468     this.tpl.compile();
10469    
10470   
10471     
10472      
10473     /** @private */
10474     this.addEvents({
10475         /**
10476          * @event beforeclick
10477          * Fires before a click is processed. Returns false to cancel the default action.
10478          * @param {Roo.View} this
10479          * @param {Number} index The index of the target node
10480          * @param {HTMLElement} node The target node
10481          * @param {Roo.EventObject} e The raw event object
10482          */
10483             "beforeclick" : true,
10484         /**
10485          * @event click
10486          * Fires when a template node is clicked.
10487          * @param {Roo.View} this
10488          * @param {Number} index The index of the target node
10489          * @param {HTMLElement} node The target node
10490          * @param {Roo.EventObject} e The raw event object
10491          */
10492             "click" : true,
10493         /**
10494          * @event dblclick
10495          * Fires when a template node is double clicked.
10496          * @param {Roo.View} this
10497          * @param {Number} index The index of the target node
10498          * @param {HTMLElement} node The target node
10499          * @param {Roo.EventObject} e The raw event object
10500          */
10501             "dblclick" : true,
10502         /**
10503          * @event contextmenu
10504          * Fires when a template node is right clicked.
10505          * @param {Roo.View} this
10506          * @param {Number} index The index of the target node
10507          * @param {HTMLElement} node The target node
10508          * @param {Roo.EventObject} e The raw event object
10509          */
10510             "contextmenu" : true,
10511         /**
10512          * @event selectionchange
10513          * Fires when the selected nodes change.
10514          * @param {Roo.View} this
10515          * @param {Array} selections Array of the selected nodes
10516          */
10517             "selectionchange" : true,
10518     
10519         /**
10520          * @event beforeselect
10521          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
10522          * @param {Roo.View} this
10523          * @param {HTMLElement} node The node to be selected
10524          * @param {Array} selections Array of currently selected nodes
10525          */
10526             "beforeselect" : true,
10527         /**
10528          * @event preparedata
10529          * Fires on every row to render, to allow you to change the data.
10530          * @param {Roo.View} this
10531          * @param {Object} data to be rendered (change this)
10532          */
10533           "preparedata" : true
10534           
10535           
10536         });
10537
10538
10539
10540     this.el.on({
10541         "click": this.onClick,
10542         "dblclick": this.onDblClick,
10543         "contextmenu": this.onContextMenu,
10544         scope:this
10545     });
10546
10547     this.selections = [];
10548     this.nodes = [];
10549     this.cmp = new Roo.CompositeElementLite([]);
10550     if(this.store){
10551         this.store = Roo.factory(this.store, Roo.data);
10552         this.setStore(this.store, true);
10553     }
10554     
10555     if ( this.footer && this.footer.xtype) {
10556            
10557          var fctr = this.wrapEl.appendChild(document.createElement("div"));
10558         
10559         this.footer.dataSource = this.store
10560         this.footer.container = fctr;
10561         this.footer = Roo.factory(this.footer, Roo);
10562         fctr.insertFirst(this.el);
10563         
10564         // this is a bit insane - as the paging toolbar seems to detach the el..
10565 //        dom.parentNode.parentNode.parentNode
10566          // they get detached?
10567     }
10568     
10569     
10570     Roo.View.superclass.constructor.call(this);
10571     
10572     
10573 };
10574
10575 Roo.extend(Roo.View, Roo.util.Observable, {
10576     
10577      /**
10578      * @cfg {Roo.data.Store} store Data store to load data from.
10579      */
10580     store : false,
10581     
10582     /**
10583      * @cfg {String|Roo.Element} el The container element.
10584      */
10585     el : '',
10586     
10587     /**
10588      * @cfg {String|Roo.Template} tpl The template used by this View 
10589      */
10590     tpl : false,
10591     /**
10592      * @cfg {String} dataName the named area of the template to use as the data area
10593      *                          Works with domtemplates roo-name="name"
10594      */
10595     dataName: false,
10596     /**
10597      * @cfg {String} selectedClass The css class to add to selected nodes
10598      */
10599     selectedClass : "x-view-selected",
10600      /**
10601      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10602      */
10603     emptyText : "",
10604     
10605     /**
10606      * @cfg {String} text to display on mask (default Loading)
10607      */
10608     mask : false,
10609     /**
10610      * @cfg {Boolean} multiSelect Allow multiple selection
10611      */
10612     multiSelect : false,
10613     /**
10614      * @cfg {Boolean} singleSelect Allow single selection
10615      */
10616     singleSelect:  false,
10617     
10618     /**
10619      * @cfg {Boolean} toggleSelect - selecting 
10620      */
10621     toggleSelect : false,
10622     
10623     /**
10624      * Returns the element this view is bound to.
10625      * @return {Roo.Element}
10626      */
10627     getEl : function(){
10628         return this.wrapEl;
10629     },
10630     
10631     
10632
10633     /**
10634      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10635      */
10636     refresh : function(){
10637         Roo.log('refresh');
10638         var t = this.tpl;
10639         
10640         // if we are using something like 'domtemplate', then
10641         // the what gets used is:
10642         // t.applySubtemplate(NAME, data, wrapping data..)
10643         // the outer template then get' applied with
10644         //     the store 'extra data'
10645         // and the body get's added to the
10646         //      roo-name="data" node?
10647         //      <span class='roo-tpl-{name}'></span> ?????
10648         
10649         
10650         
10651         this.clearSelections();
10652         this.el.update("");
10653         var html = [];
10654         var records = this.store.getRange();
10655         if(records.length < 1) {
10656             
10657             // is this valid??  = should it render a template??
10658             
10659             this.el.update(this.emptyText);
10660             return;
10661         }
10662         var el = this.el;
10663         if (this.dataName) {
10664             this.el.update(t.apply(this.store.meta)); //????
10665             el = this.el.child('.roo-tpl-' + this.dataName);
10666         }
10667         
10668         for(var i = 0, len = records.length; i < len; i++){
10669             var data = this.prepareData(records[i].data, i, records[i]);
10670             this.fireEvent("preparedata", this, data, i, records[i]);
10671             html[html.length] = Roo.util.Format.trim(
10672                 this.dataName ?
10673                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10674                     t.apply(data)
10675             );
10676         }
10677         
10678         
10679         
10680         el.update(html.join(""));
10681         this.nodes = el.dom.childNodes;
10682         this.updateIndexes(0);
10683     },
10684     
10685
10686     /**
10687      * Function to override to reformat the data that is sent to
10688      * the template for each node.
10689      * DEPRICATED - use the preparedata event handler.
10690      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10691      * a JSON object for an UpdateManager bound view).
10692      */
10693     prepareData : function(data, index, record)
10694     {
10695         this.fireEvent("preparedata", this, data, index, record);
10696         return data;
10697     },
10698
10699     onUpdate : function(ds, record){
10700          Roo.log('on update');   
10701         this.clearSelections();
10702         var index = this.store.indexOf(record);
10703         var n = this.nodes[index];
10704         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10705         n.parentNode.removeChild(n);
10706         this.updateIndexes(index, index);
10707     },
10708
10709     
10710     
10711 // --------- FIXME     
10712     onAdd : function(ds, records, index)
10713     {
10714         Roo.log(['on Add', ds, records, index] );        
10715         this.clearSelections();
10716         if(this.nodes.length == 0){
10717             this.refresh();
10718             return;
10719         }
10720         var n = this.nodes[index];
10721         for(var i = 0, len = records.length; i < len; i++){
10722             var d = this.prepareData(records[i].data, i, records[i]);
10723             if(n){
10724                 this.tpl.insertBefore(n, d);
10725             }else{
10726                 
10727                 this.tpl.append(this.el, d);
10728             }
10729         }
10730         this.updateIndexes(index);
10731     },
10732
10733     onRemove : function(ds, record, index){
10734         Roo.log('onRemove');
10735         this.clearSelections();
10736         var el = this.dataName  ?
10737             this.el.child('.roo-tpl-' + this.dataName) :
10738             this.el; 
10739         
10740         el.dom.removeChild(this.nodes[index]);
10741         this.updateIndexes(index);
10742     },
10743
10744     /**
10745      * Refresh an individual node.
10746      * @param {Number} index
10747      */
10748     refreshNode : function(index){
10749         this.onUpdate(this.store, this.store.getAt(index));
10750     },
10751
10752     updateIndexes : function(startIndex, endIndex){
10753         var ns = this.nodes;
10754         startIndex = startIndex || 0;
10755         endIndex = endIndex || ns.length - 1;
10756         for(var i = startIndex; i <= endIndex; i++){
10757             ns[i].nodeIndex = i;
10758         }
10759     },
10760
10761     /**
10762      * Changes the data store this view uses and refresh the view.
10763      * @param {Store} store
10764      */
10765     setStore : function(store, initial){
10766         if(!initial && this.store){
10767             this.store.un("datachanged", this.refresh);
10768             this.store.un("add", this.onAdd);
10769             this.store.un("remove", this.onRemove);
10770             this.store.un("update", this.onUpdate);
10771             this.store.un("clear", this.refresh);
10772             this.store.un("beforeload", this.onBeforeLoad);
10773             this.store.un("load", this.onLoad);
10774             this.store.un("loadexception", this.onLoad);
10775         }
10776         if(store){
10777           
10778             store.on("datachanged", this.refresh, this);
10779             store.on("add", this.onAdd, this);
10780             store.on("remove", this.onRemove, this);
10781             store.on("update", this.onUpdate, this);
10782             store.on("clear", this.refresh, this);
10783             store.on("beforeload", this.onBeforeLoad, this);
10784             store.on("load", this.onLoad, this);
10785             store.on("loadexception", this.onLoad, this);
10786         }
10787         
10788         if(store){
10789             this.refresh();
10790         }
10791     },
10792     /**
10793      * onbeforeLoad - masks the loading area.
10794      *
10795      */
10796     onBeforeLoad : function(store,opts)
10797     {
10798          Roo.log('onBeforeLoad');   
10799         if (!opts.add) {
10800             this.el.update("");
10801         }
10802         this.el.mask(this.mask ? this.mask : "Loading" ); 
10803     },
10804     onLoad : function ()
10805     {
10806         this.el.unmask();
10807     },
10808     
10809
10810     /**
10811      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10812      * @param {HTMLElement} node
10813      * @return {HTMLElement} The template node
10814      */
10815     findItemFromChild : function(node){
10816         var el = this.dataName  ?
10817             this.el.child('.roo-tpl-' + this.dataName,true) :
10818             this.el.dom; 
10819         
10820         if(!node || node.parentNode == el){
10821                     return node;
10822             }
10823             var p = node.parentNode;
10824             while(p && p != el){
10825             if(p.parentNode == el){
10826                 return p;
10827             }
10828             p = p.parentNode;
10829         }
10830             return null;
10831     },
10832
10833     /** @ignore */
10834     onClick : function(e){
10835         var item = this.findItemFromChild(e.getTarget());
10836         if(item){
10837             var index = this.indexOf(item);
10838             if(this.onItemClick(item, index, e) !== false){
10839                 this.fireEvent("click", this, index, item, e);
10840             }
10841         }else{
10842             this.clearSelections();
10843         }
10844     },
10845
10846     /** @ignore */
10847     onContextMenu : function(e){
10848         var item = this.findItemFromChild(e.getTarget());
10849         if(item){
10850             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10851         }
10852     },
10853
10854     /** @ignore */
10855     onDblClick : function(e){
10856         var item = this.findItemFromChild(e.getTarget());
10857         if(item){
10858             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10859         }
10860     },
10861
10862     onItemClick : function(item, index, e)
10863     {
10864         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10865             return false;
10866         }
10867         if (this.toggleSelect) {
10868             var m = this.isSelected(item) ? 'unselect' : 'select';
10869             Roo.log(m);
10870             var _t = this;
10871             _t[m](item, true, false);
10872             return true;
10873         }
10874         if(this.multiSelect || this.singleSelect){
10875             if(this.multiSelect && e.shiftKey && this.lastSelection){
10876                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10877             }else{
10878                 this.select(item, this.multiSelect && e.ctrlKey);
10879                 this.lastSelection = item;
10880             }
10881             e.preventDefault();
10882         }
10883         return true;
10884     },
10885
10886     /**
10887      * Get the number of selected nodes.
10888      * @return {Number}
10889      */
10890     getSelectionCount : function(){
10891         return this.selections.length;
10892     },
10893
10894     /**
10895      * Get the currently selected nodes.
10896      * @return {Array} An array of HTMLElements
10897      */
10898     getSelectedNodes : function(){
10899         return this.selections;
10900     },
10901
10902     /**
10903      * Get the indexes of the selected nodes.
10904      * @return {Array}
10905      */
10906     getSelectedIndexes : function(){
10907         var indexes = [], s = this.selections;
10908         for(var i = 0, len = s.length; i < len; i++){
10909             indexes.push(s[i].nodeIndex);
10910         }
10911         return indexes;
10912     },
10913
10914     /**
10915      * Clear all selections
10916      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10917      */
10918     clearSelections : function(suppressEvent){
10919         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10920             this.cmp.elements = this.selections;
10921             this.cmp.removeClass(this.selectedClass);
10922             this.selections = [];
10923             if(!suppressEvent){
10924                 this.fireEvent("selectionchange", this, this.selections);
10925             }
10926         }
10927     },
10928
10929     /**
10930      * Returns true if the passed node is selected
10931      * @param {HTMLElement/Number} node The node or node index
10932      * @return {Boolean}
10933      */
10934     isSelected : function(node){
10935         var s = this.selections;
10936         if(s.length < 1){
10937             return false;
10938         }
10939         node = this.getNode(node);
10940         return s.indexOf(node) !== -1;
10941     },
10942
10943     /**
10944      * Selects nodes.
10945      * @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
10946      * @param {Boolean} keepExisting (optional) true to keep existing selections
10947      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10948      */
10949     select : function(nodeInfo, keepExisting, suppressEvent){
10950         if(nodeInfo instanceof Array){
10951             if(!keepExisting){
10952                 this.clearSelections(true);
10953             }
10954             for(var i = 0, len = nodeInfo.length; i < len; i++){
10955                 this.select(nodeInfo[i], true, true);
10956             }
10957             return;
10958         } 
10959         var node = this.getNode(nodeInfo);
10960         if(!node || this.isSelected(node)){
10961             return; // already selected.
10962         }
10963         if(!keepExisting){
10964             this.clearSelections(true);
10965         }
10966         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10967             Roo.fly(node).addClass(this.selectedClass);
10968             this.selections.push(node);
10969             if(!suppressEvent){
10970                 this.fireEvent("selectionchange", this, this.selections);
10971             }
10972         }
10973         
10974         
10975     },
10976       /**
10977      * Unselects nodes.
10978      * @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
10979      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10980      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10981      */
10982     unselect : function(nodeInfo, keepExisting, suppressEvent)
10983     {
10984         if(nodeInfo instanceof Array){
10985             Roo.each(this.selections, function(s) {
10986                 this.unselect(s, nodeInfo);
10987             }, this);
10988             return;
10989         }
10990         var node = this.getNode(nodeInfo);
10991         if(!node || !this.isSelected(node)){
10992             Roo.log("not selected");
10993             return; // not selected.
10994         }
10995         // fireevent???
10996         var ns = [];
10997         Roo.each(this.selections, function(s) {
10998             if (s == node ) {
10999                 Roo.fly(node).removeClass(this.selectedClass);
11000
11001                 return;
11002             }
11003             ns.push(s);
11004         },this);
11005         
11006         this.selections= ns;
11007         this.fireEvent("selectionchange", this, this.selections);
11008     },
11009
11010     /**
11011      * Gets a template node.
11012      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
11013      * @return {HTMLElement} The node or null if it wasn't found
11014      */
11015     getNode : function(nodeInfo){
11016         if(typeof nodeInfo == "string"){
11017             return document.getElementById(nodeInfo);
11018         }else if(typeof nodeInfo == "number"){
11019             return this.nodes[nodeInfo];
11020         }
11021         return nodeInfo;
11022     },
11023
11024     /**
11025      * Gets a range template nodes.
11026      * @param {Number} startIndex
11027      * @param {Number} endIndex
11028      * @return {Array} An array of nodes
11029      */
11030     getNodes : function(start, end){
11031         var ns = this.nodes;
11032         start = start || 0;
11033         end = typeof end == "undefined" ? ns.length - 1 : end;
11034         var nodes = [];
11035         if(start <= end){
11036             for(var i = start; i <= end; i++){
11037                 nodes.push(ns[i]);
11038             }
11039         } else{
11040             for(var i = start; i >= end; i--){
11041                 nodes.push(ns[i]);
11042             }
11043         }
11044         return nodes;
11045     },
11046
11047     /**
11048      * Finds the index of the passed node
11049      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
11050      * @return {Number} The index of the node or -1
11051      */
11052     indexOf : function(node){
11053         node = this.getNode(node);
11054         if(typeof node.nodeIndex == "number"){
11055             return node.nodeIndex;
11056         }
11057         var ns = this.nodes;
11058         for(var i = 0, len = ns.length; i < len; i++){
11059             if(ns[i] == node){
11060                 return i;
11061             }
11062         }
11063         return -1;
11064     }
11065 });
11066 /*
11067  * - LGPL
11068  *
11069  * based on jquery fullcalendar
11070  * 
11071  */
11072
11073 Roo.bootstrap = Roo.bootstrap || {};
11074 /**
11075  * @class Roo.bootstrap.Calendar
11076  * @extends Roo.bootstrap.Component
11077  * Bootstrap Calendar class
11078  * @cfg {Boolean} loadMask (true|false) default false
11079  * @cfg {Object} header generate the user specific header of the calendar, default false
11080
11081  * @constructor
11082  * Create a new Container
11083  * @param {Object} config The config object
11084  */
11085
11086
11087
11088 Roo.bootstrap.Calendar = function(config){
11089     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
11090      this.addEvents({
11091         /**
11092              * @event select
11093              * Fires when a date is selected
11094              * @param {DatePicker} this
11095              * @param {Date} date The selected date
11096              */
11097         'select': true,
11098         /**
11099              * @event monthchange
11100              * Fires when the displayed month changes 
11101              * @param {DatePicker} this
11102              * @param {Date} date The selected month
11103              */
11104         'monthchange': true,
11105         /**
11106              * @event evententer
11107              * Fires when mouse over an event
11108              * @param {Calendar} this
11109              * @param {event} Event
11110              */
11111         'evententer': true,
11112         /**
11113              * @event eventleave
11114              * Fires when the mouse leaves an
11115              * @param {Calendar} this
11116              * @param {event}
11117              */
11118         'eventleave': true,
11119         /**
11120              * @event eventclick
11121              * Fires when the mouse click an
11122              * @param {Calendar} this
11123              * @param {event}
11124              */
11125         'eventclick': true
11126         
11127     });
11128
11129 };
11130
11131 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
11132     
11133      /**
11134      * @cfg {Number} startDay
11135      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
11136      */
11137     startDay : 0,
11138     
11139     loadMask : false,
11140     
11141     header : false,
11142       
11143     getAutoCreate : function(){
11144         
11145         
11146         var fc_button = function(name, corner, style, content ) {
11147             return Roo.apply({},{
11148                 tag : 'span',
11149                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
11150                          (corner.length ?
11151                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
11152                             ''
11153                         ),
11154                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
11155                 unselectable: 'on'
11156             });
11157         };
11158         
11159         var header = {};
11160         
11161         if(!this.header){
11162             header = {
11163                 tag : 'table',
11164                 cls : 'fc-header',
11165                 style : 'width:100%',
11166                 cn : [
11167                     {
11168                         tag: 'tr',
11169                         cn : [
11170                             {
11171                                 tag : 'td',
11172                                 cls : 'fc-header-left',
11173                                 cn : [
11174                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
11175                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
11176                                     { tag: 'span', cls: 'fc-header-space' },
11177                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
11178
11179
11180                                 ]
11181                             },
11182
11183                             {
11184                                 tag : 'td',
11185                                 cls : 'fc-header-center',
11186                                 cn : [
11187                                     {
11188                                         tag: 'span',
11189                                         cls: 'fc-header-title',
11190                                         cn : {
11191                                             tag: 'H2',
11192                                             html : 'month / year'
11193                                         }
11194                                     }
11195
11196                                 ]
11197                             },
11198                             {
11199                                 tag : 'td',
11200                                 cls : 'fc-header-right',
11201                                 cn : [
11202                               /*      fc_button('month', 'left', '', 'month' ),
11203                                     fc_button('week', '', '', 'week' ),
11204                                     fc_button('day', 'right', '', 'day' )
11205                                 */    
11206
11207                                 ]
11208                             }
11209
11210                         ]
11211                     }
11212                 ]
11213             };
11214         }
11215         
11216         header = this.header;
11217         
11218        
11219         var cal_heads = function() {
11220             var ret = [];
11221             // fixme - handle this.
11222             
11223             for (var i =0; i < Date.dayNames.length; i++) {
11224                 var d = Date.dayNames[i];
11225                 ret.push({
11226                     tag: 'th',
11227                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
11228                     html : d.substring(0,3)
11229                 });
11230                 
11231             }
11232             ret[0].cls += ' fc-first';
11233             ret[6].cls += ' fc-last';
11234             return ret;
11235         };
11236         var cal_cell = function(n) {
11237             return  {
11238                 tag: 'td',
11239                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
11240                 cn : [
11241                     {
11242                         cn : [
11243                             {
11244                                 cls: 'fc-day-number',
11245                                 html: 'D'
11246                             },
11247                             {
11248                                 cls: 'fc-day-content',
11249                              
11250                                 cn : [
11251                                      {
11252                                         style: 'position: relative;' // height: 17px;
11253                                     }
11254                                 ]
11255                             }
11256                             
11257                             
11258                         ]
11259                     }
11260                 ]
11261                 
11262             }
11263         };
11264         var cal_rows = function() {
11265             
11266             var ret = []
11267             for (var r = 0; r < 6; r++) {
11268                 var row= {
11269                     tag : 'tr',
11270                     cls : 'fc-week',
11271                     cn : []
11272                 };
11273                 
11274                 for (var i =0; i < Date.dayNames.length; i++) {
11275                     var d = Date.dayNames[i];
11276                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
11277
11278                 }
11279                 row.cn[0].cls+=' fc-first';
11280                 row.cn[0].cn[0].style = 'min-height:90px';
11281                 row.cn[6].cls+=' fc-last';
11282                 ret.push(row);
11283                 
11284             }
11285             ret[0].cls += ' fc-first';
11286             ret[4].cls += ' fc-prev-last';
11287             ret[5].cls += ' fc-last';
11288             return ret;
11289             
11290         };
11291         
11292         var cal_table = {
11293             tag: 'table',
11294             cls: 'fc-border-separate',
11295             style : 'width:100%',
11296             cellspacing  : 0,
11297             cn : [
11298                 { 
11299                     tag: 'thead',
11300                     cn : [
11301                         { 
11302                             tag: 'tr',
11303                             cls : 'fc-first fc-last',
11304                             cn : cal_heads()
11305                         }
11306                     ]
11307                 },
11308                 { 
11309                     tag: 'tbody',
11310                     cn : cal_rows()
11311                 }
11312                   
11313             ]
11314         };
11315          
11316          var cfg = {
11317             cls : 'fc fc-ltr',
11318             cn : [
11319                 header,
11320                 {
11321                     cls : 'fc-content',
11322                     style : "position: relative;",
11323                     cn : [
11324                         {
11325                             cls : 'fc-view fc-view-month fc-grid',
11326                             style : 'position: relative',
11327                             unselectable : 'on',
11328                             cn : [
11329                                 {
11330                                     cls : 'fc-event-container',
11331                                     style : 'position:absolute;z-index:8;top:0;left:0;'
11332                                 },
11333                                 cal_table
11334                             ]
11335                         }
11336                     ]
11337     
11338                 }
11339            ] 
11340             
11341         };
11342         
11343          
11344         
11345         return cfg;
11346     },
11347     
11348     
11349     initEvents : function()
11350     {
11351         if(!this.store){
11352             throw "can not find store for calendar";
11353         }
11354         
11355         var mark = {
11356             tag: "div",
11357             cls:"x-dlg-mask",
11358             style: "text-align:center",
11359             cn: [
11360                 {
11361                     tag: "div",
11362                     style: "background-color:white;width:50%;margin:250 auto",
11363                     cn: [
11364                         {
11365                             tag: "img",
11366                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
11367                         },
11368                         {
11369                             tag: "span",
11370                             html: "Loading"
11371                         }
11372                         
11373                     ]
11374                 }
11375             ]
11376         }
11377         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
11378         
11379         var size = this.el.select('.fc-content', true).first().getSize();
11380         this.maskEl.setSize(size.width, size.height);
11381         this.maskEl.enableDisplayMode("block");
11382         if(!this.loadMask){
11383             this.maskEl.hide();
11384         }
11385         
11386         this.store = Roo.factory(this.store, Roo.data);
11387         this.store.on('load', this.onLoad, this);
11388         this.store.on('beforeload', this.onBeforeLoad, this);
11389         
11390         this.resize();
11391         
11392         this.cells = this.el.select('.fc-day',true);
11393         //Roo.log(this.cells);
11394         this.textNodes = this.el.query('.fc-day-number');
11395         this.cells.addClassOnOver('fc-state-hover');
11396         
11397         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
11398         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
11399         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
11400         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
11401         
11402         this.on('monthchange', this.onMonthChange, this);
11403         
11404         this.update(new Date().clearTime());
11405     },
11406     
11407     resize : function() {
11408         var sz  = this.el.getSize();
11409         
11410         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
11411         this.el.select('.fc-day-content div',true).setHeight(34);
11412     },
11413     
11414     
11415     // private
11416     showPrevMonth : function(e){
11417         this.update(this.activeDate.add("mo", -1));
11418     },
11419     showToday : function(e){
11420         this.update(new Date().clearTime());
11421     },
11422     // private
11423     showNextMonth : function(e){
11424         this.update(this.activeDate.add("mo", 1));
11425     },
11426
11427     // private
11428     showPrevYear : function(){
11429         this.update(this.activeDate.add("y", -1));
11430     },
11431
11432     // private
11433     showNextYear : function(){
11434         this.update(this.activeDate.add("y", 1));
11435     },
11436
11437     
11438    // private
11439     update : function(date)
11440     {
11441         var vd = this.activeDate;
11442         this.activeDate = date;
11443 //        if(vd && this.el){
11444 //            var t = date.getTime();
11445 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
11446 //                Roo.log('using add remove');
11447 //                
11448 //                this.fireEvent('monthchange', this, date);
11449 //                
11450 //                this.cells.removeClass("fc-state-highlight");
11451 //                this.cells.each(function(c){
11452 //                   if(c.dateValue == t){
11453 //                       c.addClass("fc-state-highlight");
11454 //                       setTimeout(function(){
11455 //                            try{c.dom.firstChild.focus();}catch(e){}
11456 //                       }, 50);
11457 //                       return false;
11458 //                   }
11459 //                   return true;
11460 //                });
11461 //                return;
11462 //            }
11463 //        }
11464         
11465         var days = date.getDaysInMonth();
11466         
11467         var firstOfMonth = date.getFirstDateOfMonth();
11468         var startingPos = firstOfMonth.getDay()-this.startDay;
11469         
11470         if(startingPos < this.startDay){
11471             startingPos += 7;
11472         }
11473         
11474         var pm = date.add(Date.MONTH, -1);
11475         var prevStart = pm.getDaysInMonth()-startingPos;
11476 //        
11477         this.cells = this.el.select('.fc-day',true);
11478         this.textNodes = this.el.query('.fc-day-number');
11479         this.cells.addClassOnOver('fc-state-hover');
11480         
11481         var cells = this.cells.elements;
11482         var textEls = this.textNodes;
11483         
11484         Roo.each(cells, function(cell){
11485             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
11486         });
11487         
11488         days += startingPos;
11489
11490         // convert everything to numbers so it's fast
11491         var day = 86400000;
11492         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
11493         //Roo.log(d);
11494         //Roo.log(pm);
11495         //Roo.log(prevStart);
11496         
11497         var today = new Date().clearTime().getTime();
11498         var sel = date.clearTime().getTime();
11499         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
11500         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
11501         var ddMatch = this.disabledDatesRE;
11502         var ddText = this.disabledDatesText;
11503         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
11504         var ddaysText = this.disabledDaysText;
11505         var format = this.format;
11506         
11507         var setCellClass = function(cal, cell){
11508             cell.row = 0;
11509             cell.events = [];
11510             cell.more = [];
11511             //Roo.log('set Cell Class');
11512             cell.title = "";
11513             var t = d.getTime();
11514             
11515             //Roo.log(d);
11516             
11517             cell.dateValue = t;
11518             if(t == today){
11519                 cell.className += " fc-today";
11520                 cell.className += " fc-state-highlight";
11521                 cell.title = cal.todayText;
11522             }
11523             if(t == sel){
11524                 // disable highlight in other month..
11525                 //cell.className += " fc-state-highlight";
11526                 
11527             }
11528             // disabling
11529             if(t < min) {
11530                 cell.className = " fc-state-disabled";
11531                 cell.title = cal.minText;
11532                 return;
11533             }
11534             if(t > max) {
11535                 cell.className = " fc-state-disabled";
11536                 cell.title = cal.maxText;
11537                 return;
11538             }
11539             if(ddays){
11540                 if(ddays.indexOf(d.getDay()) != -1){
11541                     cell.title = ddaysText;
11542                     cell.className = " fc-state-disabled";
11543                 }
11544             }
11545             if(ddMatch && format){
11546                 var fvalue = d.dateFormat(format);
11547                 if(ddMatch.test(fvalue)){
11548                     cell.title = ddText.replace("%0", fvalue);
11549                     cell.className = " fc-state-disabled";
11550                 }
11551             }
11552             
11553             if (!cell.initialClassName) {
11554                 cell.initialClassName = cell.dom.className;
11555             }
11556             
11557             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
11558         };
11559
11560         var i = 0;
11561         
11562         for(; i < startingPos; i++) {
11563             textEls[i].innerHTML = (++prevStart);
11564             d.setDate(d.getDate()+1);
11565             
11566             cells[i].className = "fc-past fc-other-month";
11567             setCellClass(this, cells[i]);
11568         }
11569         
11570         var intDay = 0;
11571         
11572         for(; i < days; i++){
11573             intDay = i - startingPos + 1;
11574             textEls[i].innerHTML = (intDay);
11575             d.setDate(d.getDate()+1);
11576             
11577             cells[i].className = ''; // "x-date-active";
11578             setCellClass(this, cells[i]);
11579         }
11580         var extraDays = 0;
11581         
11582         for(; i < 42; i++) {
11583             textEls[i].innerHTML = (++extraDays);
11584             d.setDate(d.getDate()+1);
11585             
11586             cells[i].className = "fc-future fc-other-month";
11587             setCellClass(this, cells[i]);
11588         }
11589         
11590         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11591         
11592         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11593         
11594         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11595         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11596         
11597         if(totalRows != 6){
11598             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11599             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11600         }
11601         
11602         this.fireEvent('monthchange', this, date);
11603         
11604         
11605         /*
11606         if(!this.internalRender){
11607             var main = this.el.dom.firstChild;
11608             var w = main.offsetWidth;
11609             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11610             Roo.fly(main).setWidth(w);
11611             this.internalRender = true;
11612             // opera does not respect the auto grow header center column
11613             // then, after it gets a width opera refuses to recalculate
11614             // without a second pass
11615             if(Roo.isOpera && !this.secondPass){
11616                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11617                 this.secondPass = true;
11618                 this.update.defer(10, this, [date]);
11619             }
11620         }
11621         */
11622         
11623     },
11624     
11625     findCell : function(dt) {
11626         dt = dt.clearTime().getTime();
11627         var ret = false;
11628         this.cells.each(function(c){
11629             //Roo.log("check " +c.dateValue + '?=' + dt);
11630             if(c.dateValue == dt){
11631                 ret = c;
11632                 return false;
11633             }
11634             return true;
11635         });
11636         
11637         return ret;
11638     },
11639     
11640     findCells : function(ev) {
11641         var s = ev.start.clone().clearTime().getTime();
11642        // Roo.log(s);
11643         var e= ev.end.clone().clearTime().getTime();
11644        // Roo.log(e);
11645         var ret = [];
11646         this.cells.each(function(c){
11647              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11648             
11649             if(c.dateValue > e){
11650                 return ;
11651             }
11652             if(c.dateValue < s){
11653                 return ;
11654             }
11655             ret.push(c);
11656         });
11657         
11658         return ret;    
11659     },
11660     
11661 //    findBestRow: function(cells)
11662 //    {
11663 //        var ret = 0;
11664 //        
11665 //        for (var i =0 ; i < cells.length;i++) {
11666 //            ret  = Math.max(cells[i].rows || 0,ret);
11667 //        }
11668 //        return ret;
11669 //        
11670 //    },
11671     
11672     
11673     addItem : function(ev)
11674     {
11675         // look for vertical location slot in
11676         var cells = this.findCells(ev);
11677         
11678 //        ev.row = this.findBestRow(cells);
11679         
11680         // work out the location.
11681         
11682         var crow = false;
11683         var rows = [];
11684         for(var i =0; i < cells.length; i++) {
11685             
11686             cells[i].row = cells[0].row;
11687             
11688             if(i == 0){
11689                 cells[i].row = cells[i].row + 1;
11690             }
11691             
11692             if (!crow) {
11693                 crow = {
11694                     start : cells[i],
11695                     end :  cells[i]
11696                 };
11697                 continue;
11698             }
11699             if (crow.start.getY() == cells[i].getY()) {
11700                 // on same row.
11701                 crow.end = cells[i];
11702                 continue;
11703             }
11704             // different row.
11705             rows.push(crow);
11706             crow = {
11707                 start: cells[i],
11708                 end : cells[i]
11709             };
11710             
11711         }
11712         
11713         rows.push(crow);
11714         ev.els = [];
11715         ev.rows = rows;
11716         ev.cells = cells;
11717         
11718         cells[0].events.push(ev);
11719         
11720 //        if((typeof(cells[0].events) == 'undefined')){
11721 //            cells[0].events = [];
11722 //        }
11723 //        
11724 //        cells[0].events.push(ev);
11725 //        ev.rendered = false;
11726 //        for (var i = 0; i < cells.length;i++) {
11727 //            cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11728 //            
11729 //        }
11730         
11731         this.calevents.push(ev);
11732     },
11733     
11734     clearEvents: function() {
11735         
11736         if(!this.calevents){
11737             return;
11738         }
11739         
11740         Roo.each(this.cells.elements, function(c){
11741             c.row = 0;
11742             c.events = [];
11743             c.more = [];
11744         });
11745         
11746         Roo.each(this.calevents, function(e) {
11747             Roo.each(e.els, function(el) {
11748                 el.un('mouseenter' ,this.onEventEnter, this);
11749                 el.un('mouseleave' ,this.onEventLeave, this);
11750                 el.remove();
11751             },this);
11752         },this);
11753         
11754         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
11755             e.remove();
11756         });
11757         
11758     },
11759     
11760     renderEvents: function()
11761     {  
11762 //        for (var e = 0; e < this.calevents.length; e++) {
11763 //            
11764 //            var ev = this.calevents[e];
11765 //            var cells = ev.cells;
11766 //            var rows = ev.rows;
11767 //            
11768 //            for (var j = 0; j < cells.length; j++){
11769 //            
11770 //                if(!cells[j].more.length){
11771 //                    cells[j].row++;
11772 //                }
11773 //                if(cells[j].row > 3){
11774 //                    cells[j].more.push(ev);
11775 //                    continue;
11776 //                }
11777 //                
11778 //                cells[j].events.push(ev);
11779 //            }
11780 //        }
11781             
11782 //            for (var i = 0; i < rows.length; i++){
11783 //                // how many rows should it span..
11784 //
11785 //                var  cfg = {
11786 //                    cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11787 //                    style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11788 //
11789 //                    unselectable : "on",
11790 //                    cn : [
11791 //                        {
11792 //                            cls: 'fc-event-inner',
11793 //                            cn : [
11794 ////                                {
11795 ////                                  tag:'span',
11796 ////                                  cls: 'fc-event-time',
11797 ////                                  html : cells.length > 1 ? '' : ev.time
11798 ////                                },
11799 //                                {
11800 //                                  tag:'span',
11801 //                                  cls: 'fc-event-title',
11802 //                                  html : String.format('{0}', ev.title)
11803 //                                }
11804 //
11805 //
11806 //                            ]
11807 //                        },
11808 //                        {
11809 //                            cls: 'ui-resizable-handle ui-resizable-e',
11810 //                            html : '&nbsp;&nbsp;&nbsp'
11811 //                        }
11812 //
11813 //                    ]
11814 //                };
11815 //
11816 //                if (i == 0) {
11817 //                    cfg.cls += ' fc-event-start';
11818 //                }
11819 //                if ((i+1) == rows.length) {
11820 //                    cfg.cls += ' fc-event-end';
11821 //                }
11822 //
11823 //                var ctr = this.el.select('.fc-event-container',true).first();
11824 //                var cg = ctr.createChild(cfg);
11825 //
11826 //                var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11827 //                var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11828 //
11829 //                cg.setXY([sbox.x +2, sbox.y +(e * 20)]);    
11830 //                cg.setWidth(ebox.right - sbox.x -2);
11831 //
11832 //                cg.on('mouseenter' ,this.onEventEnter, this, ev);
11833 //                cg.on('mouseleave' ,this.onEventLeave, this, ev);
11834 //                cg.on('click', this.onEventClick, this, ev);
11835 //
11836 //                ev.els.push(cg);
11837 //
11838 //                
11839 //            }
11840 //        }
11841         
11842         var _this = this;
11843         
11844         this.cells.each(function(c) {
11845             
11846             if(c.row < 5){
11847                 return;
11848             }
11849             
11850             var ev = c.events;
11851             
11852             var r = 4;
11853             if(c.row != c.events.length){
11854                 r = 4 - (4 - (c.row - c.events.length));
11855             }
11856             
11857             c.events = ev.slice(0, r);
11858             c.more = ev.slice(r);
11859             
11860             if(c.more.length && c.more.length == 1){
11861                 c.events.push(c.more.pop());
11862             }
11863             
11864             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
11865             
11866         });
11867             
11868 //        for (var e = 0; e < this.calevents.length; e++) {
11869 //            
11870 //            var ev = this.calevents[e];
11871 //            var cells = ev.cells;
11872 //            var rows = ev.rows;
11873 //            
11874 //            for(var i = 0; i < cells.length; i++){
11875 //                
11876 //                var cbox = this.cells.item(this.cells.indexOf(cells[i]));
11877 //                
11878 //                if(cells.length < 2 && cbox.rows.length > 3){
11879 //                    cbox.more.push(ev);
11880 //                    continue;
11881 //                }
11882 //                
11883 //                cbox.rows.push(ev);
11884 //            }
11885 //        }
11886 //        
11887         this.cells.each(function(c) {
11888             
11889             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
11890             
11891             
11892             for (var e = 0; e < c.events.length; e++){
11893                 var ev = c.events[e];
11894                 var rows = ev.rows;
11895                 
11896                 for(var i = 0; i < rows.length; i++) {
11897                 
11898                     // how many rows should it span..
11899
11900                     var  cfg = {
11901                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11902                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11903
11904                         unselectable : "on",
11905                         cn : [
11906                             {
11907                                 cls: 'fc-event-inner',
11908                                 cn : [
11909     //                                {
11910     //                                  tag:'span',
11911     //                                  cls: 'fc-event-time',
11912     //                                  html : cells.length > 1 ? '' : ev.time
11913     //                                },
11914                                     {
11915                                       tag:'span',
11916                                       cls: 'fc-event-title',
11917                                       html : String.format('{0}', ev.title)
11918                                     }
11919
11920
11921                                 ]
11922                             },
11923                             {
11924                                 cls: 'ui-resizable-handle ui-resizable-e',
11925                                 html : '&nbsp;&nbsp;&nbsp'
11926                             }
11927
11928                         ]
11929                     };
11930
11931                     if (i == 0) {
11932                         cfg.cls += ' fc-event-start';
11933                     }
11934                     if ((i+1) == rows.length) {
11935                         cfg.cls += ' fc-event-end';
11936                     }
11937
11938                     var ctr = _this.el.select('.fc-event-container',true).first();
11939                     var cg = ctr.createChild(cfg);
11940
11941                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11942                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11943
11944                     var r = (c.more.length) ? 1 : 0;
11945                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
11946                     cg.setWidth(ebox.right - sbox.x -2);
11947
11948                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
11949                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
11950                     cg.on('click', _this.onEventClick, _this, ev);
11951
11952                     ev.els.push(cg);
11953                     
11954                 }
11955                 
11956             }
11957             
11958             
11959             if(c.more.length){
11960                 var  cfg = {
11961                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
11962                     style : 'position: absolute',
11963                     unselectable : "on",
11964                     cn : [
11965                         {
11966                             cls: 'fc-event-inner',
11967                             cn : [
11968                                 {
11969                                   tag:'span',
11970                                   cls: 'fc-event-title',
11971                                   html : 'More'
11972                                 }
11973
11974
11975                             ]
11976                         },
11977                         {
11978                             cls: 'ui-resizable-handle ui-resizable-e',
11979                             html : '&nbsp;&nbsp;&nbsp'
11980                         }
11981
11982                     ]
11983                 };
11984
11985                 var ctr = _this.el.select('.fc-event-container',true).first();
11986                 var cg = ctr.createChild(cfg);
11987
11988                 var sbox = c.select('.fc-day-content',true).first().getBox();
11989                 var ebox = c.select('.fc-day-content',true).first().getBox();
11990                 //Roo.log(cg);
11991                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
11992                 cg.setWidth(ebox.right - sbox.x -2);
11993
11994                 cg.on('click', _this.onMoreEventClick, _this, c.more);
11995                 
11996             }
11997             
11998         });
11999         
12000         
12001         
12002     },
12003     
12004     onEventEnter: function (e, el,event,d) {
12005         this.fireEvent('evententer', this, el, event);
12006     },
12007     
12008     onEventLeave: function (e, el,event,d) {
12009         this.fireEvent('eventleave', this, el, event);
12010     },
12011     
12012     onEventClick: function (e, el,event,d) {
12013         this.fireEvent('eventclick', this, el, event);
12014     },
12015     
12016     onMonthChange: function () {
12017         this.store.load();
12018     },
12019     
12020     onMoreEventClick: function(e, el, more)
12021     {
12022         var _this = this;
12023         
12024         this.calpopover.placement = 'right';
12025         this.calpopover.setTitle('More');
12026         
12027         this.calpopover.setContent('');
12028         
12029         var ctr = this.calpopover.el.select('.popover-content', true).first();
12030         
12031         Roo.each(more, function(m){
12032             var cfg = {
12033                 cls : 'fc-event-hori fc-event-draggable',
12034                 html : m.title
12035             }
12036             var cg = ctr.createChild(cfg);
12037             
12038             cg.on('click', _this.onEventClick, _this, m);
12039         });
12040         
12041         this.calpopover.show(el);
12042         
12043         
12044     },
12045     
12046     onLoad: function () 
12047     {   
12048         this.calevents = [];
12049         var cal = this;
12050         
12051         if(this.store.getCount() > 0){
12052             this.store.data.each(function(d){
12053                cal.addItem({
12054                     id : d.data.id,
12055                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
12056                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
12057                     time : d.data.start_time,
12058                     title : d.data.title,
12059                     description : d.data.description,
12060                     venue : d.data.venue
12061                 });
12062             });
12063         }
12064         
12065         this.renderEvents();
12066         
12067         if(this.calevents.length && this.loadMask){
12068             this.maskEl.hide();
12069         }
12070     },
12071     
12072     onBeforeLoad: function()
12073     {
12074         this.clearEvents();
12075         if(this.loadMask){
12076             this.maskEl.show();
12077         }
12078     }
12079 });
12080
12081  
12082  /*
12083  * - LGPL
12084  *
12085  * element
12086  * 
12087  */
12088
12089 /**
12090  * @class Roo.bootstrap.Popover
12091  * @extends Roo.bootstrap.Component
12092  * Bootstrap Popover class
12093  * @cfg {String} html contents of the popover   (or false to use children..)
12094  * @cfg {String} title of popover (or false to hide)
12095  * @cfg {String} placement how it is placed
12096  * @cfg {String} trigger click || hover (or false to trigger manually)
12097  * @cfg {String} over what (parent or false to trigger manually.)
12098  * 
12099  * @constructor
12100  * Create a new Popover
12101  * @param {Object} config The config object
12102  */
12103
12104 Roo.bootstrap.Popover = function(config){
12105     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
12106 };
12107
12108 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
12109     
12110     title: 'Fill in a title',
12111     html: false,
12112     
12113     placement : 'right',
12114     trigger : 'hover', // hover
12115     
12116     over: 'parent',
12117     
12118     can_build_overlaid : false,
12119     
12120     getChildContainer : function()
12121     {
12122         return this.el.select('.popover-content',true).first();
12123     },
12124     
12125     getAutoCreate : function(){
12126          Roo.log('make popover?');
12127         var cfg = {
12128            cls : 'popover roo-dynamic',
12129            style: 'display:block',
12130            cn : [
12131                 {
12132                     cls : 'arrow'
12133                 },
12134                 {
12135                     cls : 'popover-inner',
12136                     cn : [
12137                         {
12138                             tag: 'h3',
12139                             cls: 'popover-title',
12140                             html : this.title
12141                         },
12142                         {
12143                             cls : 'popover-content',
12144                             html : this.html
12145                         }
12146                     ]
12147                     
12148                 }
12149            ]
12150         };
12151         
12152         return cfg;
12153     },
12154     setTitle: function(str)
12155     {
12156         this.el.select('.popover-title',true).first().dom.innerHTML = str;
12157     },
12158     setContent: function(str)
12159     {
12160         this.el.select('.popover-content',true).first().dom.innerHTML = str;
12161     },
12162     // as it get's added to the bottom of the page.
12163     onRender : function(ct, position)
12164     {
12165         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
12166         if(!this.el){
12167             var cfg = Roo.apply({},  this.getAutoCreate());
12168             cfg.id = Roo.id();
12169             
12170             if (this.cls) {
12171                 cfg.cls += ' ' + this.cls;
12172             }
12173             if (this.style) {
12174                 cfg.style = this.style;
12175             }
12176             Roo.log("adding to ")
12177             this.el = Roo.get(document.body).createChild(cfg, position);
12178             Roo.log(this.el);
12179         }
12180         this.initEvents();
12181     },
12182     
12183     initEvents : function()
12184     {
12185         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
12186         this.el.enableDisplayMode('block');
12187         this.el.hide();
12188         if (this.over === false) {
12189             return; 
12190         }
12191         if (this.triggers === false) {
12192             return;
12193         }
12194         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12195         var triggers = this.trigger ? this.trigger.split(' ') : [];
12196         Roo.each(triggers, function(trigger) {
12197         
12198             if (trigger == 'click') {
12199                 on_el.on('click', this.toggle, this);
12200             } else if (trigger != 'manual') {
12201                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
12202                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
12203       
12204                 on_el.on(eventIn  ,this.enter, this);
12205                 on_el.on(eventOut, this.leave, this);
12206             }
12207         }, this);
12208         
12209     },
12210     
12211     
12212     // private
12213     timeout : null,
12214     hoverState : null,
12215     
12216     toggle : function () {
12217         this.hoverState == 'in' ? this.leave() : this.enter();
12218     },
12219     
12220     enter : function () {
12221        
12222     
12223         clearTimeout(this.timeout);
12224     
12225         this.hoverState = 'in'
12226     
12227         if (!this.delay || !this.delay.show) {
12228             this.show();
12229             return 
12230         }
12231         var _t = this;
12232         this.timeout = setTimeout(function () {
12233             if (_t.hoverState == 'in') {
12234                 _t.show();
12235             }
12236         }, this.delay.show)
12237     },
12238     leave : function() {
12239         clearTimeout(this.timeout);
12240     
12241         this.hoverState = 'out'
12242     
12243         if (!this.delay || !this.delay.hide) {
12244             this.hide();
12245             return 
12246         }
12247         var _t = this;
12248         this.timeout = setTimeout(function () {
12249             if (_t.hoverState == 'out') {
12250                 _t.hide();
12251             }
12252         }, this.delay.hide)
12253     },
12254     
12255     show : function (on_el)
12256     {
12257         if (!on_el) {
12258             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12259         }
12260         // set content.
12261         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
12262         if (this.html !== false) {
12263             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
12264         }
12265         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
12266         if (!this.title.length) {
12267             this.el.select('.popover-title',true).hide();
12268         }
12269         
12270         var placement = typeof this.placement == 'function' ?
12271             this.placement.call(this, this.el, on_el) :
12272             this.placement;
12273             
12274         var autoToken = /\s?auto?\s?/i;
12275         var autoPlace = autoToken.test(placement);
12276         if (autoPlace) {
12277             placement = placement.replace(autoToken, '') || 'top';
12278         }
12279         
12280         //this.el.detach()
12281         //this.el.setXY([0,0]);
12282         this.el.show();
12283         this.el.dom.style.display='block';
12284         this.el.addClass(placement);
12285         
12286         //this.el.appendTo(on_el);
12287         
12288         var p = this.getPosition();
12289         var box = this.el.getBox();
12290         
12291         if (autoPlace) {
12292             // fixme..
12293         }
12294         var align = Roo.bootstrap.Popover.alignment[placement]
12295         this.el.alignTo(on_el, align[0],align[1]);
12296         //var arrow = this.el.select('.arrow',true).first();
12297         //arrow.set(align[2], 
12298         
12299         this.el.addClass('in');
12300         this.hoverState = null;
12301         
12302         if (this.el.hasClass('fade')) {
12303             // fade it?
12304         }
12305         
12306     },
12307     hide : function()
12308     {
12309         this.el.setXY([0,0]);
12310         this.el.removeClass('in');
12311         this.el.hide();
12312         
12313     }
12314     
12315 });
12316
12317 Roo.bootstrap.Popover.alignment = {
12318     'left' : ['r-l', [-10,0], 'right'],
12319     'right' : ['l-r', [10,0], 'left'],
12320     'bottom' : ['t-b', [0,10], 'top'],
12321     'top' : [ 'b-t', [0,-10], 'bottom']
12322 };
12323
12324  /*
12325  * - LGPL
12326  *
12327  * Progress
12328  * 
12329  */
12330
12331 /**
12332  * @class Roo.bootstrap.Progress
12333  * @extends Roo.bootstrap.Component
12334  * Bootstrap Progress class
12335  * @cfg {Boolean} striped striped of the progress bar
12336  * @cfg {Boolean} active animated of the progress bar
12337  * 
12338  * 
12339  * @constructor
12340  * Create a new Progress
12341  * @param {Object} config The config object
12342  */
12343
12344 Roo.bootstrap.Progress = function(config){
12345     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12346 };
12347
12348 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
12349     
12350     striped : false,
12351     active: false,
12352     
12353     getAutoCreate : function(){
12354         var cfg = {
12355             tag: 'div',
12356             cls: 'progress'
12357         };
12358         
12359         
12360         if(this.striped){
12361             cfg.cls += ' progress-striped';
12362         }
12363       
12364         if(this.active){
12365             cfg.cls += ' active';
12366         }
12367         
12368         
12369         return cfg;
12370     }
12371    
12372 });
12373
12374  
12375
12376  /*
12377  * - LGPL
12378  *
12379  * ProgressBar
12380  * 
12381  */
12382
12383 /**
12384  * @class Roo.bootstrap.ProgressBar
12385  * @extends Roo.bootstrap.Component
12386  * Bootstrap ProgressBar class
12387  * @cfg {Number} aria_valuenow aria-value now
12388  * @cfg {Number} aria_valuemin aria-value min
12389  * @cfg {Number} aria_valuemax aria-value max
12390  * @cfg {String} label label for the progress bar
12391  * @cfg {String} panel (success | info | warning | danger )
12392  * @cfg {String} role role of the progress bar
12393  * @cfg {String} sr_only text
12394  * 
12395  * 
12396  * @constructor
12397  * Create a new ProgressBar
12398  * @param {Object} config The config object
12399  */
12400
12401 Roo.bootstrap.ProgressBar = function(config){
12402     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12403 };
12404
12405 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
12406     
12407     aria_valuenow : 0,
12408     aria_valuemin : 0,
12409     aria_valuemax : 100,
12410     label : false,
12411     panel : false,
12412     role : false,
12413     sr_only: false,
12414     
12415     getAutoCreate : function()
12416     {
12417         
12418         var cfg = {
12419             tag: 'div',
12420             cls: 'progress-bar',
12421             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12422         };
12423         
12424         if(this.sr_only){
12425             cfg.cn = {
12426                 tag: 'span',
12427                 cls: 'sr-only',
12428                 html: this.sr_only
12429             }
12430         }
12431         
12432         if(this.role){
12433             cfg.role = this.role;
12434         }
12435         
12436         if(this.aria_valuenow){
12437             cfg['aria-valuenow'] = this.aria_valuenow;
12438         }
12439         
12440         if(this.aria_valuemin){
12441             cfg['aria-valuemin'] = this.aria_valuemin;
12442         }
12443         
12444         if(this.aria_valuemax){
12445             cfg['aria-valuemax'] = this.aria_valuemax;
12446         }
12447         
12448         if(this.label && !this.sr_only){
12449             cfg.html = this.label;
12450         }
12451         
12452         if(this.panel){
12453             cfg.cls += ' progress-bar-' + this.panel;
12454         }
12455         
12456         return cfg;
12457     },
12458     
12459     update : function(aria_valuenow)
12460     {
12461         this.aria_valuenow = aria_valuenow;
12462         
12463         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12464     }
12465    
12466 });
12467
12468  
12469
12470  /*
12471  * - LGPL
12472  *
12473  * TabPanel
12474  * 
12475  */
12476
12477 /**
12478  * @class Roo.bootstrap.TabPanel
12479  * @extends Roo.bootstrap.Component
12480  * Bootstrap TabPanel class
12481  * @cfg {Boolean} active panel active
12482  * @cfg {String} html panel content
12483  * @cfg {String} tabId tab relate id
12484  * @cfg {String} navId The navbar which triggers show hide
12485  * 
12486  * 
12487  * @constructor
12488  * Create a new TabPanel
12489  * @param {Object} config The config object
12490  */
12491
12492 Roo.bootstrap.TabPanel = function(config){
12493     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
12494      this.addEvents({
12495         /**
12496              * @event changed
12497              * Fires when the active status changes
12498              * @param {Roo.bootstrap.TabPanel} this
12499              * @param {Boolean} state the new state
12500             
12501          */
12502         'changed': true
12503      });
12504 };
12505
12506 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
12507     
12508     active: false,
12509     html: false,
12510     tabId: false,
12511     navId : false,
12512     
12513     getAutoCreate : function(){
12514         var cfg = {
12515             tag: 'div',
12516             cls: 'tab-pane',
12517             html: this.html || ''
12518         };
12519         
12520         if(this.active){
12521             cfg.cls += ' active';
12522         }
12523         
12524         if(this.tabId){
12525             cfg.tabId = this.tabId;
12526         }
12527         
12528         return cfg;
12529     },
12530     onRender : function(ct, position)
12531     {
12532        // Roo.log("Call onRender: " + this.xtype);
12533         
12534         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
12535         
12536         if (this.navId && this.tabId) {
12537             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
12538             if (!item) {
12539                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
12540             } else {
12541                 item.on('changed', function(item, state) {
12542                     this.setActive(state);
12543                 }, this);
12544             }
12545         }
12546         
12547     },
12548     setActive: function(state)
12549     {
12550         Roo.log("panel - set active " + this.tabId + "=" + state);
12551         
12552         this.active = state;
12553         if (!state) {
12554             this.el.removeClass('active');
12555             
12556         } else  if (!this.el.hasClass('active')) {
12557             this.el.addClass('active');
12558         }
12559         this.fireEvent('changed', this, state);
12560     }
12561     
12562     
12563 });
12564  
12565
12566  
12567
12568  /*
12569  * - LGPL
12570  *
12571  * DateField
12572  * 
12573  */
12574
12575 /**
12576  * @class Roo.bootstrap.DateField
12577  * @extends Roo.bootstrap.Input
12578  * Bootstrap DateField class
12579  * @cfg {Number} weekStart default 0
12580  * @cfg {Number} weekStart default 0
12581  * @cfg {Number} viewMode default empty, (months|years)
12582  * @cfg {Number} minViewMode default empty, (months|years)
12583  * @cfg {Number} startDate default -Infinity
12584  * @cfg {Number} endDate default Infinity
12585  * @cfg {Boolean} todayHighlight default false
12586  * @cfg {Boolean} todayBtn default false
12587  * @cfg {Boolean} calendarWeeks default false
12588  * @cfg {Object} daysOfWeekDisabled default empty
12589  * 
12590  * @cfg {Boolean} keyboardNavigation default true
12591  * @cfg {String} language default en
12592  * 
12593  * @constructor
12594  * Create a new DateField
12595  * @param {Object} config The config object
12596  */
12597
12598 Roo.bootstrap.DateField = function(config){
12599     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
12600      this.addEvents({
12601             /**
12602              * @event show
12603              * Fires when this field show.
12604              * @param {Roo.bootstrap.DateField} this
12605              * @param {Mixed} date The date value
12606              */
12607             show : true,
12608             /**
12609              * @event show
12610              * Fires when this field hide.
12611              * @param {Roo.bootstrap.DateField} this
12612              * @param {Mixed} date The date value
12613              */
12614             hide : true,
12615             /**
12616              * @event select
12617              * Fires when select a date.
12618              * @param {Roo.bootstrap.DateField} this
12619              * @param {Mixed} date The date value
12620              */
12621             select : true
12622         });
12623 };
12624
12625 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
12626     
12627     /**
12628      * @cfg {String} format
12629      * The default date format string which can be overriden for localization support.  The format must be
12630      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
12631      */
12632     format : "m/d/y",
12633     /**
12634      * @cfg {String} altFormats
12635      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
12636      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
12637      */
12638     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
12639     
12640     weekStart : 0,
12641     
12642     viewMode : '',
12643     
12644     minViewMode : '',
12645     
12646     todayHighlight : false,
12647     
12648     todayBtn: false,
12649     
12650     language: 'en',
12651     
12652     keyboardNavigation: true,
12653     
12654     calendarWeeks: false,
12655     
12656     startDate: -Infinity,
12657     
12658     endDate: Infinity,
12659     
12660     daysOfWeekDisabled: [],
12661     
12662     _events: [],
12663     
12664     UTCDate: function()
12665     {
12666         return new Date(Date.UTC.apply(Date, arguments));
12667     },
12668     
12669     UTCToday: function()
12670     {
12671         var today = new Date();
12672         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12673     },
12674     
12675     getDate: function() {
12676             var d = this.getUTCDate();
12677             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12678     },
12679     
12680     getUTCDate: function() {
12681             return this.date;
12682     },
12683     
12684     setDate: function(d) {
12685             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12686     },
12687     
12688     setUTCDate: function(d) {
12689             this.date = d;
12690             this.setValue(this.formatDate(this.date));
12691     },
12692         
12693     onRender: function(ct, position)
12694     {
12695         
12696         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12697         
12698         this.language = this.language || 'en';
12699         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12700         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12701         
12702         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12703         this.format = this.format || 'm/d/y';
12704         this.isInline = false;
12705         this.isInput = true;
12706         this.component = this.el.select('.add-on', true).first() || false;
12707         this.component = (this.component && this.component.length === 0) ? false : this.component;
12708         this.hasInput = this.component && this.inputEL().length;
12709         
12710         if (typeof(this.minViewMode === 'string')) {
12711             switch (this.minViewMode) {
12712                 case 'months':
12713                     this.minViewMode = 1;
12714                     break;
12715                 case 'years':
12716                     this.minViewMode = 2;
12717                     break;
12718                 default:
12719                     this.minViewMode = 0;
12720                     break;
12721             }
12722         }
12723         
12724         if (typeof(this.viewMode === 'string')) {
12725             switch (this.viewMode) {
12726                 case 'months':
12727                     this.viewMode = 1;
12728                     break;
12729                 case 'years':
12730                     this.viewMode = 2;
12731                     break;
12732                 default:
12733                     this.viewMode = 0;
12734                     break;
12735             }
12736         }
12737                 
12738         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12739         
12740         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12741         
12742         this.picker().on('mousedown', this.onMousedown, this);
12743         this.picker().on('click', this.onClick, this);
12744         
12745         this.picker().addClass('datepicker-dropdown');
12746         
12747         this.startViewMode = this.viewMode;
12748         
12749         
12750         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12751             if(!this.calendarWeeks){
12752                 v.remove();
12753                 return;
12754             };
12755             
12756             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12757             v.attr('colspan', function(i, val){
12758                 return parseInt(val) + 1;
12759             });
12760         })
12761                         
12762         
12763         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12764         
12765         this.setStartDate(this.startDate);
12766         this.setEndDate(this.endDate);
12767         
12768         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12769         
12770         this.fillDow();
12771         this.fillMonths();
12772         this.update();
12773         this.showMode();
12774         
12775         if(this.isInline) {
12776             this.show();
12777         }
12778     },
12779     
12780     picker : function()
12781     {
12782         return this.el.select('.datepicker', true).first();
12783     },
12784     
12785     fillDow: function()
12786     {
12787         var dowCnt = this.weekStart;
12788         
12789         var dow = {
12790             tag: 'tr',
12791             cn: [
12792                 
12793             ]
12794         };
12795         
12796         if(this.calendarWeeks){
12797             dow.cn.push({
12798                 tag: 'th',
12799                 cls: 'cw',
12800                 html: '&nbsp;'
12801             })
12802         }
12803         
12804         while (dowCnt < this.weekStart + 7) {
12805             dow.cn.push({
12806                 tag: 'th',
12807                 cls: 'dow',
12808                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12809             });
12810         }
12811         
12812         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12813     },
12814     
12815     fillMonths: function()
12816     {    
12817         var i = 0
12818         var months = this.picker().select('>.datepicker-months td', true).first();
12819         
12820         months.dom.innerHTML = '';
12821         
12822         while (i < 12) {
12823             var month = {
12824                 tag: 'span',
12825                 cls: 'month',
12826                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12827             }
12828             
12829             months.createChild(month);
12830         }
12831         
12832     },
12833     
12834     update: function(){
12835         
12836         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12837         
12838         if (this.date < this.startDate) {
12839             this.viewDate = new Date(this.startDate);
12840         } else if (this.date > this.endDate) {
12841             this.viewDate = new Date(this.endDate);
12842         } else {
12843             this.viewDate = new Date(this.date);
12844         }
12845         
12846         this.fill();
12847     },
12848     
12849     fill: function() {
12850         var d = new Date(this.viewDate),
12851                 year = d.getUTCFullYear(),
12852                 month = d.getUTCMonth(),
12853                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12854                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12855                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12856                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12857                 currentDate = this.date && this.date.valueOf(),
12858                 today = this.UTCToday();
12859         
12860         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12861         
12862 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12863         
12864 //        this.picker.select('>tfoot th.today').
12865 //                                              .text(dates[this.language].today)
12866 //                                              .toggle(this.todayBtn !== false);
12867     
12868         this.updateNavArrows();
12869         this.fillMonths();
12870                                                 
12871         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12872         
12873         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12874          
12875         prevMonth.setUTCDate(day);
12876         
12877         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12878         
12879         var nextMonth = new Date(prevMonth);
12880         
12881         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12882         
12883         nextMonth = nextMonth.valueOf();
12884         
12885         var fillMonths = false;
12886         
12887         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12888         
12889         while(prevMonth.valueOf() < nextMonth) {
12890             var clsName = '';
12891             
12892             if (prevMonth.getUTCDay() === this.weekStart) {
12893                 if(fillMonths){
12894                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12895                 }
12896                     
12897                 fillMonths = {
12898                     tag: 'tr',
12899                     cn: []
12900                 };
12901                 
12902                 if(this.calendarWeeks){
12903                     // ISO 8601: First week contains first thursday.
12904                     // ISO also states week starts on Monday, but we can be more abstract here.
12905                     var
12906                     // Start of current week: based on weekstart/current date
12907                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12908                     // Thursday of this week
12909                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12910                     // First Thursday of year, year from thursday
12911                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12912                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12913                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12914                     
12915                     fillMonths.cn.push({
12916                         tag: 'td',
12917                         cls: 'cw',
12918                         html: calWeek
12919                     });
12920                 }
12921             }
12922             
12923             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12924                 clsName += ' old';
12925             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12926                 clsName += ' new';
12927             }
12928             if (this.todayHighlight &&
12929                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12930                 prevMonth.getUTCMonth() == today.getMonth() &&
12931                 prevMonth.getUTCDate() == today.getDate()) {
12932                 clsName += ' today';
12933             }
12934             
12935             if (currentDate && prevMonth.valueOf() === currentDate) {
12936                 clsName += ' active';
12937             }
12938             
12939             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12940                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12941                     clsName += ' disabled';
12942             }
12943             
12944             fillMonths.cn.push({
12945                 tag: 'td',
12946                 cls: 'day ' + clsName,
12947                 html: prevMonth.getDate()
12948             })
12949             
12950             prevMonth.setDate(prevMonth.getDate()+1);
12951         }
12952           
12953         var currentYear = this.date && this.date.getUTCFullYear();
12954         var currentMonth = this.date && this.date.getUTCMonth();
12955         
12956         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12957         
12958         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12959             v.removeClass('active');
12960             
12961             if(currentYear === year && k === currentMonth){
12962                 v.addClass('active');
12963             }
12964             
12965             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12966                 v.addClass('disabled');
12967             }
12968             
12969         });
12970         
12971         
12972         year = parseInt(year/10, 10) * 10;
12973         
12974         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12975         
12976         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12977         
12978         year -= 1;
12979         for (var i = -1; i < 11; i++) {
12980             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12981                 tag: 'span',
12982                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12983                 html: year
12984             })
12985             
12986             year += 1;
12987         }
12988     },
12989     
12990     showMode: function(dir) {
12991         if (dir) {
12992             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12993         }
12994         Roo.each(this.picker().select('>div',true).elements, function(v){
12995             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12996             v.hide();
12997         });
12998         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12999     },
13000     
13001     place: function()
13002     {
13003         if(this.isInline) return;
13004         
13005         this.picker().removeClass(['bottom', 'top']);
13006         
13007         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13008             /*
13009              * place to the top of element!
13010              *
13011              */
13012             
13013             this.picker().addClass('top');
13014             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13015             
13016             return;
13017         }
13018         
13019         this.picker().addClass('bottom');
13020         
13021         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13022     },
13023     
13024     parseDate : function(value){
13025         if(!value || value instanceof Date){
13026             return value;
13027         }
13028         var v = Date.parseDate(value, this.format);
13029         if (!v && this.useIso) {
13030             v = Date.parseDate(value, 'Y-m-d');
13031         }
13032         if(!v && this.altFormats){
13033             if(!this.altFormatsArray){
13034                 this.altFormatsArray = this.altFormats.split("|");
13035             }
13036             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
13037                 v = Date.parseDate(value, this.altFormatsArray[i]);
13038             }
13039         }
13040         return v;
13041     },
13042     
13043     formatDate : function(date, fmt){
13044         return (!date || !(date instanceof Date)) ?
13045         date : date.dateFormat(fmt || this.format);
13046     },
13047     
13048     onFocus : function()
13049     {
13050         Roo.bootstrap.DateField.superclass.onFocus.call(this);
13051         this.show();
13052     },
13053     
13054     onBlur : function()
13055     {
13056         Roo.bootstrap.DateField.superclass.onBlur.call(this);
13057         this.hide();
13058     },
13059     
13060     show : function()
13061     {
13062         this.picker().show();
13063         this.update();
13064         this.place();
13065         
13066         this.fireEvent('show', this, this.date);
13067     },
13068     
13069     hide : function()
13070     {
13071         if(this.isInline) return;
13072         this.picker().hide();
13073         this.viewMode = this.startViewMode;
13074         this.showMode();
13075         
13076         this.fireEvent('hide', this, this.date);
13077         
13078     },
13079     
13080     onMousedown: function(e){
13081         e.stopPropagation();
13082         e.preventDefault();
13083     },
13084     
13085     keyup: function(e){
13086         Roo.bootstrap.DateField.superclass.keyup.call(this);
13087         this.update();
13088         
13089     },
13090
13091     setValue: function(v){
13092         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
13093         
13094         this.fireEvent('select', this, this.date);
13095         
13096     },
13097     
13098     fireKey: function(e){
13099         if (!this.picker().isVisible()){
13100             if (e.keyCode == 27) // allow escape to hide and re-show picker
13101                 this.show();
13102             return;
13103         }
13104         var dateChanged = false,
13105         dir, day, month,
13106         newDate, newViewDate;
13107         switch(e.keyCode){
13108             case 27: // escape
13109                 this.hide();
13110                 e.preventDefault();
13111                 break;
13112             case 37: // left
13113             case 39: // right
13114                 if (!this.keyboardNavigation) break;
13115                 dir = e.keyCode == 37 ? -1 : 1;
13116                 
13117                 if (e.ctrlKey){
13118                     newDate = this.moveYear(this.date, dir);
13119                     newViewDate = this.moveYear(this.viewDate, dir);
13120                 } else if (e.shiftKey){
13121                     newDate = this.moveMonth(this.date, dir);
13122                     newViewDate = this.moveMonth(this.viewDate, dir);
13123                 } else {
13124                     newDate = new Date(this.date);
13125                     newDate.setUTCDate(this.date.getUTCDate() + dir);
13126                     newViewDate = new Date(this.viewDate);
13127                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
13128                 }
13129                 if (this.dateWithinRange(newDate)){
13130                     this.date = newDate;
13131                     this.viewDate = newViewDate;
13132                     this.setValue(this.formatDate(this.date));
13133                     this.update();
13134                     e.preventDefault();
13135                     dateChanged = true;
13136                 }
13137                 break;
13138             case 38: // up
13139             case 40: // down
13140                 if (!this.keyboardNavigation) break;
13141                 dir = e.keyCode == 38 ? -1 : 1;
13142                 if (e.ctrlKey){
13143                     newDate = this.moveYear(this.date, dir);
13144                     newViewDate = this.moveYear(this.viewDate, dir);
13145                 } else if (e.shiftKey){
13146                     newDate = this.moveMonth(this.date, dir);
13147                     newViewDate = this.moveMonth(this.viewDate, dir);
13148                 } else {
13149                     newDate = new Date(this.date);
13150                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
13151                     newViewDate = new Date(this.viewDate);
13152                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
13153                 }
13154                 if (this.dateWithinRange(newDate)){
13155                     this.date = newDate;
13156                     this.viewDate = newViewDate;
13157                     this.setValue(this.formatDate(this.date));
13158                     this.update();
13159                     e.preventDefault();
13160                     dateChanged = true;
13161                 }
13162                 break;
13163             case 13: // enter
13164                 this.setValue(this.formatDate(this.date));
13165                 this.hide();
13166                 e.preventDefault();
13167                 break;
13168             case 9: // tab
13169                 this.setValue(this.formatDate(this.date));
13170                 this.hide();
13171                 break;
13172         }
13173     },
13174     
13175     
13176     onClick: function(e) {
13177         e.stopPropagation();
13178         e.preventDefault();
13179         
13180         var target = e.getTarget();
13181         
13182         if(target.nodeName.toLowerCase() === 'i'){
13183             target = Roo.get(target).dom.parentNode;
13184         }
13185         
13186         var nodeName = target.nodeName;
13187         var className = target.className;
13188         var html = target.innerHTML;
13189         
13190         switch(nodeName.toLowerCase()) {
13191             case 'th':
13192                 switch(className) {
13193                     case 'switch':
13194                         this.showMode(1);
13195                         break;
13196                     case 'prev':
13197                     case 'next':
13198                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
13199                         switch(this.viewMode){
13200                                 case 0:
13201                                         this.viewDate = this.moveMonth(this.viewDate, dir);
13202                                         break;
13203                                 case 1:
13204                                 case 2:
13205                                         this.viewDate = this.moveYear(this.viewDate, dir);
13206                                         break;
13207                         }
13208                         this.fill();
13209                         break;
13210                     case 'today':
13211                         var date = new Date();
13212                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
13213                         this.fill()
13214                         this.setValue(this.formatDate(this.date));
13215                         this.hide();
13216                         break;
13217                 }
13218                 break;
13219             case 'span':
13220                 if (className.indexOf('disabled') === -1) {
13221                     this.viewDate.setUTCDate(1);
13222                     if (className.indexOf('month') !== -1) {
13223                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
13224                     } else {
13225                         var year = parseInt(html, 10) || 0;
13226                         this.viewDate.setUTCFullYear(year);
13227                         
13228                     }
13229                     this.showMode(-1);
13230                     this.fill();
13231                 }
13232                 break;
13233                 
13234             case 'td':
13235                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
13236                     var day = parseInt(html, 10) || 1;
13237                     var year = this.viewDate.getUTCFullYear(),
13238                         month = this.viewDate.getUTCMonth();
13239
13240                     if (className.indexOf('old') !== -1) {
13241                         if(month === 0 ){
13242                             month = 11;
13243                             year -= 1;
13244                         }else{
13245                             month -= 1;
13246                         }
13247                     } else if (className.indexOf('new') !== -1) {
13248                         if (month == 11) {
13249                             month = 0;
13250                             year += 1;
13251                         } else {
13252                             month += 1;
13253                         }
13254                     }
13255                     this.date = this.UTCDate(year, month, day,0,0,0,0);
13256                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
13257                     this.fill();
13258                     this.setValue(this.formatDate(this.date));
13259                     this.hide();
13260                 }
13261                 break;
13262         }
13263     },
13264     
13265     setStartDate: function(startDate){
13266         this.startDate = startDate || -Infinity;
13267         if (this.startDate !== -Infinity) {
13268             this.startDate = this.parseDate(this.startDate);
13269         }
13270         this.update();
13271         this.updateNavArrows();
13272     },
13273
13274     setEndDate: function(endDate){
13275         this.endDate = endDate || Infinity;
13276         if (this.endDate !== Infinity) {
13277             this.endDate = this.parseDate(this.endDate);
13278         }
13279         this.update();
13280         this.updateNavArrows();
13281     },
13282     
13283     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
13284         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
13285         if (typeof(this.daysOfWeekDisabled) !== 'object') {
13286             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
13287         }
13288         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
13289             return parseInt(d, 10);
13290         });
13291         this.update();
13292         this.updateNavArrows();
13293     },
13294     
13295     updateNavArrows: function() {
13296         var d = new Date(this.viewDate),
13297         year = d.getUTCFullYear(),
13298         month = d.getUTCMonth();
13299         
13300         Roo.each(this.picker().select('.prev', true).elements, function(v){
13301             v.show();
13302             switch (this.viewMode) {
13303                 case 0:
13304
13305                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
13306                         v.hide();
13307                     }
13308                     break;
13309                 case 1:
13310                 case 2:
13311                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
13312                         v.hide();
13313                     }
13314                     break;
13315             }
13316         });
13317         
13318         Roo.each(this.picker().select('.next', true).elements, function(v){
13319             v.show();
13320             switch (this.viewMode) {
13321                 case 0:
13322
13323                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13324                         v.hide();
13325                     }
13326                     break;
13327                 case 1:
13328                 case 2:
13329                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13330                         v.hide();
13331                     }
13332                     break;
13333             }
13334         })
13335     },
13336     
13337     moveMonth: function(date, dir){
13338         if (!dir) return date;
13339         var new_date = new Date(date.valueOf()),
13340         day = new_date.getUTCDate(),
13341         month = new_date.getUTCMonth(),
13342         mag = Math.abs(dir),
13343         new_month, test;
13344         dir = dir > 0 ? 1 : -1;
13345         if (mag == 1){
13346             test = dir == -1
13347             // If going back one month, make sure month is not current month
13348             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13349             ? function(){
13350                 return new_date.getUTCMonth() == month;
13351             }
13352             // If going forward one month, make sure month is as expected
13353             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13354             : function(){
13355                 return new_date.getUTCMonth() != new_month;
13356             };
13357             new_month = month + dir;
13358             new_date.setUTCMonth(new_month);
13359             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13360             if (new_month < 0 || new_month > 11)
13361                 new_month = (new_month + 12) % 12;
13362         } else {
13363             // For magnitudes >1, move one month at a time...
13364             for (var i=0; i<mag; i++)
13365                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13366                 new_date = this.moveMonth(new_date, dir);
13367             // ...then reset the day, keeping it in the new month
13368             new_month = new_date.getUTCMonth();
13369             new_date.setUTCDate(day);
13370             test = function(){
13371                 return new_month != new_date.getUTCMonth();
13372             };
13373         }
13374         // Common date-resetting loop -- if date is beyond end of month, make it
13375         // end of month
13376         while (test()){
13377             new_date.setUTCDate(--day);
13378             new_date.setUTCMonth(new_month);
13379         }
13380         return new_date;
13381     },
13382
13383     moveYear: function(date, dir){
13384         return this.moveMonth(date, dir*12);
13385     },
13386
13387     dateWithinRange: function(date){
13388         return date >= this.startDate && date <= this.endDate;
13389     },
13390
13391     
13392     remove: function() {
13393         this.picker().remove();
13394     }
13395    
13396 });
13397
13398 Roo.apply(Roo.bootstrap.DateField,  {
13399     
13400     head : {
13401         tag: 'thead',
13402         cn: [
13403         {
13404             tag: 'tr',
13405             cn: [
13406             {
13407                 tag: 'th',
13408                 cls: 'prev',
13409                 html: '<i class="icon-arrow-left"/>'
13410             },
13411             {
13412                 tag: 'th',
13413                 cls: 'switch',
13414                 colspan: '5'
13415             },
13416             {
13417                 tag: 'th',
13418                 cls: 'next',
13419                 html: '<i class="icon-arrow-right"/>'
13420             }
13421
13422             ]
13423         }
13424         ]
13425     },
13426     
13427     content : {
13428         tag: 'tbody',
13429         cn: [
13430         {
13431             tag: 'tr',
13432             cn: [
13433             {
13434                 tag: 'td',
13435                 colspan: '7'
13436             }
13437             ]
13438         }
13439         ]
13440     },
13441     
13442     footer : {
13443         tag: 'tfoot',
13444         cn: [
13445         {
13446             tag: 'tr',
13447             cn: [
13448             {
13449                 tag: 'th',
13450                 colspan: '7',
13451                 cls: 'today'
13452             }
13453                     
13454             ]
13455         }
13456         ]
13457     },
13458     
13459     dates:{
13460         en: {
13461             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13462             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13463             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13464             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13465             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13466             today: "Today"
13467         }
13468     },
13469     
13470     modes: [
13471     {
13472         clsName: 'days',
13473         navFnc: 'Month',
13474         navStep: 1
13475     },
13476     {
13477         clsName: 'months',
13478         navFnc: 'FullYear',
13479         navStep: 1
13480     },
13481     {
13482         clsName: 'years',
13483         navFnc: 'FullYear',
13484         navStep: 10
13485     }]
13486 });
13487
13488 Roo.apply(Roo.bootstrap.DateField,  {
13489   
13490     template : {
13491         tag: 'div',
13492         cls: 'datepicker dropdown-menu',
13493         cn: [
13494         {
13495             tag: 'div',
13496             cls: 'datepicker-days',
13497             cn: [
13498             {
13499                 tag: 'table',
13500                 cls: 'table-condensed',
13501                 cn:[
13502                 Roo.bootstrap.DateField.head,
13503                 {
13504                     tag: 'tbody'
13505                 },
13506                 Roo.bootstrap.DateField.footer
13507                 ]
13508             }
13509             ]
13510         },
13511         {
13512             tag: 'div',
13513             cls: 'datepicker-months',
13514             cn: [
13515             {
13516                 tag: 'table',
13517                 cls: 'table-condensed',
13518                 cn:[
13519                 Roo.bootstrap.DateField.head,
13520                 Roo.bootstrap.DateField.content,
13521                 Roo.bootstrap.DateField.footer
13522                 ]
13523             }
13524             ]
13525         },
13526         {
13527             tag: 'div',
13528             cls: 'datepicker-years',
13529             cn: [
13530             {
13531                 tag: 'table',
13532                 cls: 'table-condensed',
13533                 cn:[
13534                 Roo.bootstrap.DateField.head,
13535                 Roo.bootstrap.DateField.content,
13536                 Roo.bootstrap.DateField.footer
13537                 ]
13538             }
13539             ]
13540         }
13541         ]
13542     }
13543 });
13544
13545  
13546
13547  /*
13548  * - LGPL
13549  *
13550  * TimeField
13551  * 
13552  */
13553
13554 /**
13555  * @class Roo.bootstrap.TimeField
13556  * @extends Roo.bootstrap.Input
13557  * Bootstrap DateField class
13558  * 
13559  * 
13560  * @constructor
13561  * Create a new TimeField
13562  * @param {Object} config The config object
13563  */
13564
13565 Roo.bootstrap.TimeField = function(config){
13566     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
13567     this.addEvents({
13568             /**
13569              * @event show
13570              * Fires when this field show.
13571              * @param {Roo.bootstrap.DateField} this
13572              * @param {Mixed} date The date value
13573              */
13574             show : true,
13575             /**
13576              * @event show
13577              * Fires when this field hide.
13578              * @param {Roo.bootstrap.DateField} this
13579              * @param {Mixed} date The date value
13580              */
13581             hide : true,
13582             /**
13583              * @event select
13584              * Fires when select a date.
13585              * @param {Roo.bootstrap.DateField} this
13586              * @param {Mixed} date The date value
13587              */
13588             select : true
13589         });
13590 };
13591
13592 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
13593     
13594     /**
13595      * @cfg {String} format
13596      * The default time format string which can be overriden for localization support.  The format must be
13597      * valid according to {@link Date#parseDate} (defaults to 'H:i').
13598      */
13599     format : "H:i",
13600        
13601     onRender: function(ct, position)
13602     {
13603         
13604         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
13605                 
13606         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
13607         
13608         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13609         
13610         this.pop = this.picker().select('>.datepicker-time',true).first();
13611         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
13612         
13613         this.picker().on('mousedown', this.onMousedown, this);
13614         this.picker().on('click', this.onClick, this);
13615         
13616         this.picker().addClass('datepicker-dropdown');
13617     
13618         this.fillTime();
13619         this.update();
13620             
13621         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
13622         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
13623         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
13624         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
13625         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
13626         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
13627
13628     },
13629     
13630     fireKey: function(e){
13631         if (!this.picker().isVisible()){
13632             if (e.keyCode == 27) // allow escape to hide and re-show picker
13633                 this.show();
13634             return;
13635         }
13636
13637         e.preventDefault();
13638         
13639         switch(e.keyCode){
13640             case 27: // escape
13641                 this.hide();
13642                 break;
13643             case 37: // left
13644             case 39: // right
13645                 this.onTogglePeriod();
13646                 break;
13647             case 38: // up
13648                 this.onIncrementMinutes();
13649                 break;
13650             case 40: // down
13651                 this.onDecrementMinutes();
13652                 break;
13653             case 13: // enter
13654             case 9: // tab
13655                 this.setTime();
13656                 break;
13657         }
13658     },
13659     
13660     onClick: function(e) {
13661         e.stopPropagation();
13662         e.preventDefault();
13663     },
13664     
13665     picker : function()
13666     {
13667         return this.el.select('.datepicker', true).first();
13668     },
13669     
13670     fillTime: function()
13671     {    
13672         var time = this.pop.select('tbody', true).first();
13673         
13674         time.dom.innerHTML = '';
13675         
13676         time.createChild({
13677             tag: 'tr',
13678             cn: [
13679                 {
13680                     tag: 'td',
13681                     cn: [
13682                         {
13683                             tag: 'a',
13684                             href: '#',
13685                             cls: 'btn',
13686                             cn: [
13687                                 {
13688                                     tag: 'span',
13689                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
13690                                 }
13691                             ]
13692                         } 
13693                     ]
13694                 },
13695                 {
13696                     tag: 'td',
13697                     cls: 'separator'
13698                 },
13699                 {
13700                     tag: 'td',
13701                     cn: [
13702                         {
13703                             tag: 'a',
13704                             href: '#',
13705                             cls: 'btn',
13706                             cn: [
13707                                 {
13708                                     tag: 'span',
13709                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
13710                                 }
13711                             ]
13712                         }
13713                     ]
13714                 },
13715                 {
13716                     tag: 'td',
13717                     cls: 'separator'
13718                 }
13719             ]
13720         });
13721         
13722         time.createChild({
13723             tag: 'tr',
13724             cn: [
13725                 {
13726                     tag: 'td',
13727                     cn: [
13728                         {
13729                             tag: 'span',
13730                             cls: 'timepicker-hour',
13731                             html: '00'
13732                         }  
13733                     ]
13734                 },
13735                 {
13736                     tag: 'td',
13737                     cls: 'separator',
13738                     html: ':'
13739                 },
13740                 {
13741                     tag: 'td',
13742                     cn: [
13743                         {
13744                             tag: 'span',
13745                             cls: 'timepicker-minute',
13746                             html: '00'
13747                         }  
13748                     ]
13749                 },
13750                 {
13751                     tag: 'td',
13752                     cls: 'separator'
13753                 },
13754                 {
13755                     tag: 'td',
13756                     cn: [
13757                         {
13758                             tag: 'button',
13759                             type: 'button',
13760                             cls: 'btn btn-primary period',
13761                             html: 'AM'
13762                             
13763                         }
13764                     ]
13765                 }
13766             ]
13767         });
13768         
13769         time.createChild({
13770             tag: 'tr',
13771             cn: [
13772                 {
13773                     tag: 'td',
13774                     cn: [
13775                         {
13776                             tag: 'a',
13777                             href: '#',
13778                             cls: 'btn',
13779                             cn: [
13780                                 {
13781                                     tag: 'span',
13782                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
13783                                 }
13784                             ]
13785                         }
13786                     ]
13787                 },
13788                 {
13789                     tag: 'td',
13790                     cls: 'separator'
13791                 },
13792                 {
13793                     tag: 'td',
13794                     cn: [
13795                         {
13796                             tag: 'a',
13797                             href: '#',
13798                             cls: 'btn',
13799                             cn: [
13800                                 {
13801                                     tag: 'span',
13802                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13803                                 }
13804                             ]
13805                         }
13806                     ]
13807                 },
13808                 {
13809                     tag: 'td',
13810                     cls: 'separator'
13811                 }
13812             ]
13813         });
13814         
13815     },
13816     
13817     update: function()
13818     {
13819         
13820         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13821         
13822         this.fill();
13823     },
13824     
13825     fill: function() 
13826     {
13827         var hours = this.time.getHours();
13828         var minutes = this.time.getMinutes();
13829         var period = 'AM';
13830         
13831         if(hours > 11){
13832             period = 'PM';
13833         }
13834         
13835         if(hours == 0){
13836             hours = 12;
13837         }
13838         
13839         
13840         if(hours > 12){
13841             hours = hours - 12;
13842         }
13843         
13844         if(hours < 10){
13845             hours = '0' + hours;
13846         }
13847         
13848         if(minutes < 10){
13849             minutes = '0' + minutes;
13850         }
13851         
13852         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13853         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13854         this.pop.select('button', true).first().dom.innerHTML = period;
13855         
13856     },
13857     
13858     place: function()
13859     {   
13860         this.picker().removeClass(['bottom', 'top']);
13861         
13862         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13863             /*
13864              * place to the top of element!
13865              *
13866              */
13867             
13868             this.picker().addClass('top');
13869             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13870             
13871             return;
13872         }
13873         
13874         this.picker().addClass('bottom');
13875         
13876         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13877     },
13878   
13879     onFocus : function()
13880     {
13881         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13882         this.show();
13883     },
13884     
13885     onBlur : function()
13886     {
13887         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13888         this.hide();
13889     },
13890     
13891     show : function()
13892     {
13893         this.picker().show();
13894         this.pop.show();
13895         this.update();
13896         this.place();
13897         
13898         this.fireEvent('show', this, this.date);
13899     },
13900     
13901     hide : function()
13902     {
13903         this.picker().hide();
13904         this.pop.hide();
13905         
13906         this.fireEvent('hide', this, this.date);
13907     },
13908     
13909     setTime : function()
13910     {
13911         this.hide();
13912         this.setValue(this.time.format(this.format));
13913         
13914         this.fireEvent('select', this, this.date);
13915         
13916         
13917     },
13918     
13919     onMousedown: function(e){
13920         e.stopPropagation();
13921         e.preventDefault();
13922     },
13923     
13924     onIncrementHours: function()
13925     {
13926         Roo.log('onIncrementHours');
13927         this.time = this.time.add(Date.HOUR, 1);
13928         this.update();
13929         
13930     },
13931     
13932     onDecrementHours: function()
13933     {
13934         Roo.log('onDecrementHours');
13935         this.time = this.time.add(Date.HOUR, -1);
13936         this.update();
13937     },
13938     
13939     onIncrementMinutes: function()
13940     {
13941         Roo.log('onIncrementMinutes');
13942         this.time = this.time.add(Date.MINUTE, 1);
13943         this.update();
13944     },
13945     
13946     onDecrementMinutes: function()
13947     {
13948         Roo.log('onDecrementMinutes');
13949         this.time = this.time.add(Date.MINUTE, -1);
13950         this.update();
13951     },
13952     
13953     onTogglePeriod: function()
13954     {
13955         Roo.log('onTogglePeriod');
13956         this.time = this.time.add(Date.HOUR, 12);
13957         this.update();
13958     }
13959     
13960    
13961 });
13962
13963 Roo.apply(Roo.bootstrap.TimeField,  {
13964     
13965     content : {
13966         tag: 'tbody',
13967         cn: [
13968             {
13969                 tag: 'tr',
13970                 cn: [
13971                 {
13972                     tag: 'td',
13973                     colspan: '7'
13974                 }
13975                 ]
13976             }
13977         ]
13978     },
13979     
13980     footer : {
13981         tag: 'tfoot',
13982         cn: [
13983             {
13984                 tag: 'tr',
13985                 cn: [
13986                 {
13987                     tag: 'th',
13988                     colspan: '7',
13989                     cls: '',
13990                     cn: [
13991                         {
13992                             tag: 'button',
13993                             cls: 'btn btn-info ok',
13994                             html: 'OK'
13995                         }
13996                     ]
13997                 }
13998
13999                 ]
14000             }
14001         ]
14002     }
14003 });
14004
14005 Roo.apply(Roo.bootstrap.TimeField,  {
14006   
14007     template : {
14008         tag: 'div',
14009         cls: 'datepicker dropdown-menu',
14010         cn: [
14011             {
14012                 tag: 'div',
14013                 cls: 'datepicker-time',
14014                 cn: [
14015                 {
14016                     tag: 'table',
14017                     cls: 'table-condensed',
14018                     cn:[
14019                     Roo.bootstrap.TimeField.content,
14020                     Roo.bootstrap.TimeField.footer
14021                     ]
14022                 }
14023                 ]
14024             }
14025         ]
14026     }
14027 });
14028
14029  
14030
14031  /*
14032  * - LGPL
14033  *
14034  * CheckBox
14035  * 
14036  */
14037
14038 /**
14039  * @class Roo.bootstrap.CheckBox
14040  * @extends Roo.bootstrap.Input
14041  * Bootstrap CheckBox class
14042  * 
14043  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
14044  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
14045  * @cfg {String} boxLabel The text that appears beside the checkbox
14046  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
14047  * @cfg {Boolean} checked initnal the element
14048  * 
14049  * 
14050  * @constructor
14051  * Create a new CheckBox
14052  * @param {Object} config The config object
14053  */
14054
14055 Roo.bootstrap.CheckBox = function(config){
14056     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
14057    
14058         this.addEvents({
14059             /**
14060             * @event check
14061             * Fires when the element is checked or unchecked.
14062             * @param {Roo.bootstrap.CheckBox} this This input
14063             * @param {Boolean} checked The new checked value
14064             */
14065            check : true
14066         });
14067 };
14068
14069 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
14070     
14071     inputType: 'checkbox',
14072     inputValue: 1,
14073     valueOff: 0,
14074     boxLabel: false,
14075     checked: false,
14076     weight : false,
14077     
14078     getAutoCreate : function()
14079     {
14080         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14081         
14082         var id = Roo.id();
14083         
14084         var cfg = {};
14085         
14086         cfg.cls = 'form-group checkbox' //input-group
14087         
14088         
14089         
14090         
14091         var input =  {
14092             tag: 'input',
14093             id : id,
14094             type : this.inputType,
14095             value : (!this.checked) ? this.valueOff : this.inputValue,
14096             cls : 'roo-checkbox', //'form-box',
14097             placeholder : this.placeholder || ''
14098             
14099         };
14100         
14101         if (this.weight) { // Validity check?
14102             cfg.cls += " checkbox-" + this.weight;
14103         }
14104         
14105         if (this.disabled) {
14106             input.disabled=true;
14107         }
14108         
14109         if(this.checked){
14110             input.checked = this.checked;
14111         }
14112         
14113         if (this.name) {
14114             input.name = this.name;
14115         }
14116         
14117         if (this.size) {
14118             input.cls += ' input-' + this.size;
14119         }
14120         
14121         var settings=this;
14122         ['xs','sm','md','lg'].map(function(size){
14123             if (settings[size]) {
14124                 cfg.cls += ' col-' + size + '-' + settings[size];
14125             }
14126         });
14127         
14128        
14129         
14130         var inputblock = input;
14131         
14132         
14133         
14134         
14135         if (this.before || this.after) {
14136             
14137             inputblock = {
14138                 cls : 'input-group',
14139                 cn :  [] 
14140             };
14141             if (this.before) {
14142                 inputblock.cn.push({
14143                     tag :'span',
14144                     cls : 'input-group-addon',
14145                     html : this.before
14146                 });
14147             }
14148             inputblock.cn.push(input);
14149             if (this.after) {
14150                 inputblock.cn.push({
14151                     tag :'span',
14152                     cls : 'input-group-addon',
14153                     html : this.after
14154                 });
14155             }
14156             
14157         };
14158         
14159         if (align ==='left' && this.fieldLabel.length) {
14160                 Roo.log("left and has label");
14161                 cfg.cn = [
14162                     
14163                     {
14164                         tag: 'label',
14165                         'for' :  id,
14166                         cls : 'control-label col-md-' + this.labelWidth,
14167                         html : this.fieldLabel
14168                         
14169                     },
14170                     {
14171                         cls : "col-md-" + (12 - this.labelWidth), 
14172                         cn: [
14173                             inputblock
14174                         ]
14175                     }
14176                     
14177                 ];
14178         } else if ( this.fieldLabel.length) {
14179                 Roo.log(" label");
14180                 cfg.cn = [
14181                    
14182                     {
14183                         tag: this.boxLabel ? 'span' : 'label',
14184                         'for': id,
14185                         cls: 'control-label box-input-label',
14186                         //cls : 'input-group-addon',
14187                         html : this.fieldLabel
14188                         
14189                     },
14190                     
14191                     inputblock
14192                     
14193                 ];
14194
14195         } else {
14196             
14197                 Roo.log(" no label && no align");
14198                 cfg.cn = [  inputblock ] ;
14199                 
14200                 
14201         };
14202          if(this.boxLabel){
14203             cfg.cn.push( {
14204                 tag: 'label',
14205                 'for': id,
14206                 cls: 'box-label',
14207                 html: this.boxLabel
14208                 
14209             });
14210         }
14211         
14212         
14213        
14214         return cfg;
14215         
14216     },
14217     
14218     /**
14219      * return the real input element.
14220      */
14221     inputEl: function ()
14222     {
14223         return this.el.select('input.roo-checkbox',true).first();
14224     },
14225     
14226     label: function()
14227     {
14228         return this.el.select('label.control-label',true).first();
14229     },
14230     
14231     initEvents : function()
14232     {
14233 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
14234         
14235         this.inputEl().on('click', this.onClick,  this);
14236         
14237     },
14238     
14239     onClick : function()
14240     {   
14241         this.setChecked(!this.checked);
14242     },
14243     
14244     setChecked : function(state,suppressEvent)
14245     {
14246         this.checked = state;
14247         
14248         this.inputEl().dom.checked = state;
14249         
14250         if(suppressEvent !== true){
14251             this.fireEvent('check', this, state);
14252         }
14253         
14254         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14255         
14256     },
14257     
14258     setValue : function(v,suppressEvent)
14259     {
14260         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
14261     }
14262     
14263 });
14264
14265  
14266 /*
14267  * - LGPL
14268  *
14269  * Radio
14270  * 
14271  */
14272
14273 /**
14274  * @class Roo.bootstrap.Radio
14275  * @extends Roo.bootstrap.CheckBox
14276  * Bootstrap Radio class
14277
14278  * @constructor
14279  * Create a new Radio
14280  * @param {Object} config The config object
14281  */
14282
14283 Roo.bootstrap.Radio = function(config){
14284     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
14285    
14286 };
14287
14288 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
14289     
14290     inputType: 'radio',
14291     inputValue: '',
14292     valueOff: '',
14293     
14294     getAutoCreate : function()
14295     {
14296         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14297         
14298         var id = Roo.id();
14299         
14300         var cfg = {};
14301         
14302         cfg.cls = 'form-group radio' //input-group
14303         
14304         var input =  {
14305             tag: 'input',
14306             id : id,
14307             type : this.inputType,
14308             value : (!this.checked) ? this.valueOff : this.inputValue,
14309             cls : 'roo-radio',
14310             placeholder : this.placeholder || ''
14311             
14312         };
14313           if (this.weight) { // Validity check?
14314             cfg.cls += " radio-" + this.weight;
14315         }
14316         if (this.disabled) {
14317             input.disabled=true;
14318         }
14319         
14320         if(this.checked){
14321             input.checked = this.checked;
14322         }
14323         
14324         if (this.name) {
14325             input.name = this.name;
14326         }
14327         
14328         if (this.size) {
14329             input.cls += ' input-' + this.size;
14330         }
14331         
14332         var settings=this;
14333         ['xs','sm','md','lg'].map(function(size){
14334             if (settings[size]) {
14335                 cfg.cls += ' col-' + size + '-' + settings[size];
14336             }
14337         });
14338         
14339         var inputblock = input;
14340         
14341         if (this.before || this.after) {
14342             
14343             inputblock = {
14344                 cls : 'input-group',
14345                 cn :  [] 
14346             };
14347             if (this.before) {
14348                 inputblock.cn.push({
14349                     tag :'span',
14350                     cls : 'input-group-addon',
14351                     html : this.before
14352                 });
14353             }
14354             inputblock.cn.push(input);
14355             if (this.after) {
14356                 inputblock.cn.push({
14357                     tag :'span',
14358                     cls : 'input-group-addon',
14359                     html : this.after
14360                 });
14361             }
14362             
14363         };
14364         
14365         if (align ==='left' && this.fieldLabel.length) {
14366                 Roo.log("left and has label");
14367                 cfg.cn = [
14368                     
14369                     {
14370                         tag: 'label',
14371                         'for' :  id,
14372                         cls : 'control-label col-md-' + this.labelWidth,
14373                         html : this.fieldLabel
14374                         
14375                     },
14376                     {
14377                         cls : "col-md-" + (12 - this.labelWidth), 
14378                         cn: [
14379                             inputblock
14380                         ]
14381                     }
14382                     
14383                 ];
14384         } else if ( this.fieldLabel.length) {
14385                 Roo.log(" label");
14386                  cfg.cn = [
14387                    
14388                     {
14389                         tag: 'label',
14390                         'for': id,
14391                         cls: 'control-label box-input-label',
14392                         //cls : 'input-group-addon',
14393                         html : this.fieldLabel
14394                         
14395                     },
14396                     
14397                     inputblock
14398                     
14399                 ];
14400
14401         } else {
14402             
14403                    Roo.log(" no label && no align");
14404                 cfg.cn = [
14405                     
14406                         inputblock
14407                     
14408                 ];
14409                 
14410                 
14411         };
14412         
14413         if(this.boxLabel){
14414             cfg.cn.push({
14415                 tag: 'label',
14416                 'for': id,
14417                 cls: 'box-label',
14418                 html: this.boxLabel
14419             })
14420         }
14421         
14422         return cfg;
14423         
14424     },
14425     inputEl: function ()
14426     {
14427         return this.el.select('input.roo-radio',true).first();
14428     },
14429     onClick : function()
14430     {   
14431         this.setChecked(true);
14432     },
14433     
14434     setChecked : function(state,suppressEvent)
14435     {
14436         if(state){
14437             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14438                 v.dom.checked = false;
14439             });
14440         }
14441         
14442         this.checked = state;
14443         this.inputEl().dom.checked = state;
14444         
14445         if(suppressEvent !== true){
14446             this.fireEvent('check', this, state);
14447         }
14448         
14449         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14450         
14451     },
14452     
14453     getGroupValue : function()
14454     {
14455         var value = ''
14456         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14457             if(v.dom.checked == true){
14458                 value = v.dom.value;
14459             }
14460         });
14461         
14462         return value;
14463     },
14464     
14465     /**
14466      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
14467      * @return {Mixed} value The field value
14468      */
14469     getValue : function(){
14470         return this.getGroupValue();
14471     }
14472     
14473 });
14474
14475  
14476 //<script type="text/javascript">
14477
14478 /*
14479  * Based  Ext JS Library 1.1.1
14480  * Copyright(c) 2006-2007, Ext JS, LLC.
14481  * LGPL
14482  *
14483  */
14484  
14485 /**
14486  * @class Roo.HtmlEditorCore
14487  * @extends Roo.Component
14488  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
14489  *
14490  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
14491  */
14492
14493 Roo.HtmlEditorCore = function(config){
14494     
14495     
14496     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
14497     this.addEvents({
14498         /**
14499          * @event initialize
14500          * Fires when the editor is fully initialized (including the iframe)
14501          * @param {Roo.HtmlEditorCore} this
14502          */
14503         initialize: true,
14504         /**
14505          * @event activate
14506          * Fires when the editor is first receives the focus. Any insertion must wait
14507          * until after this event.
14508          * @param {Roo.HtmlEditorCore} this
14509          */
14510         activate: true,
14511          /**
14512          * @event beforesync
14513          * Fires before the textarea is updated with content from the editor iframe. Return false
14514          * to cancel the sync.
14515          * @param {Roo.HtmlEditorCore} this
14516          * @param {String} html
14517          */
14518         beforesync: true,
14519          /**
14520          * @event beforepush
14521          * Fires before the iframe editor is updated with content from the textarea. Return false
14522          * to cancel the push.
14523          * @param {Roo.HtmlEditorCore} this
14524          * @param {String} html
14525          */
14526         beforepush: true,
14527          /**
14528          * @event sync
14529          * Fires when the textarea is updated with content from the editor iframe.
14530          * @param {Roo.HtmlEditorCore} this
14531          * @param {String} html
14532          */
14533         sync: true,
14534          /**
14535          * @event push
14536          * Fires when the iframe editor is updated with content from the textarea.
14537          * @param {Roo.HtmlEditorCore} this
14538          * @param {String} html
14539          */
14540         push: true,
14541         
14542         /**
14543          * @event editorevent
14544          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14545          * @param {Roo.HtmlEditorCore} this
14546          */
14547         editorevent: true
14548     });
14549      
14550 };
14551
14552
14553 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
14554
14555
14556      /**
14557      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
14558      */
14559     
14560     owner : false,
14561     
14562      /**
14563      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14564      *                        Roo.resizable.
14565      */
14566     resizable : false,
14567      /**
14568      * @cfg {Number} height (in pixels)
14569      */   
14570     height: 300,
14571    /**
14572      * @cfg {Number} width (in pixels)
14573      */   
14574     width: 500,
14575     
14576     /**
14577      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14578      * 
14579      */
14580     stylesheets: false,
14581     
14582     // id of frame..
14583     frameId: false,
14584     
14585     // private properties
14586     validationEvent : false,
14587     deferHeight: true,
14588     initialized : false,
14589     activated : false,
14590     sourceEditMode : false,
14591     onFocus : Roo.emptyFn,
14592     iframePad:3,
14593     hideMode:'offsets',
14594     
14595     clearUp: true,
14596     
14597      
14598     
14599
14600     /**
14601      * Protected method that will not generally be called directly. It
14602      * is called when the editor initializes the iframe with HTML contents. Override this method if you
14603      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
14604      */
14605     getDocMarkup : function(){
14606         // body styles..
14607         var st = '';
14608         Roo.log(this.stylesheets);
14609         
14610         // inherit styels from page...?? 
14611         if (this.stylesheets === false) {
14612             
14613             Roo.get(document.head).select('style').each(function(node) {
14614                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14615             });
14616             
14617             Roo.get(document.head).select('link').each(function(node) { 
14618                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14619             });
14620             
14621         } else if (!this.stylesheets.length) {
14622                 // simple..
14623                 st = '<style type="text/css">' +
14624                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14625                    '</style>';
14626         } else {
14627             Roo.each(this.stylesheets, function(s) {
14628                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
14629             });
14630             
14631         }
14632         
14633         st +=  '<style type="text/css">' +
14634             'IMG { cursor: pointer } ' +
14635         '</style>';
14636
14637         
14638         return '<html><head>' + st  +
14639             //<style type="text/css">' +
14640             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14641             //'</style>' +
14642             ' </head><body class="roo-htmleditor-body"></body></html>';
14643     },
14644
14645     // private
14646     onRender : function(ct, position)
14647     {
14648         var _t = this;
14649         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
14650         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
14651         
14652         
14653         this.el.dom.style.border = '0 none';
14654         this.el.dom.setAttribute('tabIndex', -1);
14655         this.el.addClass('x-hidden hide');
14656         
14657         
14658         
14659         if(Roo.isIE){ // fix IE 1px bogus margin
14660             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
14661         }
14662        
14663         
14664         this.frameId = Roo.id();
14665         
14666          
14667         
14668         var iframe = this.owner.wrap.createChild({
14669             tag: 'iframe',
14670             cls: 'form-control', // bootstrap..
14671             id: this.frameId,
14672             name: this.frameId,
14673             frameBorder : 'no',
14674             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
14675         }, this.el
14676         );
14677         
14678         
14679         this.iframe = iframe.dom;
14680
14681          this.assignDocWin();
14682         
14683         this.doc.designMode = 'on';
14684        
14685         this.doc.open();
14686         this.doc.write(this.getDocMarkup());
14687         this.doc.close();
14688
14689         
14690         var task = { // must defer to wait for browser to be ready
14691             run : function(){
14692                 //console.log("run task?" + this.doc.readyState);
14693                 this.assignDocWin();
14694                 if(this.doc.body || this.doc.readyState == 'complete'){
14695                     try {
14696                         this.doc.designMode="on";
14697                     } catch (e) {
14698                         return;
14699                     }
14700                     Roo.TaskMgr.stop(task);
14701                     this.initEditor.defer(10, this);
14702                 }
14703             },
14704             interval : 10,
14705             duration: 10000,
14706             scope: this
14707         };
14708         Roo.TaskMgr.start(task);
14709
14710         
14711          
14712     },
14713
14714     // private
14715     onResize : function(w, h)
14716     {
14717          Roo.log('resize: ' +w + ',' + h );
14718         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14719         if(!this.iframe){
14720             return;
14721         }
14722         if(typeof w == 'number'){
14723             
14724             this.iframe.style.width = w + 'px';
14725         }
14726         if(typeof h == 'number'){
14727             
14728             this.iframe.style.height = h + 'px';
14729             if(this.doc){
14730                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14731             }
14732         }
14733         
14734     },
14735
14736     /**
14737      * Toggles the editor between standard and source edit mode.
14738      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14739      */
14740     toggleSourceEdit : function(sourceEditMode){
14741         
14742         this.sourceEditMode = sourceEditMode === true;
14743         
14744         if(this.sourceEditMode){
14745  
14746             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
14747             
14748         }else{
14749             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14750             //this.iframe.className = '';
14751             this.deferFocus();
14752         }
14753         //this.setSize(this.owner.wrap.getSize());
14754         //this.fireEvent('editmodechange', this, this.sourceEditMode);
14755     },
14756
14757     
14758   
14759
14760     /**
14761      * Protected method that will not generally be called directly. If you need/want
14762      * custom HTML cleanup, this is the method you should override.
14763      * @param {String} html The HTML to be cleaned
14764      * return {String} The cleaned HTML
14765      */
14766     cleanHtml : function(html){
14767         html = String(html);
14768         if(html.length > 5){
14769             if(Roo.isSafari){ // strip safari nonsense
14770                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14771             }
14772         }
14773         if(html == '&nbsp;'){
14774             html = '';
14775         }
14776         return html;
14777     },
14778
14779     /**
14780      * HTML Editor -> Textarea
14781      * Protected method that will not generally be called directly. Syncs the contents
14782      * of the editor iframe with the textarea.
14783      */
14784     syncValue : function(){
14785         if(this.initialized){
14786             var bd = (this.doc.body || this.doc.documentElement);
14787             //this.cleanUpPaste(); -- this is done else where and causes havoc..
14788             var html = bd.innerHTML;
14789             if(Roo.isSafari){
14790                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14791                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14792                 if(m && m[1]){
14793                     html = '<div style="'+m[0]+'">' + html + '</div>';
14794                 }
14795             }
14796             html = this.cleanHtml(html);
14797             // fix up the special chars.. normaly like back quotes in word...
14798             // however we do not want to do this with chinese..
14799             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14800                 var cc = b.charCodeAt();
14801                 if (
14802                     (cc >= 0x4E00 && cc < 0xA000 ) ||
14803                     (cc >= 0x3400 && cc < 0x4E00 ) ||
14804                     (cc >= 0xf900 && cc < 0xfb00 )
14805                 ) {
14806                         return b;
14807                 }
14808                 return "&#"+cc+";" 
14809             });
14810             if(this.owner.fireEvent('beforesync', this, html) !== false){
14811                 this.el.dom.value = html;
14812                 this.owner.fireEvent('sync', this, html);
14813             }
14814         }
14815     },
14816
14817     /**
14818      * Protected method that will not generally be called directly. Pushes the value of the textarea
14819      * into the iframe editor.
14820      */
14821     pushValue : function(){
14822         if(this.initialized){
14823             var v = this.el.dom.value.trim();
14824             
14825 //            if(v.length < 1){
14826 //                v = '&#160;';
14827 //            }
14828             
14829             if(this.owner.fireEvent('beforepush', this, v) !== false){
14830                 var d = (this.doc.body || this.doc.documentElement);
14831                 d.innerHTML = v;
14832                 this.cleanUpPaste();
14833                 this.el.dom.value = d.innerHTML;
14834                 this.owner.fireEvent('push', this, v);
14835             }
14836         }
14837     },
14838
14839     // private
14840     deferFocus : function(){
14841         this.focus.defer(10, this);
14842     },
14843
14844     // doc'ed in Field
14845     focus : function(){
14846         if(this.win && !this.sourceEditMode){
14847             this.win.focus();
14848         }else{
14849             this.el.focus();
14850         }
14851     },
14852     
14853     assignDocWin: function()
14854     {
14855         var iframe = this.iframe;
14856         
14857          if(Roo.isIE){
14858             this.doc = iframe.contentWindow.document;
14859             this.win = iframe.contentWindow;
14860         } else {
14861             if (!Roo.get(this.frameId)) {
14862                 return;
14863             }
14864             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14865             this.win = Roo.get(this.frameId).dom.contentWindow;
14866         }
14867     },
14868     
14869     // private
14870     initEditor : function(){
14871         //console.log("INIT EDITOR");
14872         this.assignDocWin();
14873         
14874         
14875         
14876         this.doc.designMode="on";
14877         this.doc.open();
14878         this.doc.write(this.getDocMarkup());
14879         this.doc.close();
14880         
14881         var dbody = (this.doc.body || this.doc.documentElement);
14882         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14883         // this copies styles from the containing element into thsi one..
14884         // not sure why we need all of this..
14885         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14886         
14887         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
14888         //ss['background-attachment'] = 'fixed'; // w3c
14889         dbody.bgProperties = 'fixed'; // ie
14890         //Roo.DomHelper.applyStyles(dbody, ss);
14891         Roo.EventManager.on(this.doc, {
14892             //'mousedown': this.onEditorEvent,
14893             'mouseup': this.onEditorEvent,
14894             'dblclick': this.onEditorEvent,
14895             'click': this.onEditorEvent,
14896             'keyup': this.onEditorEvent,
14897             buffer:100,
14898             scope: this
14899         });
14900         if(Roo.isGecko){
14901             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14902         }
14903         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14904             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14905         }
14906         this.initialized = true;
14907
14908         this.owner.fireEvent('initialize', this);
14909         this.pushValue();
14910     },
14911
14912     // private
14913     onDestroy : function(){
14914         
14915         
14916         
14917         if(this.rendered){
14918             
14919             //for (var i =0; i < this.toolbars.length;i++) {
14920             //    // fixme - ask toolbars for heights?
14921             //    this.toolbars[i].onDestroy();
14922            // }
14923             
14924             //this.wrap.dom.innerHTML = '';
14925             //this.wrap.remove();
14926         }
14927     },
14928
14929     // private
14930     onFirstFocus : function(){
14931         
14932         this.assignDocWin();
14933         
14934         
14935         this.activated = true;
14936          
14937     
14938         if(Roo.isGecko){ // prevent silly gecko errors
14939             this.win.focus();
14940             var s = this.win.getSelection();
14941             if(!s.focusNode || s.focusNode.nodeType != 3){
14942                 var r = s.getRangeAt(0);
14943                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14944                 r.collapse(true);
14945                 this.deferFocus();
14946             }
14947             try{
14948                 this.execCmd('useCSS', true);
14949                 this.execCmd('styleWithCSS', false);
14950             }catch(e){}
14951         }
14952         this.owner.fireEvent('activate', this);
14953     },
14954
14955     // private
14956     adjustFont: function(btn){
14957         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14958         //if(Roo.isSafari){ // safari
14959         //    adjust *= 2;
14960        // }
14961         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14962         if(Roo.isSafari){ // safari
14963             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14964             v =  (v < 10) ? 10 : v;
14965             v =  (v > 48) ? 48 : v;
14966             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14967             
14968         }
14969         
14970         
14971         v = Math.max(1, v+adjust);
14972         
14973         this.execCmd('FontSize', v  );
14974     },
14975
14976     onEditorEvent : function(e){
14977         this.owner.fireEvent('editorevent', this, e);
14978       //  this.updateToolbar();
14979         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14980     },
14981
14982     insertTag : function(tg)
14983     {
14984         // could be a bit smarter... -> wrap the current selected tRoo..
14985         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14986             
14987             range = this.createRange(this.getSelection());
14988             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14989             wrappingNode.appendChild(range.extractContents());
14990             range.insertNode(wrappingNode);
14991
14992             return;
14993             
14994             
14995             
14996         }
14997         this.execCmd("formatblock",   tg);
14998         
14999     },
15000     
15001     insertText : function(txt)
15002     {
15003         
15004         
15005         var range = this.createRange();
15006         range.deleteContents();
15007                //alert(Sender.getAttribute('label'));
15008                
15009         range.insertNode(this.doc.createTextNode(txt));
15010     } ,
15011     
15012      
15013
15014     /**
15015      * Executes a Midas editor command on the editor document and performs necessary focus and
15016      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
15017      * @param {String} cmd The Midas command
15018      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
15019      */
15020     relayCmd : function(cmd, value){
15021         this.win.focus();
15022         this.execCmd(cmd, value);
15023         this.owner.fireEvent('editorevent', this);
15024         //this.updateToolbar();
15025         this.owner.deferFocus();
15026     },
15027
15028     /**
15029      * Executes a Midas editor command directly on the editor document.
15030      * For visual commands, you should use {@link #relayCmd} instead.
15031      * <b>This should only be called after the editor is initialized.</b>
15032      * @param {String} cmd The Midas command
15033      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
15034      */
15035     execCmd : function(cmd, value){
15036         this.doc.execCommand(cmd, false, value === undefined ? null : value);
15037         this.syncValue();
15038     },
15039  
15040  
15041    
15042     /**
15043      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
15044      * to insert tRoo.
15045      * @param {String} text | dom node.. 
15046      */
15047     insertAtCursor : function(text)
15048     {
15049         
15050         
15051         
15052         if(!this.activated){
15053             return;
15054         }
15055         /*
15056         if(Roo.isIE){
15057             this.win.focus();
15058             var r = this.doc.selection.createRange();
15059             if(r){
15060                 r.collapse(true);
15061                 r.pasteHTML(text);
15062                 this.syncValue();
15063                 this.deferFocus();
15064             
15065             }
15066             return;
15067         }
15068         */
15069         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
15070             this.win.focus();
15071             
15072             
15073             // from jquery ui (MIT licenced)
15074             var range, node;
15075             var win = this.win;
15076             
15077             if (win.getSelection && win.getSelection().getRangeAt) {
15078                 range = win.getSelection().getRangeAt(0);
15079                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
15080                 range.insertNode(node);
15081             } else if (win.document.selection && win.document.selection.createRange) {
15082                 // no firefox support
15083                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
15084                 win.document.selection.createRange().pasteHTML(txt);
15085             } else {
15086                 // no firefox support
15087                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
15088                 this.execCmd('InsertHTML', txt);
15089             } 
15090             
15091             this.syncValue();
15092             
15093             this.deferFocus();
15094         }
15095     },
15096  // private
15097     mozKeyPress : function(e){
15098         if(e.ctrlKey){
15099             var c = e.getCharCode(), cmd;
15100           
15101             if(c > 0){
15102                 c = String.fromCharCode(c).toLowerCase();
15103                 switch(c){
15104                     case 'b':
15105                         cmd = 'bold';
15106                         break;
15107                     case 'i':
15108                         cmd = 'italic';
15109                         break;
15110                     
15111                     case 'u':
15112                         cmd = 'underline';
15113                         break;
15114                     
15115                     case 'v':
15116                         this.cleanUpPaste.defer(100, this);
15117                         return;
15118                         
15119                 }
15120                 if(cmd){
15121                     this.win.focus();
15122                     this.execCmd(cmd);
15123                     this.deferFocus();
15124                     e.preventDefault();
15125                 }
15126                 
15127             }
15128         }
15129     },
15130
15131     // private
15132     fixKeys : function(){ // load time branching for fastest keydown performance
15133         if(Roo.isIE){
15134             return function(e){
15135                 var k = e.getKey(), r;
15136                 if(k == e.TAB){
15137                     e.stopEvent();
15138                     r = this.doc.selection.createRange();
15139                     if(r){
15140                         r.collapse(true);
15141                         r.pasteHTML('&#160;&#160;&#160;&#160;');
15142                         this.deferFocus();
15143                     }
15144                     return;
15145                 }
15146                 
15147                 if(k == e.ENTER){
15148                     r = this.doc.selection.createRange();
15149                     if(r){
15150                         var target = r.parentElement();
15151                         if(!target || target.tagName.toLowerCase() != 'li'){
15152                             e.stopEvent();
15153                             r.pasteHTML('<br />');
15154                             r.collapse(false);
15155                             r.select();
15156                         }
15157                     }
15158                 }
15159                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15160                     this.cleanUpPaste.defer(100, this);
15161                     return;
15162                 }
15163                 
15164                 
15165             };
15166         }else if(Roo.isOpera){
15167             return function(e){
15168                 var k = e.getKey();
15169                 if(k == e.TAB){
15170                     e.stopEvent();
15171                     this.win.focus();
15172                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
15173                     this.deferFocus();
15174                 }
15175                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15176                     this.cleanUpPaste.defer(100, this);
15177                     return;
15178                 }
15179                 
15180             };
15181         }else if(Roo.isSafari){
15182             return function(e){
15183                 var k = e.getKey();
15184                 
15185                 if(k == e.TAB){
15186                     e.stopEvent();
15187                     this.execCmd('InsertText','\t');
15188                     this.deferFocus();
15189                     return;
15190                 }
15191                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15192                     this.cleanUpPaste.defer(100, this);
15193                     return;
15194                 }
15195                 
15196              };
15197         }
15198     }(),
15199     
15200     getAllAncestors: function()
15201     {
15202         var p = this.getSelectedNode();
15203         var a = [];
15204         if (!p) {
15205             a.push(p); // push blank onto stack..
15206             p = this.getParentElement();
15207         }
15208         
15209         
15210         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
15211             a.push(p);
15212             p = p.parentNode;
15213         }
15214         a.push(this.doc.body);
15215         return a;
15216     },
15217     lastSel : false,
15218     lastSelNode : false,
15219     
15220     
15221     getSelection : function() 
15222     {
15223         this.assignDocWin();
15224         return Roo.isIE ? this.doc.selection : this.win.getSelection();
15225     },
15226     
15227     getSelectedNode: function() 
15228     {
15229         // this may only work on Gecko!!!
15230         
15231         // should we cache this!!!!
15232         
15233         
15234         
15235          
15236         var range = this.createRange(this.getSelection()).cloneRange();
15237         
15238         if (Roo.isIE) {
15239             var parent = range.parentElement();
15240             while (true) {
15241                 var testRange = range.duplicate();
15242                 testRange.moveToElementText(parent);
15243                 if (testRange.inRange(range)) {
15244                     break;
15245                 }
15246                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
15247                     break;
15248                 }
15249                 parent = parent.parentElement;
15250             }
15251             return parent;
15252         }
15253         
15254         // is ancestor a text element.
15255         var ac =  range.commonAncestorContainer;
15256         if (ac.nodeType == 3) {
15257             ac = ac.parentNode;
15258         }
15259         
15260         var ar = ac.childNodes;
15261          
15262         var nodes = [];
15263         var other_nodes = [];
15264         var has_other_nodes = false;
15265         for (var i=0;i<ar.length;i++) {
15266             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
15267                 continue;
15268             }
15269             // fullly contained node.
15270             
15271             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
15272                 nodes.push(ar[i]);
15273                 continue;
15274             }
15275             
15276             // probably selected..
15277             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
15278                 other_nodes.push(ar[i]);
15279                 continue;
15280             }
15281             // outer..
15282             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
15283                 continue;
15284             }
15285             
15286             
15287             has_other_nodes = true;
15288         }
15289         if (!nodes.length && other_nodes.length) {
15290             nodes= other_nodes;
15291         }
15292         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
15293             return false;
15294         }
15295         
15296         return nodes[0];
15297     },
15298     createRange: function(sel)
15299     {
15300         // this has strange effects when using with 
15301         // top toolbar - not sure if it's a great idea.
15302         //this.editor.contentWindow.focus();
15303         if (typeof sel != "undefined") {
15304             try {
15305                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
15306             } catch(e) {
15307                 return this.doc.createRange();
15308             }
15309         } else {
15310             return this.doc.createRange();
15311         }
15312     },
15313     getParentElement: function()
15314     {
15315         
15316         this.assignDocWin();
15317         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
15318         
15319         var range = this.createRange(sel);
15320          
15321         try {
15322             var p = range.commonAncestorContainer;
15323             while (p.nodeType == 3) { // text node
15324                 p = p.parentNode;
15325             }
15326             return p;
15327         } catch (e) {
15328             return null;
15329         }
15330     
15331     },
15332     /***
15333      *
15334      * Range intersection.. the hard stuff...
15335      *  '-1' = before
15336      *  '0' = hits..
15337      *  '1' = after.
15338      *         [ -- selected range --- ]
15339      *   [fail]                        [fail]
15340      *
15341      *    basically..
15342      *      if end is before start or  hits it. fail.
15343      *      if start is after end or hits it fail.
15344      *
15345      *   if either hits (but other is outside. - then it's not 
15346      *   
15347      *    
15348      **/
15349     
15350     
15351     // @see http://www.thismuchiknow.co.uk/?p=64.
15352     rangeIntersectsNode : function(range, node)
15353     {
15354         var nodeRange = node.ownerDocument.createRange();
15355         try {
15356             nodeRange.selectNode(node);
15357         } catch (e) {
15358             nodeRange.selectNodeContents(node);
15359         }
15360     
15361         var rangeStartRange = range.cloneRange();
15362         rangeStartRange.collapse(true);
15363     
15364         var rangeEndRange = range.cloneRange();
15365         rangeEndRange.collapse(false);
15366     
15367         var nodeStartRange = nodeRange.cloneRange();
15368         nodeStartRange.collapse(true);
15369     
15370         var nodeEndRange = nodeRange.cloneRange();
15371         nodeEndRange.collapse(false);
15372     
15373         return rangeStartRange.compareBoundaryPoints(
15374                  Range.START_TO_START, nodeEndRange) == -1 &&
15375                rangeEndRange.compareBoundaryPoints(
15376                  Range.START_TO_START, nodeStartRange) == 1;
15377         
15378          
15379     },
15380     rangeCompareNode : function(range, node)
15381     {
15382         var nodeRange = node.ownerDocument.createRange();
15383         try {
15384             nodeRange.selectNode(node);
15385         } catch (e) {
15386             nodeRange.selectNodeContents(node);
15387         }
15388         
15389         
15390         range.collapse(true);
15391     
15392         nodeRange.collapse(true);
15393      
15394         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15395         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15396          
15397         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15398         
15399         var nodeIsBefore   =  ss == 1;
15400         var nodeIsAfter    = ee == -1;
15401         
15402         if (nodeIsBefore && nodeIsAfter)
15403             return 0; // outer
15404         if (!nodeIsBefore && nodeIsAfter)
15405             return 1; //right trailed.
15406         
15407         if (nodeIsBefore && !nodeIsAfter)
15408             return 2;  // left trailed.
15409         // fully contined.
15410         return 3;
15411     },
15412
15413     // private? - in a new class?
15414     cleanUpPaste :  function()
15415     {
15416         // cleans up the whole document..
15417         Roo.log('cleanuppaste');
15418         
15419         this.cleanUpChildren(this.doc.body);
15420         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15421         if (clean != this.doc.body.innerHTML) {
15422             this.doc.body.innerHTML = clean;
15423         }
15424         
15425     },
15426     
15427     cleanWordChars : function(input) {// change the chars to hex code
15428         var he = Roo.HtmlEditorCore;
15429         
15430         var output = input;
15431         Roo.each(he.swapCodes, function(sw) { 
15432             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15433             
15434             output = output.replace(swapper, sw[1]);
15435         });
15436         
15437         return output;
15438     },
15439     
15440     
15441     cleanUpChildren : function (n)
15442     {
15443         if (!n.childNodes.length) {
15444             return;
15445         }
15446         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15447            this.cleanUpChild(n.childNodes[i]);
15448         }
15449     },
15450     
15451     
15452         
15453     
15454     cleanUpChild : function (node)
15455     {
15456         var ed = this;
15457         //console.log(node);
15458         if (node.nodeName == "#text") {
15459             // clean up silly Windows -- stuff?
15460             return; 
15461         }
15462         if (node.nodeName == "#comment") {
15463             node.parentNode.removeChild(node);
15464             // clean up silly Windows -- stuff?
15465             return; 
15466         }
15467         
15468         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
15469             // remove node.
15470             node.parentNode.removeChild(node);
15471             return;
15472             
15473         }
15474         
15475         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
15476         
15477         // remove <a name=....> as rendering on yahoo mailer is borked with this.
15478         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
15479         
15480         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
15481         //    remove_keep_children = true;
15482         //}
15483         
15484         if (remove_keep_children) {
15485             this.cleanUpChildren(node);
15486             // inserts everything just before this node...
15487             while (node.childNodes.length) {
15488                 var cn = node.childNodes[0];
15489                 node.removeChild(cn);
15490                 node.parentNode.insertBefore(cn, node);
15491             }
15492             node.parentNode.removeChild(node);
15493             return;
15494         }
15495         
15496         if (!node.attributes || !node.attributes.length) {
15497             this.cleanUpChildren(node);
15498             return;
15499         }
15500         
15501         function cleanAttr(n,v)
15502         {
15503             
15504             if (v.match(/^\./) || v.match(/^\//)) {
15505                 return;
15506             }
15507             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
15508                 return;
15509             }
15510             if (v.match(/^#/)) {
15511                 return;
15512             }
15513 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
15514             node.removeAttribute(n);
15515             
15516         }
15517         
15518         function cleanStyle(n,v)
15519         {
15520             if (v.match(/expression/)) { //XSS?? should we even bother..
15521                 node.removeAttribute(n);
15522                 return;
15523             }
15524             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
15525             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
15526             
15527             
15528             var parts = v.split(/;/);
15529             var clean = [];
15530             
15531             Roo.each(parts, function(p) {
15532                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
15533                 if (!p.length) {
15534                     return true;
15535                 }
15536                 var l = p.split(':').shift().replace(/\s+/g,'');
15537                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
15538                 
15539                 if ( cblack.indexOf(l) > -1) {
15540 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15541                     //node.removeAttribute(n);
15542                     return true;
15543                 }
15544                 //Roo.log()
15545                 // only allow 'c whitelisted system attributes'
15546                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
15547 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15548                     //node.removeAttribute(n);
15549                     return true;
15550                 }
15551                 
15552                 
15553                  
15554                 
15555                 clean.push(p);
15556                 return true;
15557             });
15558             if (clean.length) { 
15559                 node.setAttribute(n, clean.join(';'));
15560             } else {
15561                 node.removeAttribute(n);
15562             }
15563             
15564         }
15565         
15566         
15567         for (var i = node.attributes.length-1; i > -1 ; i--) {
15568             var a = node.attributes[i];
15569             //console.log(a);
15570             
15571             if (a.name.toLowerCase().substr(0,2)=='on')  {
15572                 node.removeAttribute(a.name);
15573                 continue;
15574             }
15575             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
15576                 node.removeAttribute(a.name);
15577                 continue;
15578             }
15579             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
15580                 cleanAttr(a.name,a.value); // fixme..
15581                 continue;
15582             }
15583             if (a.name == 'style') {
15584                 cleanStyle(a.name,a.value);
15585                 continue;
15586             }
15587             /// clean up MS crap..
15588             // tecnically this should be a list of valid class'es..
15589             
15590             
15591             if (a.name == 'class') {
15592                 if (a.value.match(/^Mso/)) {
15593                     node.className = '';
15594                 }
15595                 
15596                 if (a.value.match(/body/)) {
15597                     node.className = '';
15598                 }
15599                 continue;
15600             }
15601             
15602             // style cleanup!?
15603             // class cleanup?
15604             
15605         }
15606         
15607         
15608         this.cleanUpChildren(node);
15609         
15610         
15611     },
15612     /**
15613      * Clean up MS wordisms...
15614      */
15615     cleanWord : function(node)
15616     {
15617         var _t = this;
15618         var cleanWordChildren = function()
15619         {
15620             if (!node.childNodes.length) {
15621                 return;
15622             }
15623             for (var i = node.childNodes.length-1; i > -1 ; i--) {
15624                _t.cleanWord(node.childNodes[i]);
15625             }
15626         }
15627         
15628         
15629         if (!node) {
15630             this.cleanWord(this.doc.body);
15631             return;
15632         }
15633         if (node.nodeName == "#text") {
15634             // clean up silly Windows -- stuff?
15635             return; 
15636         }
15637         if (node.nodeName == "#comment") {
15638             node.parentNode.removeChild(node);
15639             // clean up silly Windows -- stuff?
15640             return; 
15641         }
15642         
15643         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
15644             node.parentNode.removeChild(node);
15645             return;
15646         }
15647         
15648         // remove - but keep children..
15649         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
15650             while (node.childNodes.length) {
15651                 var cn = node.childNodes[0];
15652                 node.removeChild(cn);
15653                 node.parentNode.insertBefore(cn, node);
15654             }
15655             node.parentNode.removeChild(node);
15656             cleanWordChildren();
15657             return;
15658         }
15659         // clean styles
15660         if (node.className.length) {
15661             
15662             var cn = node.className.split(/\W+/);
15663             var cna = [];
15664             Roo.each(cn, function(cls) {
15665                 if (cls.match(/Mso[a-zA-Z]+/)) {
15666                     return;
15667                 }
15668                 cna.push(cls);
15669             });
15670             node.className = cna.length ? cna.join(' ') : '';
15671             if (!cna.length) {
15672                 node.removeAttribute("class");
15673             }
15674         }
15675         
15676         if (node.hasAttribute("lang")) {
15677             node.removeAttribute("lang");
15678         }
15679         
15680         if (node.hasAttribute("style")) {
15681             
15682             var styles = node.getAttribute("style").split(";");
15683             var nstyle = [];
15684             Roo.each(styles, function(s) {
15685                 if (!s.match(/:/)) {
15686                     return;
15687                 }
15688                 var kv = s.split(":");
15689                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
15690                     return;
15691                 }
15692                 // what ever is left... we allow.
15693                 nstyle.push(s);
15694             });
15695             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
15696             if (!nstyle.length) {
15697                 node.removeAttribute('style');
15698             }
15699         }
15700         
15701         cleanWordChildren();
15702         
15703         
15704     },
15705     domToHTML : function(currentElement, depth, nopadtext) {
15706         
15707             depth = depth || 0;
15708             nopadtext = nopadtext || false;
15709         
15710             if (!currentElement) {
15711                 return this.domToHTML(this.doc.body);
15712             }
15713             
15714             //Roo.log(currentElement);
15715             var j;
15716             var allText = false;
15717             var nodeName = currentElement.nodeName;
15718             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
15719             
15720             if  (nodeName == '#text') {
15721                 return currentElement.nodeValue;
15722             }
15723             
15724             
15725             var ret = '';
15726             if (nodeName != 'BODY') {
15727                  
15728                 var i = 0;
15729                 // Prints the node tagName, such as <A>, <IMG>, etc
15730                 if (tagName) {
15731                     var attr = [];
15732                     for(i = 0; i < currentElement.attributes.length;i++) {
15733                         // quoting?
15734                         var aname = currentElement.attributes.item(i).name;
15735                         if (!currentElement.attributes.item(i).value.length) {
15736                             continue;
15737                         }
15738                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
15739                     }
15740                     
15741                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
15742                 } 
15743                 else {
15744                     
15745                     // eack
15746                 }
15747             } else {
15748                 tagName = false;
15749             }
15750             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
15751                 return ret;
15752             }
15753             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
15754                 nopadtext = true;
15755             }
15756             
15757             
15758             // Traverse the tree
15759             i = 0;
15760             var currentElementChild = currentElement.childNodes.item(i);
15761             var allText = true;
15762             var innerHTML  = '';
15763             lastnode = '';
15764             while (currentElementChild) {
15765                 // Formatting code (indent the tree so it looks nice on the screen)
15766                 var nopad = nopadtext;
15767                 if (lastnode == 'SPAN') {
15768                     nopad  = true;
15769                 }
15770                 // text
15771                 if  (currentElementChild.nodeName == '#text') {
15772                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
15773                     if (!nopad && toadd.length > 80) {
15774                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
15775                     }
15776                     innerHTML  += toadd;
15777                     
15778                     i++;
15779                     currentElementChild = currentElement.childNodes.item(i);
15780                     lastNode = '';
15781                     continue;
15782                 }
15783                 allText = false;
15784                 
15785                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
15786                     
15787                 // Recursively traverse the tree structure of the child node
15788                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
15789                 lastnode = currentElementChild.nodeName;
15790                 i++;
15791                 currentElementChild=currentElement.childNodes.item(i);
15792             }
15793             
15794             ret += innerHTML;
15795             
15796             if (!allText) {
15797                     // The remaining code is mostly for formatting the tree
15798                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
15799             }
15800             
15801             
15802             if (tagName) {
15803                 ret+= "</"+tagName+">";
15804             }
15805             return ret;
15806             
15807         }
15808     
15809     // hide stuff that is not compatible
15810     /**
15811      * @event blur
15812      * @hide
15813      */
15814     /**
15815      * @event change
15816      * @hide
15817      */
15818     /**
15819      * @event focus
15820      * @hide
15821      */
15822     /**
15823      * @event specialkey
15824      * @hide
15825      */
15826     /**
15827      * @cfg {String} fieldClass @hide
15828      */
15829     /**
15830      * @cfg {String} focusClass @hide
15831      */
15832     /**
15833      * @cfg {String} autoCreate @hide
15834      */
15835     /**
15836      * @cfg {String} inputType @hide
15837      */
15838     /**
15839      * @cfg {String} invalidClass @hide
15840      */
15841     /**
15842      * @cfg {String} invalidText @hide
15843      */
15844     /**
15845      * @cfg {String} msgFx @hide
15846      */
15847     /**
15848      * @cfg {String} validateOnBlur @hide
15849      */
15850 });
15851
15852 Roo.HtmlEditorCore.white = [
15853         'area', 'br', 'img', 'input', 'hr', 'wbr',
15854         
15855        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
15856        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
15857        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
15858        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
15859        'table',   'ul',         'xmp', 
15860        
15861        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
15862       'thead',   'tr', 
15863      
15864       'dir', 'menu', 'ol', 'ul', 'dl',
15865        
15866       'embed',  'object'
15867 ];
15868
15869
15870 Roo.HtmlEditorCore.black = [
15871     //    'embed',  'object', // enable - backend responsiblity to clean thiese
15872         'applet', // 
15873         'base',   'basefont', 'bgsound', 'blink',  'body', 
15874         'frame',  'frameset', 'head',    'html',   'ilayer', 
15875         'iframe', 'layer',  'link',     'meta',    'object',   
15876         'script', 'style' ,'title',  'xml' // clean later..
15877 ];
15878 Roo.HtmlEditorCore.clean = [
15879     'script', 'style', 'title', 'xml'
15880 ];
15881 Roo.HtmlEditorCore.remove = [
15882     'font'
15883 ];
15884 // attributes..
15885
15886 Roo.HtmlEditorCore.ablack = [
15887     'on'
15888 ];
15889     
15890 Roo.HtmlEditorCore.aclean = [ 
15891     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
15892 ];
15893
15894 // protocols..
15895 Roo.HtmlEditorCore.pwhite= [
15896         'http',  'https',  'mailto'
15897 ];
15898
15899 // white listed style attributes.
15900 Roo.HtmlEditorCore.cwhite= [
15901       //  'text-align', /// default is to allow most things..
15902       
15903          
15904 //        'font-size'//??
15905 ];
15906
15907 // black listed style attributes.
15908 Roo.HtmlEditorCore.cblack= [
15909       //  'font-size' -- this can be set by the project 
15910 ];
15911
15912
15913 Roo.HtmlEditorCore.swapCodes   =[ 
15914     [    8211, "--" ], 
15915     [    8212, "--" ], 
15916     [    8216,  "'" ],  
15917     [    8217, "'" ],  
15918     [    8220, '"' ],  
15919     [    8221, '"' ],  
15920     [    8226, "*" ],  
15921     [    8230, "..." ]
15922 ]; 
15923
15924     /*
15925  * - LGPL
15926  *
15927  * HtmlEditor
15928  * 
15929  */
15930
15931 /**
15932  * @class Roo.bootstrap.HtmlEditor
15933  * @extends Roo.bootstrap.TextArea
15934  * Bootstrap HtmlEditor class
15935
15936  * @constructor
15937  * Create a new HtmlEditor
15938  * @param {Object} config The config object
15939  */
15940
15941 Roo.bootstrap.HtmlEditor = function(config){
15942     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15943     if (!this.toolbars) {
15944         this.toolbars = [];
15945     }
15946     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15947     this.addEvents({
15948             /**
15949              * @event initialize
15950              * Fires when the editor is fully initialized (including the iframe)
15951              * @param {HtmlEditor} this
15952              */
15953             initialize: true,
15954             /**
15955              * @event activate
15956              * Fires when the editor is first receives the focus. Any insertion must wait
15957              * until after this event.
15958              * @param {HtmlEditor} this
15959              */
15960             activate: true,
15961              /**
15962              * @event beforesync
15963              * Fires before the textarea is updated with content from the editor iframe. Return false
15964              * to cancel the sync.
15965              * @param {HtmlEditor} this
15966              * @param {String} html
15967              */
15968             beforesync: true,
15969              /**
15970              * @event beforepush
15971              * Fires before the iframe editor is updated with content from the textarea. Return false
15972              * to cancel the push.
15973              * @param {HtmlEditor} this
15974              * @param {String} html
15975              */
15976             beforepush: true,
15977              /**
15978              * @event sync
15979              * Fires when the textarea is updated with content from the editor iframe.
15980              * @param {HtmlEditor} this
15981              * @param {String} html
15982              */
15983             sync: true,
15984              /**
15985              * @event push
15986              * Fires when the iframe editor is updated with content from the textarea.
15987              * @param {HtmlEditor} this
15988              * @param {String} html
15989              */
15990             push: true,
15991              /**
15992              * @event editmodechange
15993              * Fires when the editor switches edit modes
15994              * @param {HtmlEditor} this
15995              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15996              */
15997             editmodechange: true,
15998             /**
15999              * @event editorevent
16000              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16001              * @param {HtmlEditor} this
16002              */
16003             editorevent: true,
16004             /**
16005              * @event firstfocus
16006              * Fires when on first focus - needed by toolbars..
16007              * @param {HtmlEditor} this
16008              */
16009             firstfocus: true,
16010             /**
16011              * @event autosave
16012              * Auto save the htmlEditor value as a file into Events
16013              * @param {HtmlEditor} this
16014              */
16015             autosave: true,
16016             /**
16017              * @event savedpreview
16018              * preview the saved version of htmlEditor
16019              * @param {HtmlEditor} this
16020              */
16021             savedpreview: true
16022         });
16023 };
16024
16025
16026 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
16027     
16028     
16029       /**
16030      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
16031      */
16032     toolbars : false,
16033    
16034      /**
16035      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16036      *                        Roo.resizable.
16037      */
16038     resizable : false,
16039      /**
16040      * @cfg {Number} height (in pixels)
16041      */   
16042     height: 300,
16043    /**
16044      * @cfg {Number} width (in pixels)
16045      */   
16046     width: false,
16047     
16048     /**
16049      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16050      * 
16051      */
16052     stylesheets: false,
16053     
16054     // id of frame..
16055     frameId: false,
16056     
16057     // private properties
16058     validationEvent : false,
16059     deferHeight: true,
16060     initialized : false,
16061     activated : false,
16062     
16063     onFocus : Roo.emptyFn,
16064     iframePad:3,
16065     hideMode:'offsets',
16066     
16067     
16068     tbContainer : false,
16069     
16070     toolbarContainer :function() {
16071         return this.wrap.select('.x-html-editor-tb',true).first();
16072     },
16073
16074     /**
16075      * Protected method that will not generally be called directly. It
16076      * is called when the editor creates its toolbar. Override this method if you need to
16077      * add custom toolbar buttons.
16078      * @param {HtmlEditor} editor
16079      */
16080     createToolbar : function(){
16081         
16082         Roo.log("create toolbars");
16083         
16084         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
16085         this.toolbars[0].render(this.toolbarContainer());
16086         
16087         return;
16088         
16089 //        if (!editor.toolbars || !editor.toolbars.length) {
16090 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
16091 //        }
16092 //        
16093 //        for (var i =0 ; i < editor.toolbars.length;i++) {
16094 //            editor.toolbars[i] = Roo.factory(
16095 //                    typeof(editor.toolbars[i]) == 'string' ?
16096 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
16097 //                Roo.bootstrap.HtmlEditor);
16098 //            editor.toolbars[i].init(editor);
16099 //        }
16100     },
16101
16102      
16103     // private
16104     onRender : function(ct, position)
16105     {
16106        // Roo.log("Call onRender: " + this.xtype);
16107         var _t = this;
16108         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
16109       
16110         this.wrap = this.inputEl().wrap({
16111             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
16112         });
16113         
16114         this.editorcore.onRender(ct, position);
16115          
16116         if (this.resizable) {
16117             this.resizeEl = new Roo.Resizable(this.wrap, {
16118                 pinned : true,
16119                 wrap: true,
16120                 dynamic : true,
16121                 minHeight : this.height,
16122                 height: this.height,
16123                 handles : this.resizable,
16124                 width: this.width,
16125                 listeners : {
16126                     resize : function(r, w, h) {
16127                         _t.onResize(w,h); // -something
16128                     }
16129                 }
16130             });
16131             
16132         }
16133         this.createToolbar(this);
16134        
16135         
16136         if(!this.width && this.resizable){
16137             this.setSize(this.wrap.getSize());
16138         }
16139         if (this.resizeEl) {
16140             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
16141             // should trigger onReize..
16142         }
16143         
16144     },
16145
16146     // private
16147     onResize : function(w, h)
16148     {
16149         Roo.log('resize: ' +w + ',' + h );
16150         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
16151         var ew = false;
16152         var eh = false;
16153         
16154         if(this.inputEl() ){
16155             if(typeof w == 'number'){
16156                 var aw = w - this.wrap.getFrameWidth('lr');
16157                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
16158                 ew = aw;
16159             }
16160             if(typeof h == 'number'){
16161                  var tbh = -11;  // fixme it needs to tool bar size!
16162                 for (var i =0; i < this.toolbars.length;i++) {
16163                     // fixme - ask toolbars for heights?
16164                     tbh += this.toolbars[i].el.getHeight();
16165                     //if (this.toolbars[i].footer) {
16166                     //    tbh += this.toolbars[i].footer.el.getHeight();
16167                     //}
16168                 }
16169               
16170                 
16171                 
16172                 
16173                 
16174                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
16175                 ah -= 5; // knock a few pixes off for look..
16176                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
16177                 var eh = ah;
16178             }
16179         }
16180         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
16181         this.editorcore.onResize(ew,eh);
16182         
16183     },
16184
16185     /**
16186      * Toggles the editor between standard and source edit mode.
16187      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16188      */
16189     toggleSourceEdit : function(sourceEditMode)
16190     {
16191         this.editorcore.toggleSourceEdit(sourceEditMode);
16192         
16193         if(this.editorcore.sourceEditMode){
16194             Roo.log('editor - showing textarea');
16195             
16196 //            Roo.log('in');
16197 //            Roo.log(this.syncValue());
16198             this.syncValue();
16199             this.inputEl().removeClass('hide');
16200             this.inputEl().dom.removeAttribute('tabIndex');
16201             this.inputEl().focus();
16202         }else{
16203             Roo.log('editor - hiding textarea');
16204 //            Roo.log('out')
16205 //            Roo.log(this.pushValue()); 
16206             this.pushValue();
16207             
16208             this.inputEl().addClass('hide');
16209             this.inputEl().dom.setAttribute('tabIndex', -1);
16210             //this.deferFocus();
16211         }
16212          
16213         if(this.resizable){
16214             this.setSize(this.wrap.getSize());
16215         }
16216         
16217         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
16218     },
16219  
16220     // private (for BoxComponent)
16221     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16222
16223     // private (for BoxComponent)
16224     getResizeEl : function(){
16225         return this.wrap;
16226     },
16227
16228     // private (for BoxComponent)
16229     getPositionEl : function(){
16230         return this.wrap;
16231     },
16232
16233     // private
16234     initEvents : function(){
16235         this.originalValue = this.getValue();
16236     },
16237
16238 //    /**
16239 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16240 //     * @method
16241 //     */
16242 //    markInvalid : Roo.emptyFn,
16243 //    /**
16244 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16245 //     * @method
16246 //     */
16247 //    clearInvalid : Roo.emptyFn,
16248
16249     setValue : function(v){
16250         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
16251         this.editorcore.pushValue();
16252     },
16253
16254      
16255     // private
16256     deferFocus : function(){
16257         this.focus.defer(10, this);
16258     },
16259
16260     // doc'ed in Field
16261     focus : function(){
16262         this.editorcore.focus();
16263         
16264     },
16265       
16266
16267     // private
16268     onDestroy : function(){
16269         
16270         
16271         
16272         if(this.rendered){
16273             
16274             for (var i =0; i < this.toolbars.length;i++) {
16275                 // fixme - ask toolbars for heights?
16276                 this.toolbars[i].onDestroy();
16277             }
16278             
16279             this.wrap.dom.innerHTML = '';
16280             this.wrap.remove();
16281         }
16282     },
16283
16284     // private
16285     onFirstFocus : function(){
16286         //Roo.log("onFirstFocus");
16287         this.editorcore.onFirstFocus();
16288          for (var i =0; i < this.toolbars.length;i++) {
16289             this.toolbars[i].onFirstFocus();
16290         }
16291         
16292     },
16293     
16294     // private
16295     syncValue : function()
16296     {   
16297         this.editorcore.syncValue();
16298     },
16299     
16300     pushValue : function()
16301     {   
16302         this.editorcore.pushValue();
16303     }
16304      
16305     
16306     // hide stuff that is not compatible
16307     /**
16308      * @event blur
16309      * @hide
16310      */
16311     /**
16312      * @event change
16313      * @hide
16314      */
16315     /**
16316      * @event focus
16317      * @hide
16318      */
16319     /**
16320      * @event specialkey
16321      * @hide
16322      */
16323     /**
16324      * @cfg {String} fieldClass @hide
16325      */
16326     /**
16327      * @cfg {String} focusClass @hide
16328      */
16329     /**
16330      * @cfg {String} autoCreate @hide
16331      */
16332     /**
16333      * @cfg {String} inputType @hide
16334      */
16335     /**
16336      * @cfg {String} invalidClass @hide
16337      */
16338     /**
16339      * @cfg {String} invalidText @hide
16340      */
16341     /**
16342      * @cfg {String} msgFx @hide
16343      */
16344     /**
16345      * @cfg {String} validateOnBlur @hide
16346      */
16347 });
16348  
16349     
16350    
16351    
16352    
16353       
16354 Roo.namespace('Roo.bootstrap.htmleditor');
16355 /**
16356  * @class Roo.bootstrap.HtmlEditorToolbar1
16357  * Basic Toolbar
16358  * 
16359  * Usage:
16360  *
16361  new Roo.bootstrap.HtmlEditor({
16362     ....
16363     toolbars : [
16364         new Roo.bootstrap.HtmlEditorToolbar1({
16365             disable : { fonts: 1 , format: 1, ..., ... , ...],
16366             btns : [ .... ]
16367         })
16368     }
16369      
16370  * 
16371  * @cfg {Object} disable List of elements to disable..
16372  * @cfg {Array} btns List of additional buttons.
16373  * 
16374  * 
16375  * NEEDS Extra CSS? 
16376  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
16377  */
16378  
16379 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
16380 {
16381     
16382     Roo.apply(this, config);
16383     
16384     // default disabled, based on 'good practice'..
16385     this.disable = this.disable || {};
16386     Roo.applyIf(this.disable, {
16387         fontSize : true,
16388         colors : true,
16389         specialElements : true
16390     });
16391     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
16392     
16393     this.editor = config.editor;
16394     this.editorcore = config.editor.editorcore;
16395     
16396     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
16397     
16398     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
16399     // dont call parent... till later.
16400 }
16401 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
16402      
16403     bar : true,
16404     
16405     editor : false,
16406     editorcore : false,
16407     
16408     
16409     formats : [
16410         "p" ,  
16411         "h1","h2","h3","h4","h5","h6", 
16412         "pre", "code", 
16413         "abbr", "acronym", "address", "cite", "samp", "var",
16414         'div','span'
16415     ],
16416     
16417     onRender : function(ct, position)
16418     {
16419        // Roo.log("Call onRender: " + this.xtype);
16420         
16421        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
16422        Roo.log(this.el);
16423        this.el.dom.style.marginBottom = '0';
16424        var _this = this;
16425        var editorcore = this.editorcore;
16426        var editor= this.editor;
16427        
16428        var children = [];
16429        var btn = function(id,cmd , toggle, handler){
16430        
16431             var  event = toggle ? 'toggle' : 'click';
16432        
16433             var a = {
16434                 size : 'sm',
16435                 xtype: 'Button',
16436                 xns: Roo.bootstrap,
16437                 glyphicon : id,
16438                 cmd : id || cmd,
16439                 enableToggle:toggle !== false,
16440                 //html : 'submit'
16441                 pressed : toggle ? false : null,
16442                 listeners : {}
16443             }
16444             a.listeners[toggle ? 'toggle' : 'click'] = function() {
16445                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
16446             }
16447             children.push(a);
16448             return a;
16449        }
16450         
16451         var style = {
16452                 xtype: 'Button',
16453                 size : 'sm',
16454                 xns: Roo.bootstrap,
16455                 glyphicon : 'font',
16456                 //html : 'submit'
16457                 menu : {
16458                     xtype: 'Menu',
16459                     xns: Roo.bootstrap,
16460                     items:  []
16461                 }
16462         };
16463         Roo.each(this.formats, function(f) {
16464             style.menu.items.push({
16465                 xtype :'MenuItem',
16466                 xns: Roo.bootstrap,
16467                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
16468                 tagname : f,
16469                 listeners : {
16470                     click : function()
16471                     {
16472                         editorcore.insertTag(this.tagname);
16473                         editor.focus();
16474                     }
16475                 }
16476                 
16477             });
16478         });
16479          children.push(style);   
16480             
16481             
16482         btn('bold',false,true);
16483         btn('italic',false,true);
16484         btn('align-left', 'justifyleft',true);
16485         btn('align-center', 'justifycenter',true);
16486         btn('align-right' , 'justifyright',true);
16487         btn('link', false, false, function(btn) {
16488             //Roo.log("create link?");
16489             var url = prompt(this.createLinkText, this.defaultLinkValue);
16490             if(url && url != 'http:/'+'/'){
16491                 this.editorcore.relayCmd('createlink', url);
16492             }
16493         }),
16494         btn('list','insertunorderedlist',true);
16495         btn('pencil', false,true, function(btn){
16496                 Roo.log(this);
16497                 
16498                 this.toggleSourceEdit(btn.pressed);
16499         });
16500         /*
16501         var cog = {
16502                 xtype: 'Button',
16503                 size : 'sm',
16504                 xns: Roo.bootstrap,
16505                 glyphicon : 'cog',
16506                 //html : 'submit'
16507                 menu : {
16508                     xtype: 'Menu',
16509                     xns: Roo.bootstrap,
16510                     items:  []
16511                 }
16512         };
16513         
16514         cog.menu.items.push({
16515             xtype :'MenuItem',
16516             xns: Roo.bootstrap,
16517             html : Clean styles,
16518             tagname : f,
16519             listeners : {
16520                 click : function()
16521                 {
16522                     editorcore.insertTag(this.tagname);
16523                     editor.focus();
16524                 }
16525             }
16526             
16527         });
16528        */
16529         
16530          
16531        this.xtype = 'NavSimplebar';
16532         
16533         for(var i=0;i< children.length;i++) {
16534             
16535             this.buttons.add(this.addxtypeChild(children[i]));
16536             
16537         }
16538         
16539         editor.on('editorevent', this.updateToolbar, this);
16540     },
16541     onBtnClick : function(id)
16542     {
16543        this.editorcore.relayCmd(id);
16544        this.editorcore.focus();
16545     },
16546     
16547     /**
16548      * Protected method that will not generally be called directly. It triggers
16549      * a toolbar update by reading the markup state of the current selection in the editor.
16550      */
16551     updateToolbar: function(){
16552
16553         if(!this.editorcore.activated){
16554             this.editor.onFirstFocus(); // is this neeed?
16555             return;
16556         }
16557
16558         var btns = this.buttons; 
16559         var doc = this.editorcore.doc;
16560         btns.get('bold').setActive(doc.queryCommandState('bold'));
16561         btns.get('italic').setActive(doc.queryCommandState('italic'));
16562         //btns.get('underline').setActive(doc.queryCommandState('underline'));
16563         
16564         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
16565         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
16566         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
16567         
16568         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
16569         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
16570          /*
16571         
16572         var ans = this.editorcore.getAllAncestors();
16573         if (this.formatCombo) {
16574             
16575             
16576             var store = this.formatCombo.store;
16577             this.formatCombo.setValue("");
16578             for (var i =0; i < ans.length;i++) {
16579                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
16580                     // select it..
16581                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
16582                     break;
16583                 }
16584             }
16585         }
16586         
16587         
16588         
16589         // hides menus... - so this cant be on a menu...
16590         Roo.bootstrap.MenuMgr.hideAll();
16591         */
16592         Roo.bootstrap.MenuMgr.hideAll();
16593         //this.editorsyncValue();
16594     },
16595     onFirstFocus: function() {
16596         this.buttons.each(function(item){
16597            item.enable();
16598         });
16599     },
16600     toggleSourceEdit : function(sourceEditMode){
16601         
16602           
16603         if(sourceEditMode){
16604             Roo.log("disabling buttons");
16605            this.buttons.each( function(item){
16606                 if(item.cmd != 'pencil'){
16607                     item.disable();
16608                 }
16609             });
16610           
16611         }else{
16612             Roo.log("enabling buttons");
16613             if(this.editorcore.initialized){
16614                 this.buttons.each( function(item){
16615                     item.enable();
16616                 });
16617             }
16618             
16619         }
16620         Roo.log("calling toggole on editor");
16621         // tell the editor that it's been pressed..
16622         this.editor.toggleSourceEdit(sourceEditMode);
16623        
16624     }
16625 });
16626
16627
16628
16629
16630
16631 /**
16632  * @class Roo.bootstrap.Table.AbstractSelectionModel
16633  * @extends Roo.util.Observable
16634  * Abstract base class for grid SelectionModels.  It provides the interface that should be
16635  * implemented by descendant classes.  This class should not be directly instantiated.
16636  * @constructor
16637  */
16638 Roo.bootstrap.Table.AbstractSelectionModel = function(){
16639     this.locked = false;
16640     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
16641 };
16642
16643
16644 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
16645     /** @ignore Called by the grid automatically. Do not call directly. */
16646     init : function(grid){
16647         this.grid = grid;
16648         this.initEvents();
16649     },
16650
16651     /**
16652      * Locks the selections.
16653      */
16654     lock : function(){
16655         this.locked = true;
16656     },
16657
16658     /**
16659      * Unlocks the selections.
16660      */
16661     unlock : function(){
16662         this.locked = false;
16663     },
16664
16665     /**
16666      * Returns true if the selections are locked.
16667      * @return {Boolean}
16668      */
16669     isLocked : function(){
16670         return this.locked;
16671     }
16672 });
16673 /**
16674  * @class Roo.bootstrap.Table.ColumnModel
16675  * @extends Roo.util.Observable
16676  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
16677  * the columns in the table.
16678  
16679  * @constructor
16680  * @param {Object} config An Array of column config objects. See this class's
16681  * config objects for details.
16682 */
16683 Roo.bootstrap.Table.ColumnModel = function(config){
16684         /**
16685      * The config passed into the constructor
16686      */
16687     this.config = config;
16688     this.lookup = {};
16689
16690     // if no id, create one
16691     // if the column does not have a dataIndex mapping,
16692     // map it to the order it is in the config
16693     for(var i = 0, len = config.length; i < len; i++){
16694         var c = config[i];
16695         if(typeof c.dataIndex == "undefined"){
16696             c.dataIndex = i;
16697         }
16698         if(typeof c.renderer == "string"){
16699             c.renderer = Roo.util.Format[c.renderer];
16700         }
16701         if(typeof c.id == "undefined"){
16702             c.id = Roo.id();
16703         }
16704 //        if(c.editor && c.editor.xtype){
16705 //            c.editor  = Roo.factory(c.editor, Roo.grid);
16706 //        }
16707 //        if(c.editor && c.editor.isFormField){
16708 //            c.editor = new Roo.grid.GridEditor(c.editor);
16709 //        }
16710
16711         this.lookup[c.id] = c;
16712     }
16713
16714     /**
16715      * The width of columns which have no width specified (defaults to 100)
16716      * @type Number
16717      */
16718     this.defaultWidth = 100;
16719
16720     /**
16721      * Default sortable of columns which have no sortable specified (defaults to false)
16722      * @type Boolean
16723      */
16724     this.defaultSortable = false;
16725
16726     this.addEvents({
16727         /**
16728              * @event widthchange
16729              * Fires when the width of a column changes.
16730              * @param {ColumnModel} this
16731              * @param {Number} columnIndex The column index
16732              * @param {Number} newWidth The new width
16733              */
16734             "widthchange": true,
16735         /**
16736              * @event headerchange
16737              * Fires when the text of a header changes.
16738              * @param {ColumnModel} this
16739              * @param {Number} columnIndex The column index
16740              * @param {Number} newText The new header text
16741              */
16742             "headerchange": true,
16743         /**
16744              * @event hiddenchange
16745              * Fires when a column is hidden or "unhidden".
16746              * @param {ColumnModel} this
16747              * @param {Number} columnIndex The column index
16748              * @param {Boolean} hidden true if hidden, false otherwise
16749              */
16750             "hiddenchange": true,
16751             /**
16752          * @event columnmoved
16753          * Fires when a column is moved.
16754          * @param {ColumnModel} this
16755          * @param {Number} oldIndex
16756          * @param {Number} newIndex
16757          */
16758         "columnmoved" : true,
16759         /**
16760          * @event columlockchange
16761          * Fires when a column's locked state is changed
16762          * @param {ColumnModel} this
16763          * @param {Number} colIndex
16764          * @param {Boolean} locked true if locked
16765          */
16766         "columnlockchange" : true
16767     });
16768     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
16769 };
16770 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
16771     /**
16772      * @cfg {String} header The header text to display in the Grid view.
16773      */
16774     /**
16775      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
16776      * {@link Roo.data.Record} definition from which to draw the column's value. If not
16777      * specified, the column's index is used as an index into the Record's data Array.
16778      */
16779     /**
16780      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
16781      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
16782      */
16783     /**
16784      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
16785      * Defaults to the value of the {@link #defaultSortable} property.
16786      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
16787      */
16788     /**
16789      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
16790      */
16791     /**
16792      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
16793      */
16794     /**
16795      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
16796      */
16797     /**
16798      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
16799      */
16800     /**
16801      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
16802      * given the cell's data value. See {@link #setRenderer}. If not specified, the
16803      * default renderer uses the raw data value.
16804      */
16805     /**
16806      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
16807      */
16808
16809     /**
16810      * Returns the id of the column at the specified index.
16811      * @param {Number} index The column index
16812      * @return {String} the id
16813      */
16814     getColumnId : function(index){
16815         return this.config[index].id;
16816     },
16817
16818     /**
16819      * Returns the column for a specified id.
16820      * @param {String} id The column id
16821      * @return {Object} the column
16822      */
16823     getColumnById : function(id){
16824         return this.lookup[id];
16825     },
16826
16827     
16828     /**
16829      * Returns the column for a specified dataIndex.
16830      * @param {String} dataIndex The column dataIndex
16831      * @return {Object|Boolean} the column or false if not found
16832      */
16833     getColumnByDataIndex: function(dataIndex){
16834         var index = this.findColumnIndex(dataIndex);
16835         return index > -1 ? this.config[index] : false;
16836     },
16837     
16838     /**
16839      * Returns the index for a specified column id.
16840      * @param {String} id The column id
16841      * @return {Number} the index, or -1 if not found
16842      */
16843     getIndexById : function(id){
16844         for(var i = 0, len = this.config.length; i < len; i++){
16845             if(this.config[i].id == id){
16846                 return i;
16847             }
16848         }
16849         return -1;
16850     },
16851     
16852     /**
16853      * Returns the index for a specified column dataIndex.
16854      * @param {String} dataIndex The column dataIndex
16855      * @return {Number} the index, or -1 if not found
16856      */
16857     
16858     findColumnIndex : function(dataIndex){
16859         for(var i = 0, len = this.config.length; i < len; i++){
16860             if(this.config[i].dataIndex == dataIndex){
16861                 return i;
16862             }
16863         }
16864         return -1;
16865     },
16866     
16867     
16868     moveColumn : function(oldIndex, newIndex){
16869         var c = this.config[oldIndex];
16870         this.config.splice(oldIndex, 1);
16871         this.config.splice(newIndex, 0, c);
16872         this.dataMap = null;
16873         this.fireEvent("columnmoved", this, oldIndex, newIndex);
16874     },
16875
16876     isLocked : function(colIndex){
16877         return this.config[colIndex].locked === true;
16878     },
16879
16880     setLocked : function(colIndex, value, suppressEvent){
16881         if(this.isLocked(colIndex) == value){
16882             return;
16883         }
16884         this.config[colIndex].locked = value;
16885         if(!suppressEvent){
16886             this.fireEvent("columnlockchange", this, colIndex, value);
16887         }
16888     },
16889
16890     getTotalLockedWidth : function(){
16891         var totalWidth = 0;
16892         for(var i = 0; i < this.config.length; i++){
16893             if(this.isLocked(i) && !this.isHidden(i)){
16894                 this.totalWidth += this.getColumnWidth(i);
16895             }
16896         }
16897         return totalWidth;
16898     },
16899
16900     getLockedCount : function(){
16901         for(var i = 0, len = this.config.length; i < len; i++){
16902             if(!this.isLocked(i)){
16903                 return i;
16904             }
16905         }
16906     },
16907
16908     /**
16909      * Returns the number of columns.
16910      * @return {Number}
16911      */
16912     getColumnCount : function(visibleOnly){
16913         if(visibleOnly === true){
16914             var c = 0;
16915             for(var i = 0, len = this.config.length; i < len; i++){
16916                 if(!this.isHidden(i)){
16917                     c++;
16918                 }
16919             }
16920             return c;
16921         }
16922         return this.config.length;
16923     },
16924
16925     /**
16926      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16927      * @param {Function} fn
16928      * @param {Object} scope (optional)
16929      * @return {Array} result
16930      */
16931     getColumnsBy : function(fn, scope){
16932         var r = [];
16933         for(var i = 0, len = this.config.length; i < len; i++){
16934             var c = this.config[i];
16935             if(fn.call(scope||this, c, i) === true){
16936                 r[r.length] = c;
16937             }
16938         }
16939         return r;
16940     },
16941
16942     /**
16943      * Returns true if the specified column is sortable.
16944      * @param {Number} col The column index
16945      * @return {Boolean}
16946      */
16947     isSortable : function(col){
16948         if(typeof this.config[col].sortable == "undefined"){
16949             return this.defaultSortable;
16950         }
16951         return this.config[col].sortable;
16952     },
16953
16954     /**
16955      * Returns the rendering (formatting) function defined for the column.
16956      * @param {Number} col The column index.
16957      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16958      */
16959     getRenderer : function(col){
16960         if(!this.config[col].renderer){
16961             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
16962         }
16963         return this.config[col].renderer;
16964     },
16965
16966     /**
16967      * Sets the rendering (formatting) function for a column.
16968      * @param {Number} col The column index
16969      * @param {Function} fn The function to use to process the cell's raw data
16970      * to return HTML markup for the grid view. The render function is called with
16971      * the following parameters:<ul>
16972      * <li>Data value.</li>
16973      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16974      * <li>css A CSS style string to apply to the table cell.</li>
16975      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16976      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16977      * <li>Row index</li>
16978      * <li>Column index</li>
16979      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16980      */
16981     setRenderer : function(col, fn){
16982         this.config[col].renderer = fn;
16983     },
16984
16985     /**
16986      * Returns the width for the specified column.
16987      * @param {Number} col The column index
16988      * @return {Number}
16989      */
16990     getColumnWidth : function(col){
16991         return this.config[col].width * 1 || this.defaultWidth;
16992     },
16993
16994     /**
16995      * Sets the width for a column.
16996      * @param {Number} col The column index
16997      * @param {Number} width The new width
16998      */
16999     setColumnWidth : function(col, width, suppressEvent){
17000         this.config[col].width = width;
17001         this.totalWidth = null;
17002         if(!suppressEvent){
17003              this.fireEvent("widthchange", this, col, width);
17004         }
17005     },
17006
17007     /**
17008      * Returns the total width of all columns.
17009      * @param {Boolean} includeHidden True to include hidden column widths
17010      * @return {Number}
17011      */
17012     getTotalWidth : function(includeHidden){
17013         if(!this.totalWidth){
17014             this.totalWidth = 0;
17015             for(var i = 0, len = this.config.length; i < len; i++){
17016                 if(includeHidden || !this.isHidden(i)){
17017                     this.totalWidth += this.getColumnWidth(i);
17018                 }
17019             }
17020         }
17021         return this.totalWidth;
17022     },
17023
17024     /**
17025      * Returns the header for the specified column.
17026      * @param {Number} col The column index
17027      * @return {String}
17028      */
17029     getColumnHeader : function(col){
17030         return this.config[col].header;
17031     },
17032
17033     /**
17034      * Sets the header for a column.
17035      * @param {Number} col The column index
17036      * @param {String} header The new header
17037      */
17038     setColumnHeader : function(col, header){
17039         this.config[col].header = header;
17040         this.fireEvent("headerchange", this, col, header);
17041     },
17042
17043     /**
17044      * Returns the tooltip for the specified column.
17045      * @param {Number} col The column index
17046      * @return {String}
17047      */
17048     getColumnTooltip : function(col){
17049             return this.config[col].tooltip;
17050     },
17051     /**
17052      * Sets the tooltip for a column.
17053      * @param {Number} col The column index
17054      * @param {String} tooltip The new tooltip
17055      */
17056     setColumnTooltip : function(col, tooltip){
17057             this.config[col].tooltip = tooltip;
17058     },
17059
17060     /**
17061      * Returns the dataIndex for the specified column.
17062      * @param {Number} col The column index
17063      * @return {Number}
17064      */
17065     getDataIndex : function(col){
17066         return this.config[col].dataIndex;
17067     },
17068
17069     /**
17070      * Sets the dataIndex for a column.
17071      * @param {Number} col The column index
17072      * @param {Number} dataIndex The new dataIndex
17073      */
17074     setDataIndex : function(col, dataIndex){
17075         this.config[col].dataIndex = dataIndex;
17076     },
17077
17078     
17079     
17080     /**
17081      * Returns true if the cell is editable.
17082      * @param {Number} colIndex The column index
17083      * @param {Number} rowIndex The row index
17084      * @return {Boolean}
17085      */
17086     isCellEditable : function(colIndex, rowIndex){
17087         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
17088     },
17089
17090     /**
17091      * Returns the editor defined for the cell/column.
17092      * return false or null to disable editing.
17093      * @param {Number} colIndex The column index
17094      * @param {Number} rowIndex The row index
17095      * @return {Object}
17096      */
17097     getCellEditor : function(colIndex, rowIndex){
17098         return this.config[colIndex].editor;
17099     },
17100
17101     /**
17102      * Sets if a column is editable.
17103      * @param {Number} col The column index
17104      * @param {Boolean} editable True if the column is editable
17105      */
17106     setEditable : function(col, editable){
17107         this.config[col].editable = editable;
17108     },
17109
17110
17111     /**
17112      * Returns true if the column is hidden.
17113      * @param {Number} colIndex The column index
17114      * @return {Boolean}
17115      */
17116     isHidden : function(colIndex){
17117         return this.config[colIndex].hidden;
17118     },
17119
17120
17121     /**
17122      * Returns true if the column width cannot be changed
17123      */
17124     isFixed : function(colIndex){
17125         return this.config[colIndex].fixed;
17126     },
17127
17128     /**
17129      * Returns true if the column can be resized
17130      * @return {Boolean}
17131      */
17132     isResizable : function(colIndex){
17133         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
17134     },
17135     /**
17136      * Sets if a column is hidden.
17137      * @param {Number} colIndex The column index
17138      * @param {Boolean} hidden True if the column is hidden
17139      */
17140     setHidden : function(colIndex, hidden){
17141         this.config[colIndex].hidden = hidden;
17142         this.totalWidth = null;
17143         this.fireEvent("hiddenchange", this, colIndex, hidden);
17144     },
17145
17146     /**
17147      * Sets the editor for a column.
17148      * @param {Number} col The column index
17149      * @param {Object} editor The editor object
17150      */
17151     setEditor : function(col, editor){
17152         this.config[col].editor = editor;
17153     }
17154 });
17155
17156 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
17157         if(typeof value == "string" && value.length < 1){
17158             return "&#160;";
17159         }
17160         return value;
17161 };
17162
17163 // Alias for backwards compatibility
17164 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
17165
17166 /**
17167  * @extends Roo.bootstrap.Table.AbstractSelectionModel
17168  * @class Roo.bootstrap.Table.RowSelectionModel
17169  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17170  * It supports multiple selections and keyboard selection/navigation. 
17171  * @constructor
17172  * @param {Object} config
17173  */
17174
17175 Roo.bootstrap.Table.RowSelectionModel = function(config){
17176     Roo.apply(this, config);
17177     this.selections = new Roo.util.MixedCollection(false, function(o){
17178         return o.id;
17179     });
17180
17181     this.last = false;
17182     this.lastActive = false;
17183
17184     this.addEvents({
17185         /**
17186              * @event selectionchange
17187              * Fires when the selection changes
17188              * @param {SelectionModel} this
17189              */
17190             "selectionchange" : true,
17191         /**
17192              * @event afterselectionchange
17193              * Fires after the selection changes (eg. by key press or clicking)
17194              * @param {SelectionModel} this
17195              */
17196             "afterselectionchange" : true,
17197         /**
17198              * @event beforerowselect
17199              * Fires when a row is selected being selected, return false to cancel.
17200              * @param {SelectionModel} this
17201              * @param {Number} rowIndex The selected index
17202              * @param {Boolean} keepExisting False if other selections will be cleared
17203              */
17204             "beforerowselect" : true,
17205         /**
17206              * @event rowselect
17207              * Fires when a row is selected.
17208              * @param {SelectionModel} this
17209              * @param {Number} rowIndex The selected index
17210              * @param {Roo.data.Record} r The record
17211              */
17212             "rowselect" : true,
17213         /**
17214              * @event rowdeselect
17215              * Fires when a row is deselected.
17216              * @param {SelectionModel} this
17217              * @param {Number} rowIndex The selected index
17218              */
17219         "rowdeselect" : true
17220     });
17221     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17222     this.locked = false;
17223 };
17224
17225 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
17226     /**
17227      * @cfg {Boolean} singleSelect
17228      * True to allow selection of only one row at a time (defaults to false)
17229      */
17230     singleSelect : false,
17231
17232     // private
17233     initEvents : function(){
17234
17235         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17236             this.grid.on("mousedown", this.handleMouseDown, this);
17237         }else{ // allow click to work like normal
17238             this.grid.on("rowclick", this.handleDragableRowClick, this);
17239         }
17240
17241         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17242             "up" : function(e){
17243                 if(!e.shiftKey){
17244                     this.selectPrevious(e.shiftKey);
17245                 }else if(this.last !== false && this.lastActive !== false){
17246                     var last = this.last;
17247                     this.selectRange(this.last,  this.lastActive-1);
17248                     this.grid.getView().focusRow(this.lastActive);
17249                     if(last !== false){
17250                         this.last = last;
17251                     }
17252                 }else{
17253                     this.selectFirstRow();
17254                 }
17255                 this.fireEvent("afterselectionchange", this);
17256             },
17257             "down" : function(e){
17258                 if(!e.shiftKey){
17259                     this.selectNext(e.shiftKey);
17260                 }else if(this.last !== false && this.lastActive !== false){
17261                     var last = this.last;
17262                     this.selectRange(this.last,  this.lastActive+1);
17263                     this.grid.getView().focusRow(this.lastActive);
17264                     if(last !== false){
17265                         this.last = last;
17266                     }
17267                 }else{
17268                     this.selectFirstRow();
17269                 }
17270                 this.fireEvent("afterselectionchange", this);
17271             },
17272             scope: this
17273         });
17274
17275         var view = this.grid.view;
17276         view.on("refresh", this.onRefresh, this);
17277         view.on("rowupdated", this.onRowUpdated, this);
17278         view.on("rowremoved", this.onRemove, this);
17279     },
17280
17281     // private
17282     onRefresh : function(){
17283         var ds = this.grid.dataSource, i, v = this.grid.view;
17284         var s = this.selections;
17285         s.each(function(r){
17286             if((i = ds.indexOfId(r.id)) != -1){
17287                 v.onRowSelect(i);
17288             }else{
17289                 s.remove(r);
17290             }
17291         });
17292     },
17293
17294     // private
17295     onRemove : function(v, index, r){
17296         this.selections.remove(r);
17297     },
17298
17299     // private
17300     onRowUpdated : function(v, index, r){
17301         if(this.isSelected(r)){
17302             v.onRowSelect(index);
17303         }
17304     },
17305
17306     /**
17307      * Select records.
17308      * @param {Array} records The records to select
17309      * @param {Boolean} keepExisting (optional) True to keep existing selections
17310      */
17311     selectRecords : function(records, keepExisting){
17312         if(!keepExisting){
17313             this.clearSelections();
17314         }
17315         var ds = this.grid.dataSource;
17316         for(var i = 0, len = records.length; i < len; i++){
17317             this.selectRow(ds.indexOf(records[i]), true);
17318         }
17319     },
17320
17321     /**
17322      * Gets the number of selected rows.
17323      * @return {Number}
17324      */
17325     getCount : function(){
17326         return this.selections.length;
17327     },
17328
17329     /**
17330      * Selects the first row in the grid.
17331      */
17332     selectFirstRow : function(){
17333         this.selectRow(0);
17334     },
17335
17336     /**
17337      * Select the last row.
17338      * @param {Boolean} keepExisting (optional) True to keep existing selections
17339      */
17340     selectLastRow : function(keepExisting){
17341         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
17342     },
17343
17344     /**
17345      * Selects the row immediately following the last selected row.
17346      * @param {Boolean} keepExisting (optional) True to keep existing selections
17347      */
17348     selectNext : function(keepExisting){
17349         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
17350             this.selectRow(this.last+1, keepExisting);
17351             this.grid.getView().focusRow(this.last);
17352         }
17353     },
17354
17355     /**
17356      * Selects the row that precedes the last selected row.
17357      * @param {Boolean} keepExisting (optional) True to keep existing selections
17358      */
17359     selectPrevious : function(keepExisting){
17360         if(this.last){
17361             this.selectRow(this.last-1, keepExisting);
17362             this.grid.getView().focusRow(this.last);
17363         }
17364     },
17365
17366     /**
17367      * Returns the selected records
17368      * @return {Array} Array of selected records
17369      */
17370     getSelections : function(){
17371         return [].concat(this.selections.items);
17372     },
17373
17374     /**
17375      * Returns the first selected record.
17376      * @return {Record}
17377      */
17378     getSelected : function(){
17379         return this.selections.itemAt(0);
17380     },
17381
17382
17383     /**
17384      * Clears all selections.
17385      */
17386     clearSelections : function(fast){
17387         if(this.locked) return;
17388         if(fast !== true){
17389             var ds = this.grid.dataSource;
17390             var s = this.selections;
17391             s.each(function(r){
17392                 this.deselectRow(ds.indexOfId(r.id));
17393             }, this);
17394             s.clear();
17395         }else{
17396             this.selections.clear();
17397         }
17398         this.last = false;
17399     },
17400
17401
17402     /**
17403      * Selects all rows.
17404      */
17405     selectAll : function(){
17406         if(this.locked) return;
17407         this.selections.clear();
17408         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
17409             this.selectRow(i, true);
17410         }
17411     },
17412
17413     /**
17414      * Returns True if there is a selection.
17415      * @return {Boolean}
17416      */
17417     hasSelection : function(){
17418         return this.selections.length > 0;
17419     },
17420
17421     /**
17422      * Returns True if the specified row is selected.
17423      * @param {Number/Record} record The record or index of the record to check
17424      * @return {Boolean}
17425      */
17426     isSelected : function(index){
17427         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
17428         return (r && this.selections.key(r.id) ? true : false);
17429     },
17430
17431     /**
17432      * Returns True if the specified record id is selected.
17433      * @param {String} id The id of record to check
17434      * @return {Boolean}
17435      */
17436     isIdSelected : function(id){
17437         return (this.selections.key(id) ? true : false);
17438     },
17439
17440     // private
17441     handleMouseDown : function(e, t){
17442         var view = this.grid.getView(), rowIndex;
17443         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
17444             return;
17445         };
17446         if(e.shiftKey && this.last !== false){
17447             var last = this.last;
17448             this.selectRange(last, rowIndex, e.ctrlKey);
17449             this.last = last; // reset the last
17450             view.focusRow(rowIndex);
17451         }else{
17452             var isSelected = this.isSelected(rowIndex);
17453             if(e.button !== 0 && isSelected){
17454                 view.focusRow(rowIndex);
17455             }else if(e.ctrlKey && isSelected){
17456                 this.deselectRow(rowIndex);
17457             }else if(!isSelected){
17458                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
17459                 view.focusRow(rowIndex);
17460             }
17461         }
17462         this.fireEvent("afterselectionchange", this);
17463     },
17464     // private
17465     handleDragableRowClick :  function(grid, rowIndex, e) 
17466     {
17467         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
17468             this.selectRow(rowIndex, false);
17469             grid.view.focusRow(rowIndex);
17470              this.fireEvent("afterselectionchange", this);
17471         }
17472     },
17473     
17474     /**
17475      * Selects multiple rows.
17476      * @param {Array} rows Array of the indexes of the row to select
17477      * @param {Boolean} keepExisting (optional) True to keep existing selections
17478      */
17479     selectRows : function(rows, keepExisting){
17480         if(!keepExisting){
17481             this.clearSelections();
17482         }
17483         for(var i = 0, len = rows.length; i < len; i++){
17484             this.selectRow(rows[i], true);
17485         }
17486     },
17487
17488     /**
17489      * Selects a range of rows. All rows in between startRow and endRow are also selected.
17490      * @param {Number} startRow The index of the first row in the range
17491      * @param {Number} endRow The index of the last row in the range
17492      * @param {Boolean} keepExisting (optional) True to retain existing selections
17493      */
17494     selectRange : function(startRow, endRow, keepExisting){
17495         if(this.locked) return;
17496         if(!keepExisting){
17497             this.clearSelections();
17498         }
17499         if(startRow <= endRow){
17500             for(var i = startRow; i <= endRow; i++){
17501                 this.selectRow(i, true);
17502             }
17503         }else{
17504             for(var i = startRow; i >= endRow; i--){
17505                 this.selectRow(i, true);
17506             }
17507         }
17508     },
17509
17510     /**
17511      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
17512      * @param {Number} startRow The index of the first row in the range
17513      * @param {Number} endRow The index of the last row in the range
17514      */
17515     deselectRange : function(startRow, endRow, preventViewNotify){
17516         if(this.locked) return;
17517         for(var i = startRow; i <= endRow; i++){
17518             this.deselectRow(i, preventViewNotify);
17519         }
17520     },
17521
17522     /**
17523      * Selects a row.
17524      * @param {Number} row The index of the row to select
17525      * @param {Boolean} keepExisting (optional) True to keep existing selections
17526      */
17527     selectRow : function(index, keepExisting, preventViewNotify){
17528         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17529         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17530             if(!keepExisting || this.singleSelect){
17531                 this.clearSelections();
17532             }
17533             var r = this.grid.dataSource.getAt(index);
17534             this.selections.add(r);
17535             this.last = this.lastActive = index;
17536             if(!preventViewNotify){
17537                 this.grid.getView().onRowSelect(index);
17538             }
17539             this.fireEvent("rowselect", this, index, r);
17540             this.fireEvent("selectionchange", this);
17541         }
17542     },
17543
17544     /**
17545      * Deselects a row.
17546      * @param {Number} row The index of the row to deselect
17547      */
17548     deselectRow : function(index, preventViewNotify){
17549         if(this.locked) return;
17550         if(this.last == index){
17551             this.last = false;
17552         }
17553         if(this.lastActive == index){
17554             this.lastActive = false;
17555         }
17556         var r = this.grid.dataSource.getAt(index);
17557         this.selections.remove(r);
17558         if(!preventViewNotify){
17559             this.grid.getView().onRowDeselect(index);
17560         }
17561         this.fireEvent("rowdeselect", this, index);
17562         this.fireEvent("selectionchange", this);
17563     },
17564
17565     // private
17566     restoreLast : function(){
17567         if(this._last){
17568             this.last = this._last;
17569         }
17570     },
17571
17572     // private
17573     acceptsNav : function(row, col, cm){
17574         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17575     },
17576
17577     // private
17578     onEditorKey : function(field, e){
17579         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17580         if(k == e.TAB){
17581             e.stopEvent();
17582             ed.completeEdit();
17583             if(e.shiftKey){
17584                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17585             }else{
17586                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17587             }
17588         }else if(k == e.ENTER && !e.ctrlKey){
17589             e.stopEvent();
17590             ed.completeEdit();
17591             if(e.shiftKey){
17592                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17593             }else{
17594                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17595             }
17596         }else if(k == e.ESC){
17597             ed.cancelEdit();
17598         }
17599         if(newCell){
17600             g.startEditing(newCell[0], newCell[1]);
17601         }
17602     }
17603 });/*
17604  * - LGPL
17605  *
17606  * element
17607  * 
17608  */
17609
17610 /**
17611  * @class Roo.bootstrap.MessageBar
17612  * @extends Roo.bootstrap.Component
17613  * Bootstrap MessageBar class
17614  * @cfg {String} html contents of the MessageBar
17615  * @cfg {String} weight (info | success | warning | danger) default info
17616  * @cfg {String} beforeClass insert the bar before the given class
17617  * @cfg {Boolean} closable (true | false) default false
17618  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
17619  * 
17620  * @constructor
17621  * Create a new Element
17622  * @param {Object} config The config object
17623  */
17624
17625 Roo.bootstrap.MessageBar = function(config){
17626     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
17627 };
17628
17629 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
17630     
17631     html: '',
17632     weight: 'info',
17633     closable: false,
17634     fixed: false,
17635     beforeClass: 'bootstrap-sticky-wrap',
17636     
17637     getAutoCreate : function(){
17638         
17639         var cfg = {
17640             tag: 'div',
17641             cls: 'alert alert-dismissable alert-' + this.weight,
17642             cn: [
17643                 {
17644                     tag: 'span',
17645                     cls: 'message',
17646                     html: this.html || ''
17647                 }
17648             ]
17649         }
17650         
17651         if(this.fixed){
17652             cfg.cls += ' alert-messages-fixed';
17653         }
17654         
17655         if(this.closable){
17656             cfg.cn.push({
17657                 tag: 'button',
17658                 cls: 'close',
17659                 html: 'x'
17660             });
17661         }
17662         
17663         return cfg;
17664     },
17665     
17666     onRender : function(ct, position)
17667     {
17668         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17669         
17670         if(!this.el){
17671             var cfg = Roo.apply({},  this.getAutoCreate());
17672             cfg.id = Roo.id();
17673             
17674             if (this.cls) {
17675                 cfg.cls += ' ' + this.cls;
17676             }
17677             if (this.style) {
17678                 cfg.style = this.style;
17679             }
17680             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
17681             
17682             this.el.setVisibilityMode(Roo.Element.DISPLAY);
17683         }
17684         
17685         this.el.select('>button.close').on('click', this.hide, this);
17686         
17687     },
17688     
17689     show : function()
17690     {
17691         if (!this.rendered) {
17692             this.render();
17693         }
17694         
17695         this.el.show();
17696         
17697         this.fireEvent('show', this);
17698         
17699     },
17700     
17701     hide : function()
17702     {
17703         if (!this.rendered) {
17704             this.render();
17705         }
17706         
17707         this.el.hide();
17708         
17709         this.fireEvent('hide', this);
17710     },
17711     
17712     update : function()
17713     {
17714 //        var e = this.el.dom.firstChild;
17715 //        
17716 //        if(this.closable){
17717 //            e = e.nextSibling;
17718 //        }
17719 //        
17720 //        e.data = this.html || '';
17721
17722         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
17723     }
17724    
17725 });
17726
17727  
17728
17729