roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr](true));
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
775  * @cfg {Number} md colspan out of 12 for computer-sized screens
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
777  * @cfg {String} html content of column.
778  * 
779  * @constructor
780  * Create a new Column
781  * @param {Object} config The config object
782  */
783
784 Roo.bootstrap.Column = function(config){
785     Roo.bootstrap.Column.superclass.constructor.call(this, config);
786 };
787
788 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
789     
790     xs: null,
791     sm: null,
792     md: null,
793     lg: null,
794     html: '',
795     offset: 0,
796     
797     getAutoCreate : function(){
798         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
799         
800         cfg = {
801             tag: 'div',
802             cls: 'column'
803         };
804         
805         var settings=this;
806         ['xs','sm','md','lg'].map(function(size){
807             if (settings[size]) {
808                 cfg.cls += ' col-' + size + '-' + settings[size];
809             }
810         });
811         if (this.html.length) {
812             cfg.html = this.html;
813         }
814         
815         return cfg;
816     }
817    
818 });
819
820  
821
822  /*
823  * - LGPL
824  *
825  * page container.
826  * 
827  */
828
829
830 /**
831  * @class Roo.bootstrap.Container
832  * @extends Roo.bootstrap.Component
833  * Bootstrap Container class
834  * @cfg {Boolean} jumbotron is it a jumbotron element
835  * @cfg {String} html content of element
836  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
837  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
838  * @cfg {String} header content of header (for panel)
839  * @cfg {String} footer content of footer (for panel)
840  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
841  * @cfg {String} tag (header|aside|section) type of HTML tag.
842
843  *     
844  * @constructor
845  * Create a new Container
846  * @param {Object} config The config object
847  */
848
849 Roo.bootstrap.Container = function(config){
850     Roo.bootstrap.Container.superclass.constructor.call(this, config);
851 };
852
853 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
854     
855     jumbotron : false,
856     well: '',
857     panel : '',
858     header: '',
859     footer : '',
860     sticky: '',
861     tag : false,
862   
863      
864     getChildContainer : function() {
865         
866         if(!this.el){
867             return false;
868         }
869         
870         if (this.panel.length) {
871             return this.el.select('.panel-body',true).first();
872         }
873         
874         return this.el;
875     },
876     
877     
878     getAutoCreate : function(){
879         
880         var cfg = {
881             tag : this.tag || 'div',
882             html : '',
883             cls : ''
884         };
885         if (this.jumbotron) {
886             cfg.cls = 'jumbotron';
887         }
888         // - this is applied by the parent..
889         //if (this.cls) {
890         //    cfg.cls = this.cls + '';
891         //}
892         
893         if (this.sticky.length) {
894             
895             var bd = Roo.get(document.body);
896             if (!bd.hasClass('bootstrap-sticky')) {
897                 bd.addClass('bootstrap-sticky');
898                 Roo.select('html',true).setStyle('height', '100%');
899             }
900              
901             cfg.cls += 'bootstrap-sticky-' + this.sticky;
902         }
903         
904         
905         if (this.well.length) {
906             switch (this.well) {
907                 case 'lg':
908                 case 'sm':
909                     cfg.cls +=' well well-' +this.well;
910                     break;
911                 default:
912                     cfg.cls +=' well';
913                     break;
914             }
915         }
916         
917         var body = cfg;
918         
919         if (this.panel.length) {
920             cfg.cls += ' panel panel-' + this.panel;
921             cfg.cn = [];
922             if (this.header.length) {
923                 cfg.cn.push({
924                     
925                     cls : 'panel-heading',
926                     cn : [{
927                         tag: 'h3',
928                         cls : 'panel-title',
929                         html : this.header
930                     }]
931                     
932                 });
933             }
934             body = false;
935             cfg.cn.push({
936                 cls : 'panel-body',
937                 html : this.html
938             });
939             
940             
941             if (this.footer.length) {
942                 cfg.cn.push({
943                     cls : 'panel-footer',
944                     html : this.footer
945                     
946                 });
947             }
948             
949         }
950         
951         if (body) {
952             body.html = this.html || cfg.html;
953         }
954         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
955             cfg.cls =  'container';
956         }
957         
958         return cfg;
959     },
960     
961     titleEl : function()
962     {
963         if(!this.el || !this.panel.length || !this.header.length){
964             return;
965         }
966         
967         return this.el.select('.panel-title',true).first();
968     },
969     
970     setTitle : function(v)
971     {
972         var titleEl = this.titleEl();
973         
974         if(!titleEl){
975             return;
976         }
977         
978         titleEl.dom.innerHTML = v;
979     },
980     
981     getTitle : function()
982     {
983         
984         var titleEl = this.titleEl();
985         
986         if(!titleEl){
987             return '';
988         }
989         
990         return titleEl.dom.innerHTML;
991     }
992    
993 });
994
995  /*
996  * - LGPL
997  *
998  * image
999  * 
1000  */
1001
1002
1003 /**
1004  * @class Roo.bootstrap.Img
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Img class
1007  * @cfg {Boolean} imgResponsive false | true
1008  * @cfg {String} border rounded | circle | thumbnail
1009  * @cfg {String} src image source
1010  * @cfg {String} alt image alternative text
1011  * @cfg {String} href a tag href
1012  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1013  * 
1014  * @constructor
1015  * Create a new Input
1016  * @param {Object} config The config object
1017  */
1018
1019 Roo.bootstrap.Img = function(config){
1020     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1021     
1022     this.addEvents({
1023         // img events
1024         /**
1025          * @event click
1026          * The img click event for the img.
1027          * @param {Roo.EventObject} e
1028          */
1029         "click" : true
1030     });
1031 };
1032
1033 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1034     
1035     imgResponsive: true,
1036     border: '',
1037     src: '',
1038     href: false,
1039     target: false,
1040
1041     getAutoCreate : function(){
1042         
1043         var cfg = {
1044             tag: 'img',
1045             cls: (this.imgResponsive) ? 'img-responsive' : '',
1046             html : null
1047         }
1048         
1049         cfg.html = this.html || cfg.html;
1050         
1051         cfg.src = this.src || cfg.src;
1052         
1053         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1054             cfg.cls += ' img-' + this.border;
1055         }
1056         
1057         if(this.alt){
1058             cfg.alt = this.alt;
1059         }
1060         
1061         if(this.href){
1062             var a = {
1063                 tag: 'a',
1064                 href: this.href,
1065                 cn: [
1066                     cfg
1067                 ]
1068             }
1069             
1070             if(this.target){
1071                 a.target = this.target;
1072             }
1073             
1074         }
1075         
1076         
1077         return (this.href) ? a : cfg;
1078     },
1079     
1080     initEvents: function() {
1081         
1082         if(!this.href){
1083             this.el.on('click', this.onClick, this);
1084         }
1085     },
1086     
1087     onClick : function(e)
1088     {
1089         Roo.log('img onclick');
1090         this.fireEvent('click', this, e);
1091     }
1092    
1093 });
1094
1095  /*
1096  * - LGPL
1097  *
1098  * image
1099  * 
1100  */
1101
1102
1103 /**
1104  * @class Roo.bootstrap.Link
1105  * @extends Roo.bootstrap.Component
1106  * Bootstrap Link Class
1107  * @cfg {String} alt image alternative text
1108  * @cfg {String} href a tag href
1109  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1110  * @cfg {String} html the content of the link.
1111  * @cfg {Boolean} preventDefault (true | false) default false
1112
1113  * 
1114  * @constructor
1115  * Create a new Input
1116  * @param {Object} config The config object
1117  */
1118
1119 Roo.bootstrap.Link = function(config){
1120     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1121     
1122     this.addEvents({
1123         // img events
1124         /**
1125          * @event click
1126          * The img click event for the img.
1127          * @param {Roo.EventObject} e
1128          */
1129         "click" : true
1130     });
1131 };
1132
1133 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1134     
1135     href: false,
1136     target: false,
1137     preventDefault: false,
1138
1139     getAutoCreate : function(){
1140         
1141         var cfg = {
1142             tag: 'a',
1143             html : this.html || 'html-missing'
1144         }
1145         
1146         
1147         if(this.alt){
1148             cfg.alt = this.alt;
1149         }
1150         cfg.href = this.href || '#';
1151         if(this.target){
1152             cfg.target = this.target;
1153         }
1154         
1155         return cfg;
1156     },
1157     
1158     initEvents: function() {
1159         
1160         if(!this.href || this.preventDefault){
1161             this.el.on('click', this.onClick, this);
1162         }
1163     },
1164     
1165     onClick : function(e)
1166     {
1167         if(this.preventDefault){
1168             e.preventDefault();
1169         }
1170         //Roo.log('img onclick');
1171         this.fireEvent('click', this, e);
1172     }
1173    
1174 });
1175
1176  /*
1177  * - LGPL
1178  *
1179  * header
1180  * 
1181  */
1182
1183 /**
1184  * @class Roo.bootstrap.Header
1185  * @extends Roo.bootstrap.Component
1186  * Bootstrap Header class
1187  * @cfg {String} html content of header
1188  * @cfg {Number} level (1|2|3|4|5|6) default 1
1189  * 
1190  * @constructor
1191  * Create a new Header
1192  * @param {Object} config The config object
1193  */
1194
1195
1196 Roo.bootstrap.Header  = function(config){
1197     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1198 };
1199
1200 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1201     
1202     //href : false,
1203     html : false,
1204     level : 1,
1205     
1206     
1207     
1208     getAutoCreate : function(){
1209         
1210         var cfg = {
1211             tag: 'h' + (1 *this.level),
1212             html: this.html || 'fill in html'
1213         } ;
1214         
1215         return cfg;
1216     }
1217    
1218 });
1219
1220  
1221
1222  /*
1223  * Based on:
1224  * Ext JS Library 1.1.1
1225  * Copyright(c) 2006-2007, Ext JS, LLC.
1226  *
1227  * Originally Released Under LGPL - original licence link has changed is not relivant.
1228  *
1229  * Fork - LGPL
1230  * <script type="text/javascript">
1231  */
1232  
1233 /**
1234  * @class Roo.bootstrap.MenuMgr
1235  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1236  * @singleton
1237  */
1238 Roo.bootstrap.MenuMgr = function(){
1239    var menus, active, groups = {}, attached = false, lastShow = new Date();
1240
1241    // private - called when first menu is created
1242    function init(){
1243        menus = {};
1244        active = new Roo.util.MixedCollection();
1245        Roo.get(document).addKeyListener(27, function(){
1246            if(active.length > 0){
1247                hideAll();
1248            }
1249        });
1250    }
1251
1252    // private
1253    function hideAll(){
1254        if(active && active.length > 0){
1255            var c = active.clone();
1256            c.each(function(m){
1257                m.hide();
1258            });
1259        }
1260    }
1261
1262    // private
1263    function onHide(m){
1264        active.remove(m);
1265        if(active.length < 1){
1266            Roo.get(document).un("mouseup", onMouseDown);
1267             
1268            attached = false;
1269        }
1270    }
1271
1272    // private
1273    function onShow(m){
1274        var last = active.last();
1275        lastShow = new Date();
1276        active.add(m);
1277        if(!attached){
1278           Roo.get(document).on("mouseup", onMouseDown);
1279            
1280            attached = true;
1281        }
1282        if(m.parentMenu){
1283           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1284           m.parentMenu.activeChild = m;
1285        }else if(last && last.isVisible()){
1286           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1287        }
1288    }
1289
1290    // private
1291    function onBeforeHide(m){
1292        if(m.activeChild){
1293            m.activeChild.hide();
1294        }
1295        if(m.autoHideTimer){
1296            clearTimeout(m.autoHideTimer);
1297            delete m.autoHideTimer;
1298        }
1299    }
1300
1301    // private
1302    function onBeforeShow(m){
1303        var pm = m.parentMenu;
1304        if(!pm && !m.allowOtherMenus){
1305            hideAll();
1306        }else if(pm && pm.activeChild && active != m){
1307            pm.activeChild.hide();
1308        }
1309    }
1310
1311    // private
1312    function onMouseDown(e){
1313         Roo.log("on MouseDown");
1314         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1315            hideAll();
1316         }
1317         
1318         
1319    }
1320
1321    // private
1322    function onBeforeCheck(mi, state){
1323        if(state){
1324            var g = groups[mi.group];
1325            for(var i = 0, l = g.length; i < l; i++){
1326                if(g[i] != mi){
1327                    g[i].setChecked(false);
1328                }
1329            }
1330        }
1331    }
1332
1333    return {
1334
1335        /**
1336         * Hides all menus that are currently visible
1337         */
1338        hideAll : function(){
1339             hideAll();  
1340        },
1341
1342        // private
1343        register : function(menu){
1344            if(!menus){
1345                init();
1346            }
1347            menus[menu.id] = menu;
1348            menu.on("beforehide", onBeforeHide);
1349            menu.on("hide", onHide);
1350            menu.on("beforeshow", onBeforeShow);
1351            menu.on("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                if(!groups[g]){
1355                    groups[g] = [];
1356                }
1357                groups[g].push(menu);
1358                menu.on("checkchange", onCheck);
1359            }
1360        },
1361
1362         /**
1363          * Returns a {@link Roo.menu.Menu} object
1364          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1365          * be used to generate and return a new Menu instance.
1366          */
1367        get : function(menu){
1368            if(typeof menu == "string"){ // menu id
1369                return menus[menu];
1370            }else if(menu.events){  // menu instance
1371                return menu;
1372            }
1373            /*else if(typeof menu.length == 'number'){ // array of menu items?
1374                return new Roo.bootstrap.Menu({items:menu});
1375            }else{ // otherwise, must be a config
1376                return new Roo.bootstrap.Menu(menu);
1377            }
1378            */
1379            return false;
1380        },
1381
1382        // private
1383        unregister : function(menu){
1384            delete menus[menu.id];
1385            menu.un("beforehide", onBeforeHide);
1386            menu.un("hide", onHide);
1387            menu.un("beforeshow", onBeforeShow);
1388            menu.un("show", onShow);
1389            var g = menu.group;
1390            if(g && menu.events["checkchange"]){
1391                groups[g].remove(menu);
1392                menu.un("checkchange", onCheck);
1393            }
1394        },
1395
1396        // private
1397        registerCheckable : function(menuItem){
1398            var g = menuItem.group;
1399            if(g){
1400                if(!groups[g]){
1401                    groups[g] = [];
1402                }
1403                groups[g].push(menuItem);
1404                menuItem.on("beforecheckchange", onBeforeCheck);
1405            }
1406        },
1407
1408        // private
1409        unregisterCheckable : function(menuItem){
1410            var g = menuItem.group;
1411            if(g){
1412                groups[g].remove(menuItem);
1413                menuItem.un("beforecheckchange", onBeforeCheck);
1414            }
1415        }
1416    };
1417 }();/*
1418  * - LGPL
1419  *
1420  * menu
1421  * 
1422  */
1423
1424 /**
1425  * @class Roo.bootstrap.Menu
1426  * @extends Roo.bootstrap.Component
1427  * Bootstrap Menu class - container for MenuItems
1428  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1429  * 
1430  * @constructor
1431  * Create a new Menu
1432  * @param {Object} config The config object
1433  */
1434
1435
1436 Roo.bootstrap.Menu = function(config){
1437     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1438     if (this.registerMenu) {
1439         Roo.bootstrap.MenuMgr.register(this);
1440     }
1441     this.addEvents({
1442         /**
1443          * @event beforeshow
1444          * Fires before this menu is displayed
1445          * @param {Roo.menu.Menu} this
1446          */
1447         beforeshow : true,
1448         /**
1449          * @event beforehide
1450          * Fires before this menu is hidden
1451          * @param {Roo.menu.Menu} this
1452          */
1453         beforehide : true,
1454         /**
1455          * @event show
1456          * Fires after this menu is displayed
1457          * @param {Roo.menu.Menu} this
1458          */
1459         show : true,
1460         /**
1461          * @event hide
1462          * Fires after this menu is hidden
1463          * @param {Roo.menu.Menu} this
1464          */
1465         hide : true,
1466         /**
1467          * @event click
1468          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1469          * @param {Roo.menu.Menu} this
1470          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1471          * @param {Roo.EventObject} e
1472          */
1473         click : true,
1474         /**
1475          * @event mouseover
1476          * Fires when the mouse is hovering over this menu
1477          * @param {Roo.menu.Menu} this
1478          * @param {Roo.EventObject} e
1479          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1480          */
1481         mouseover : true,
1482         /**
1483          * @event mouseout
1484          * Fires when the mouse exits this menu
1485          * @param {Roo.menu.Menu} this
1486          * @param {Roo.EventObject} e
1487          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1488          */
1489         mouseout : true,
1490         /**
1491          * @event itemclick
1492          * Fires when a menu item contained in this menu is clicked
1493          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1494          * @param {Roo.EventObject} e
1495          */
1496         itemclick: true
1497     });
1498     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1499 };
1500
1501 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1502     
1503    /// html : false,
1504     //align : '',
1505     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1506     type: false,
1507     /**
1508      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1509      */
1510     registerMenu : true,
1511     
1512     menuItems :false, // stores the menu items..
1513     
1514     hidden:true,
1515     
1516     parentMenu : false,
1517     
1518     getChildContainer : function() {
1519         return this.el;  
1520     },
1521     
1522     getAutoCreate : function(){
1523          
1524         //if (['right'].indexOf(this.align)!==-1) {
1525         //    cfg.cn[1].cls += ' pull-right'
1526         //}
1527         
1528         
1529         var cfg = {
1530             tag : 'ul',
1531             cls : 'dropdown-menu' ,
1532             style : 'z-index:1000'
1533             
1534         }
1535         
1536         if (this.type === 'submenu') {
1537             cfg.cls = 'submenu active';
1538         }
1539         if (this.type === 'treeview') {
1540             cfg.cls = 'treeview-menu';
1541         }
1542         
1543         return cfg;
1544     },
1545     initEvents : function() {
1546         
1547        // Roo.log("ADD event");
1548        // Roo.log(this.triggerEl.dom);
1549         this.triggerEl.on('click', this.onTriggerPress, this);
1550         this.triggerEl.addClass('dropdown-toggle');
1551         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1552
1553         this.el.on("mouseover", this.onMouseOver, this);
1554         this.el.on("mouseout", this.onMouseOut, this);
1555         
1556         
1557     },
1558     findTargetItem : function(e){
1559         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1560         if(!t){
1561             return false;
1562         }
1563         //Roo.log(t);         Roo.log(t.id);
1564         if(t && t.id){
1565             //Roo.log(this.menuitems);
1566             return this.menuitems.get(t.id);
1567             
1568             //return this.items.get(t.menuItemId);
1569         }
1570         
1571         return false;
1572     },
1573     onClick : function(e){
1574         Roo.log("menu.onClick");
1575         var t = this.findTargetItem(e);
1576         if(!t){
1577             return;
1578         }
1579         Roo.log(e);
1580         /*
1581         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1582             if(t == this.activeItem && t.shouldDeactivate(e)){
1583                 this.activeItem.deactivate();
1584                 delete this.activeItem;
1585                 return;
1586             }
1587             if(t.canActivate){
1588                 this.setActiveItem(t, true);
1589             }
1590             return;
1591             
1592             
1593         }
1594         */
1595         Roo.log('pass click event');
1596         
1597         t.onClick(e);
1598         
1599         this.fireEvent("click", this, t, e);
1600         
1601         this.hide();
1602     },
1603      onMouseOver : function(e){
1604         var t  = this.findTargetItem(e);
1605         //Roo.log(t);
1606         //if(t){
1607         //    if(t.canActivate && !t.disabled){
1608         //        this.setActiveItem(t, true);
1609         //    }
1610         //}
1611         
1612         this.fireEvent("mouseover", this, e, t);
1613     },
1614     isVisible : function(){
1615         return !this.hidden;
1616     },
1617      onMouseOut : function(e){
1618         var t  = this.findTargetItem(e);
1619         
1620         //if(t ){
1621         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1622         //        this.activeItem.deactivate();
1623         //        delete this.activeItem;
1624         //    }
1625         //}
1626         this.fireEvent("mouseout", this, e, t);
1627     },
1628     
1629     
1630     /**
1631      * Displays this menu relative to another element
1632      * @param {String/HTMLElement/Roo.Element} element The element to align to
1633      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1634      * the element (defaults to this.defaultAlign)
1635      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1636      */
1637     show : function(el, pos, parentMenu){
1638         this.parentMenu = parentMenu;
1639         if(!this.el){
1640             this.render();
1641         }
1642         this.fireEvent("beforeshow", this);
1643         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1644     },
1645      /**
1646      * Displays this menu at a specific xy position
1647      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1648      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1649      */
1650     showAt : function(xy, parentMenu, /* private: */_e){
1651         this.parentMenu = parentMenu;
1652         if(!this.el){
1653             this.render();
1654         }
1655         if(_e !== false){
1656             this.fireEvent("beforeshow", this);
1657             
1658             //xy = this.el.adjustForConstraints(xy);
1659         }
1660         //this.el.setXY(xy);
1661         //this.el.show();
1662         this.hideMenuItems();
1663         this.hidden = false;
1664         this.triggerEl.addClass('open');
1665         this.focus();
1666         this.fireEvent("show", this);
1667     },
1668     
1669     focus : function(){
1670         return;
1671         if(!this.hidden){
1672             this.doFocus.defer(50, this);
1673         }
1674     },
1675
1676     doFocus : function(){
1677         if(!this.hidden){
1678             this.focusEl.focus();
1679         }
1680     },
1681
1682     /**
1683      * Hides this menu and optionally all parent menus
1684      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1685      */
1686     hide : function(deep){
1687         
1688         this.hideMenuItems();
1689         if(this.el && this.isVisible()){
1690             this.fireEvent("beforehide", this);
1691             if(this.activeItem){
1692                 this.activeItem.deactivate();
1693                 this.activeItem = null;
1694             }
1695             this.triggerEl.removeClass('open');;
1696             this.hidden = true;
1697             this.fireEvent("hide", this);
1698         }
1699         if(deep === true && this.parentMenu){
1700             this.parentMenu.hide(true);
1701         }
1702     },
1703     
1704     onTriggerPress  : function(e)
1705     {
1706         
1707         Roo.log('trigger press');
1708         //Roo.log(e.getTarget());
1709        // Roo.log(this.triggerEl.dom);
1710         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1711             return;
1712         }
1713         if (this.isVisible()) {
1714             Roo.log('hide');
1715             this.hide();
1716         } else {
1717             this.show(this.triggerEl, false, false);
1718         }
1719         
1720         
1721     },
1722     
1723          
1724        
1725     
1726     hideMenuItems : function()
1727     {
1728         //$(backdrop).remove()
1729         Roo.select('.open',true).each(function(aa) {
1730             
1731             aa.removeClass('open');
1732           //var parent = getParent($(this))
1733           //var relatedTarget = { relatedTarget: this }
1734           
1735            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1736           //if (e.isDefaultPrevented()) return
1737            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1738         })
1739     },
1740     addxtypeChild : function (tree, cntr) {
1741         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1742           
1743         this.menuitems.add(comp);
1744         return comp;
1745
1746     },
1747     getEl : function()
1748     {
1749         Roo.log(this.el);
1750         return this.el;
1751     }
1752 });
1753
1754  
1755  /*
1756  * - LGPL
1757  *
1758  * menu item
1759  * 
1760  */
1761
1762
1763 /**
1764  * @class Roo.bootstrap.MenuItem
1765  * @extends Roo.bootstrap.Component
1766  * Bootstrap MenuItem class
1767  * @cfg {String} html the menu label
1768  * @cfg {String} href the link
1769  * @cfg {Boolean} preventDefault (true | false) default true
1770  * 
1771  * 
1772  * @constructor
1773  * Create a new MenuItem
1774  * @param {Object} config The config object
1775  */
1776
1777
1778 Roo.bootstrap.MenuItem = function(config){
1779     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1780     this.addEvents({
1781         // raw events
1782         /**
1783          * @event click
1784          * The raw click event for the entire grid.
1785          * @param {Roo.EventObject} e
1786          */
1787         "click" : true
1788     });
1789 };
1790
1791 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1792     
1793     href : false,
1794     html : false,
1795     preventDefault: true,
1796     
1797     getAutoCreate : function(){
1798         var cfg= {
1799             tag: 'li',
1800             cls: 'dropdown-menu-item',
1801             cn: [
1802                     {
1803                         tag : 'a',
1804                         href : '#',
1805                         html : 'Link'
1806                     }
1807                 ]
1808         };
1809         if (this.parent().type == 'treeview') {
1810             cfg.cls = 'treeview-menu';
1811         }
1812         
1813         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1814         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1815         return cfg;
1816     },
1817     
1818     initEvents: function() {
1819         
1820         //this.el.select('a').on('click', this.onClick, this);
1821         
1822     },
1823     onClick : function(e)
1824     {
1825         Roo.log('item on click ');
1826         //if(this.preventDefault){
1827         //    e.preventDefault();
1828         //}
1829         //this.parent().hideMenuItems();
1830         
1831         this.fireEvent('click', this, e);
1832     },
1833     getEl : function()
1834     {
1835         return this.el;
1836     }
1837 });
1838
1839  
1840
1841  /*
1842  * - LGPL
1843  *
1844  * menu separator
1845  * 
1846  */
1847
1848
1849 /**
1850  * @class Roo.bootstrap.MenuSeparator
1851  * @extends Roo.bootstrap.Component
1852  * Bootstrap MenuSeparator class
1853  * 
1854  * @constructor
1855  * Create a new MenuItem
1856  * @param {Object} config The config object
1857  */
1858
1859
1860 Roo.bootstrap.MenuSeparator = function(config){
1861     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1862 };
1863
1864 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1865     
1866     getAutoCreate : function(){
1867         var cfg = {
1868             cls: 'divider',
1869             tag : 'li'
1870         };
1871         
1872         return cfg;
1873     }
1874    
1875 });
1876
1877  
1878
1879  
1880 /*
1881 <div class="modal fade">
1882   <div class="modal-dialog">
1883     <div class="modal-content">
1884       <div class="modal-header">
1885         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1886         <h4 class="modal-title">Modal title</h4>
1887       </div>
1888       <div class="modal-body">
1889         <p>One fine body&hellip;</p>
1890       </div>
1891       <div class="modal-footer">
1892         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1893         <button type="button" class="btn btn-primary">Save changes</button>
1894       </div>
1895     </div><!-- /.modal-content -->
1896   </div><!-- /.modal-dialog -->
1897 </div><!-- /.modal -->
1898 */
1899 /*
1900  * - LGPL
1901  *
1902  * page contgainer.
1903  * 
1904  */
1905
1906 /**
1907  * @class Roo.bootstrap.Modal
1908  * @extends Roo.bootstrap.Component
1909  * Bootstrap Modal class
1910  * @cfg {String} title Title of dialog
1911  * @cfg {Boolean} specificTitle (true|false) default false
1912  * @cfg {Array} buttons Array of buttons or standard button set..
1913  * @cfg {String} buttonPosition (left|right|center) default right
1914  * 
1915  * @constructor
1916  * Create a new Modal Dialog
1917  * @param {Object} config The config object
1918  */
1919
1920 Roo.bootstrap.Modal = function(config){
1921     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1922     this.addEvents({
1923         // raw events
1924         /**
1925          * @event btnclick
1926          * The raw btnclick event for the button
1927          * @param {Roo.EventObject} e
1928          */
1929         "btnclick" : true
1930     });
1931     this.buttons = this.buttons || [];
1932 };
1933
1934 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1935     
1936     title : 'test dialog',
1937    
1938     buttons : false,
1939     
1940     // set on load...
1941     body:  false,
1942     
1943     specificTitle: false,
1944     
1945     buttonPosition: 'right',
1946     
1947     onRender : function(ct, position)
1948     {
1949         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1950      
1951         if(!this.el){
1952             var cfg = Roo.apply({},  this.getAutoCreate());
1953             cfg.id = Roo.id();
1954             //if(!cfg.name){
1955             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1956             //}
1957             //if (!cfg.name.length) {
1958             //    delete cfg.name;
1959            // }
1960             if (this.cls) {
1961                 cfg.cls += ' ' + this.cls;
1962             }
1963             if (this.style) {
1964                 cfg.style = this.style;
1965             }
1966             this.el = Roo.get(document.body).createChild(cfg, position);
1967         }
1968         //var type = this.el.dom.type;
1969         
1970         if(this.tabIndex !== undefined){
1971             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1972         }
1973         
1974         
1975         
1976         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1977         this.maskEl.enableDisplayMode("block");
1978         this.maskEl.hide();
1979         //this.el.addClass("x-dlg-modal");
1980     
1981         if (this.buttons.length) {
1982             Roo.each(this.buttons, function(bb) {
1983                 b = Roo.apply({}, bb);
1984                 b.xns = b.xns || Roo.bootstrap;
1985                 b.xtype = b.xtype || 'Button';
1986                 if (typeof(b.listeners) == 'undefined') {
1987                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1988                 }
1989                 
1990                 var btn = Roo.factory(b);
1991                 
1992                 btn.onRender(this.el.select('.modal-footer div').first());
1993                 
1994             },this);
1995         }
1996         // render the children.
1997         var nitems = [];
1998         
1999         if(typeof(this.items) != 'undefined'){
2000             var items = this.items;
2001             delete this.items;
2002
2003             for(var i =0;i < items.length;i++) {
2004                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2005             }
2006         }
2007         
2008         this.items = nitems;
2009         
2010         this.body = this.el.select('.modal-body',true).first();
2011         this.close = this.el.select('.modal-header .close', true).first();
2012         this.footer = this.el.select('.modal-footer',true).first();
2013         this.initEvents();
2014         //this.el.addClass([this.fieldClass, this.cls]);
2015         
2016     },
2017     getAutoCreate : function(){
2018         
2019         
2020         var bdy = {
2021                 cls : 'modal-body',
2022                 html : this.html || ''
2023         };
2024         
2025         var title = {
2026             tag: 'h4',
2027             cls : 'modal-title',
2028             html : this.title
2029         };
2030         
2031         if(this.specificTitle){
2032             title = this.title;
2033         };
2034         
2035         return modal = {
2036             cls: "modal fade",
2037             style : 'display: none',
2038             cn : [
2039                 {
2040                     cls: "modal-dialog",
2041                     cn : [
2042                         {
2043                             cls : "modal-content",
2044                             cn : [
2045                                 {
2046                                     cls : 'modal-header',
2047                                     cn : [
2048                                         {
2049                                             tag: 'button',
2050                                             cls : 'close',
2051                                             html : '&times'
2052                                         },
2053                                         title
2054                                     ]
2055                                 },
2056                                 bdy,
2057                                 {
2058                                     cls : 'modal-footer',
2059                                     cn : [
2060                                         {
2061                                             tag: 'div',
2062                                             cls: 'btn-' + this.buttonPosition
2063                                         }
2064                                     ]
2065                                     
2066                                 }
2067                                 
2068                                 
2069                             ]
2070                             
2071                         }
2072                     ]
2073                         
2074                 }
2075             ]
2076             
2077             
2078         };
2079           
2080     },
2081     getChildContainer : function() {
2082          
2083          return this.el.select('.modal-body',true).first();
2084         
2085     },
2086     getButtonContainer : function() {
2087          return this.el.select('.modal-footer div',true).first();
2088         
2089     },
2090     initEvents : function()
2091     {
2092         this.el.select('.modal-header .close').on('click', this.hide, this);
2093 //        
2094 //        this.addxtype(this);
2095     },
2096     show : function() {
2097         
2098         if (!this.rendered) {
2099             this.render();
2100         }
2101        
2102         this.el.addClass('on');
2103         this.el.removeClass('fade');
2104         this.el.setStyle('display', 'block');
2105         Roo.get(document.body).addClass("x-body-masked");
2106         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2107         this.maskEl.show();
2108         this.el.setStyle('zIndex', '10001');
2109         this.fireEvent('show', this);
2110         
2111         
2112     },
2113     hide : function()
2114     {
2115         Roo.log('Modal hide?!');
2116         this.maskEl.hide();
2117         Roo.get(document.body).removeClass("x-body-masked");
2118         this.el.removeClass('on');
2119         this.el.addClass('fade');
2120         this.el.setStyle('display', 'none');
2121         this.fireEvent('hide', this);
2122     },
2123     
2124     addButton : function(str, cb)
2125     {
2126          
2127         
2128         var b = Roo.apply({}, { html : str } );
2129         b.xns = b.xns || Roo.bootstrap;
2130         b.xtype = b.xtype || 'Button';
2131         if (typeof(b.listeners) == 'undefined') {
2132             b.listeners = { click : cb.createDelegate(this)  };
2133         }
2134         
2135         var btn = Roo.factory(b);
2136            
2137         btn.onRender(this.el.select('.modal-footer div').first());
2138         
2139         return btn;   
2140        
2141     },
2142     
2143     setDefaultButton : function(btn)
2144     {
2145         //this.el.select('.modal-footer').()
2146     },
2147     resizeTo: function(w,h)
2148     {
2149         // skip..
2150     },
2151     setContentSize  : function(w, h)
2152     {
2153         
2154     },
2155     onButtonClick: function(btn,e)
2156     {
2157         //Roo.log([a,b,c]);
2158         this.fireEvent('btnclick', btn.name, e);
2159     },
2160     setTitle: function(str) {
2161         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2162         
2163     }
2164 });
2165
2166
2167 Roo.apply(Roo.bootstrap.Modal,  {
2168     /**
2169          * Button config that displays a single OK button
2170          * @type Object
2171          */
2172         OK :  [{
2173             name : 'ok',
2174             weight : 'primary',
2175             html : 'OK'
2176         }], 
2177         /**
2178          * Button config that displays Yes and No buttons
2179          * @type Object
2180          */
2181         YESNO : [
2182             {
2183                 name  : 'no',
2184                 html : 'No'
2185             },
2186             {
2187                 name  :'yes',
2188                 weight : 'primary',
2189                 html : 'Yes'
2190             }
2191         ],
2192         
2193         /**
2194          * Button config that displays OK and Cancel buttons
2195          * @type Object
2196          */
2197         OKCANCEL : [
2198             {
2199                name : 'cancel',
2200                 html : 'Cancel'
2201             },
2202             {
2203                 name : 'ok',
2204                 weight : 'primary',
2205                 html : 'OK'
2206             }
2207         ],
2208         /**
2209          * Button config that displays Yes, No and Cancel buttons
2210          * @type Object
2211          */
2212         YESNOCANCEL : [
2213             {
2214                 name : 'yes',
2215                 weight : 'primary',
2216                 html : 'Yes'
2217             },
2218             {
2219                 name : 'no',
2220                 html : 'No'
2221             },
2222             {
2223                 name : 'cancel',
2224                 html : 'Cancel'
2225             }
2226         ]
2227 });
2228  /*
2229  * - LGPL
2230  *
2231  * messagebox - can be used as a replace
2232  * 
2233  */
2234 /**
2235  * @class Roo.MessageBox
2236  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2237  * Example usage:
2238  *<pre><code>
2239 // Basic alert:
2240 Roo.Msg.alert('Status', 'Changes saved successfully.');
2241
2242 // Prompt for user data:
2243 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2244     if (btn == 'ok'){
2245         // process text value...
2246     }
2247 });
2248
2249 // Show a dialog using config options:
2250 Roo.Msg.show({
2251    title:'Save Changes?',
2252    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2253    buttons: Roo.Msg.YESNOCANCEL,
2254    fn: processResult,
2255    animEl: 'elId'
2256 });
2257 </code></pre>
2258  * @singleton
2259  */
2260 Roo.bootstrap.MessageBox = function(){
2261     var dlg, opt, mask, waitTimer;
2262     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2263     var buttons, activeTextEl, bwidth;
2264
2265     
2266     // private
2267     var handleButton = function(button){
2268         dlg.hide();
2269         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2270     };
2271
2272     // private
2273     var handleHide = function(){
2274         if(opt && opt.cls){
2275             dlg.el.removeClass(opt.cls);
2276         }
2277         //if(waitTimer){
2278         //    Roo.TaskMgr.stop(waitTimer);
2279         //    waitTimer = null;
2280         //}
2281     };
2282
2283     // private
2284     var updateButtons = function(b){
2285         var width = 0;
2286         if(!b){
2287             buttons["ok"].hide();
2288             buttons["cancel"].hide();
2289             buttons["yes"].hide();
2290             buttons["no"].hide();
2291             //dlg.footer.dom.style.display = 'none';
2292             return width;
2293         }
2294         dlg.footer.dom.style.display = '';
2295         for(var k in buttons){
2296             if(typeof buttons[k] != "function"){
2297                 if(b[k]){
2298                     buttons[k].show();
2299                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2300                     width += buttons[k].el.getWidth()+15;
2301                 }else{
2302                     buttons[k].hide();
2303                 }
2304             }
2305         }
2306         return width;
2307     };
2308
2309     // private
2310     var handleEsc = function(d, k, e){
2311         if(opt && opt.closable !== false){
2312             dlg.hide();
2313         }
2314         if(e){
2315             e.stopEvent();
2316         }
2317     };
2318
2319     return {
2320         /**
2321          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2322          * @return {Roo.BasicDialog} The BasicDialog element
2323          */
2324         getDialog : function(){
2325            if(!dlg){
2326                 dlg = new Roo.bootstrap.Modal( {
2327                     //draggable: true,
2328                     //resizable:false,
2329                     //constraintoviewport:false,
2330                     //fixedcenter:true,
2331                     //collapsible : false,
2332                     //shim:true,
2333                     //modal: true,
2334                   //  width:400,
2335                   //  height:100,
2336                     //buttonAlign:"center",
2337                     closeClick : function(){
2338                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2339                             handleButton("no");
2340                         }else{
2341                             handleButton("cancel");
2342                         }
2343                     }
2344                 });
2345                 dlg.render();
2346                 dlg.on("hide", handleHide);
2347                 mask = dlg.mask;
2348                 //dlg.addKeyListener(27, handleEsc);
2349                 buttons = {};
2350                 this.buttons = buttons;
2351                 var bt = this.buttonText;
2352                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2353                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2354                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2355                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2356                 Roo.log(buttons)
2357                 bodyEl = dlg.body.createChild({
2358
2359                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2360                         '<textarea class="roo-mb-textarea"></textarea>' +
2361                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2362                 });
2363                 msgEl = bodyEl.dom.firstChild;
2364                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2365                 textboxEl.enableDisplayMode();
2366                 textboxEl.addKeyListener([10,13], function(){
2367                     if(dlg.isVisible() && opt && opt.buttons){
2368                         if(opt.buttons.ok){
2369                             handleButton("ok");
2370                         }else if(opt.buttons.yes){
2371                             handleButton("yes");
2372                         }
2373                     }
2374                 });
2375                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2376                 textareaEl.enableDisplayMode();
2377                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2378                 progressEl.enableDisplayMode();
2379                 var pf = progressEl.dom.firstChild;
2380                 if (pf) {
2381                     pp = Roo.get(pf.firstChild);
2382                     pp.setHeight(pf.offsetHeight);
2383                 }
2384                 
2385             }
2386             return dlg;
2387         },
2388
2389         /**
2390          * Updates the message box body text
2391          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2392          * the XHTML-compliant non-breaking space character '&amp;#160;')
2393          * @return {Roo.MessageBox} This message box
2394          */
2395         updateText : function(text){
2396             if(!dlg.isVisible() && !opt.width){
2397                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2398             }
2399             msgEl.innerHTML = text || '&#160;';
2400       
2401             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2402             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2403             var w = Math.max(
2404                     Math.min(opt.width || cw , this.maxWidth), 
2405                     Math.max(opt.minWidth || this.minWidth, bwidth)
2406             );
2407             if(opt.prompt){
2408                 activeTextEl.setWidth(w);
2409             }
2410             if(dlg.isVisible()){
2411                 dlg.fixedcenter = false;
2412             }
2413             // to big, make it scroll. = But as usual stupid IE does not support
2414             // !important..
2415             
2416             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2417                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2418                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2419             } else {
2420                 bodyEl.dom.style.height = '';
2421                 bodyEl.dom.style.overflowY = '';
2422             }
2423             if (cw > w) {
2424                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2425             } else {
2426                 bodyEl.dom.style.overflowX = '';
2427             }
2428             
2429             dlg.setContentSize(w, bodyEl.getHeight());
2430             if(dlg.isVisible()){
2431                 dlg.fixedcenter = true;
2432             }
2433             return this;
2434         },
2435
2436         /**
2437          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2438          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2439          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2440          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2441          * @return {Roo.MessageBox} This message box
2442          */
2443         updateProgress : function(value, text){
2444             if(text){
2445                 this.updateText(text);
2446             }
2447             if (pp) { // weird bug on my firefox - for some reason this is not defined
2448                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2449             }
2450             return this;
2451         },        
2452
2453         /**
2454          * Returns true if the message box is currently displayed
2455          * @return {Boolean} True if the message box is visible, else false
2456          */
2457         isVisible : function(){
2458             return dlg && dlg.isVisible();  
2459         },
2460
2461         /**
2462          * Hides the message box if it is displayed
2463          */
2464         hide : function(){
2465             if(this.isVisible()){
2466                 dlg.hide();
2467             }  
2468         },
2469
2470         /**
2471          * Displays a new message box, or reinitializes an existing message box, based on the config options
2472          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2473          * The following config object properties are supported:
2474          * <pre>
2475 Property    Type             Description
2476 ----------  ---------------  ------------------------------------------------------------------------------------
2477 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2478                                    closes (defaults to undefined)
2479 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2480                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2481 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2482                                    progress and wait dialogs will ignore this property and always hide the
2483                                    close button as they can only be closed programmatically.
2484 cls               String           A custom CSS class to apply to the message box element
2485 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2486                                    displayed (defaults to 75)
2487 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2488                                    function will be btn (the name of the button that was clicked, if applicable,
2489                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2490                                    Progress and wait dialogs will ignore this option since they do not respond to
2491                                    user actions and can only be closed programmatically, so any required function
2492                                    should be called by the same code after it closes the dialog.
2493 icon              String           A CSS class that provides a background image to be used as an icon for
2494                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2495 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2496 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2497 modal             Boolean          False to allow user interaction with the page while the message box is
2498                                    displayed (defaults to true)
2499 msg               String           A string that will replace the existing message box body text (defaults
2500                                    to the XHTML-compliant non-breaking space character '&#160;')
2501 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2502 progress          Boolean          True to display a progress bar (defaults to false)
2503 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2504 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2505 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2506 title             String           The title text
2507 value             String           The string value to set into the active textbox element if displayed
2508 wait              Boolean          True to display a progress bar (defaults to false)
2509 width             Number           The width of the dialog in pixels
2510 </pre>
2511          *
2512          * Example usage:
2513          * <pre><code>
2514 Roo.Msg.show({
2515    title: 'Address',
2516    msg: 'Please enter your address:',
2517    width: 300,
2518    buttons: Roo.MessageBox.OKCANCEL,
2519    multiline: true,
2520    fn: saveAddress,
2521    animEl: 'addAddressBtn'
2522 });
2523 </code></pre>
2524          * @param {Object} config Configuration options
2525          * @return {Roo.MessageBox} This message box
2526          */
2527         show : function(options)
2528         {
2529             
2530             // this causes nightmares if you show one dialog after another
2531             // especially on callbacks..
2532              
2533             if(this.isVisible()){
2534                 
2535                 this.hide();
2536                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2537                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2538                 Roo.log("New Dialog Message:" +  options.msg )
2539                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2540                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2541                 
2542             }
2543             var d = this.getDialog();
2544             opt = options;
2545             d.setTitle(opt.title || "&#160;");
2546             d.close.setDisplayed(opt.closable !== false);
2547             activeTextEl = textboxEl;
2548             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2549             if(opt.prompt){
2550                 if(opt.multiline){
2551                     textboxEl.hide();
2552                     textareaEl.show();
2553                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2554                         opt.multiline : this.defaultTextHeight);
2555                     activeTextEl = textareaEl;
2556                 }else{
2557                     textboxEl.show();
2558                     textareaEl.hide();
2559                 }
2560             }else{
2561                 textboxEl.hide();
2562                 textareaEl.hide();
2563             }
2564             progressEl.setDisplayed(opt.progress === true);
2565             this.updateProgress(0);
2566             activeTextEl.dom.value = opt.value || "";
2567             if(opt.prompt){
2568                 dlg.setDefaultButton(activeTextEl);
2569             }else{
2570                 var bs = opt.buttons;
2571                 var db = null;
2572                 if(bs && bs.ok){
2573                     db = buttons["ok"];
2574                 }else if(bs && bs.yes){
2575                     db = buttons["yes"];
2576                 }
2577                 dlg.setDefaultButton(db);
2578             }
2579             bwidth = updateButtons(opt.buttons);
2580             this.updateText(opt.msg);
2581             if(opt.cls){
2582                 d.el.addClass(opt.cls);
2583             }
2584             d.proxyDrag = opt.proxyDrag === true;
2585             d.modal = opt.modal !== false;
2586             d.mask = opt.modal !== false ? mask : false;
2587             if(!d.isVisible()){
2588                 // force it to the end of the z-index stack so it gets a cursor in FF
2589                 document.body.appendChild(dlg.el.dom);
2590                 d.animateTarget = null;
2591                 d.show(options.animEl);
2592             }
2593             return this;
2594         },
2595
2596         /**
2597          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2598          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2599          * and closing the message box when the process is complete.
2600          * @param {String} title The title bar text
2601          * @param {String} msg The message box body text
2602          * @return {Roo.MessageBox} This message box
2603          */
2604         progress : function(title, msg){
2605             this.show({
2606                 title : title,
2607                 msg : msg,
2608                 buttons: false,
2609                 progress:true,
2610                 closable:false,
2611                 minWidth: this.minProgressWidth,
2612                 modal : true
2613             });
2614             return this;
2615         },
2616
2617         /**
2618          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2619          * If a callback function is passed it will be called after the user clicks the button, and the
2620          * id of the button that was clicked will be passed as the only parameter to the callback
2621          * (could also be the top-right close button).
2622          * @param {String} title The title bar text
2623          * @param {String} msg The message box body text
2624          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2625          * @param {Object} scope (optional) The scope of the callback function
2626          * @return {Roo.MessageBox} This message box
2627          */
2628         alert : function(title, msg, fn, scope){
2629             this.show({
2630                 title : title,
2631                 msg : msg,
2632                 buttons: this.OK,
2633                 fn: fn,
2634                 scope : scope,
2635                 modal : true
2636             });
2637             return this;
2638         },
2639
2640         /**
2641          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2642          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2643          * You are responsible for closing the message box when the process is complete.
2644          * @param {String} msg The message box body text
2645          * @param {String} title (optional) The title bar text
2646          * @return {Roo.MessageBox} This message box
2647          */
2648         wait : function(msg, title){
2649             this.show({
2650                 title : title,
2651                 msg : msg,
2652                 buttons: false,
2653                 closable:false,
2654                 progress:true,
2655                 modal:true,
2656                 width:300,
2657                 wait:true
2658             });
2659             waitTimer = Roo.TaskMgr.start({
2660                 run: function(i){
2661                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2662                 },
2663                 interval: 1000
2664             });
2665             return this;
2666         },
2667
2668         /**
2669          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2670          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2671          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2672          * @param {String} title The title bar text
2673          * @param {String} msg The message box body text
2674          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2675          * @param {Object} scope (optional) The scope of the callback function
2676          * @return {Roo.MessageBox} This message box
2677          */
2678         confirm : function(title, msg, fn, scope){
2679             this.show({
2680                 title : title,
2681                 msg : msg,
2682                 buttons: this.YESNO,
2683                 fn: fn,
2684                 scope : scope,
2685                 modal : true
2686             });
2687             return this;
2688         },
2689
2690         /**
2691          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2692          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2693          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2694          * (could also be the top-right close button) and the text that was entered will be passed as the two
2695          * parameters to the callback.
2696          * @param {String} title The title bar text
2697          * @param {String} msg The message box body text
2698          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2699          * @param {Object} scope (optional) The scope of the callback function
2700          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2701          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2702          * @return {Roo.MessageBox} This message box
2703          */
2704         prompt : function(title, msg, fn, scope, multiline){
2705             this.show({
2706                 title : title,
2707                 msg : msg,
2708                 buttons: this.OKCANCEL,
2709                 fn: fn,
2710                 minWidth:250,
2711                 scope : scope,
2712                 prompt:true,
2713                 multiline: multiline,
2714                 modal : true
2715             });
2716             return this;
2717         },
2718
2719         /**
2720          * Button config that displays a single OK button
2721          * @type Object
2722          */
2723         OK : {ok:true},
2724         /**
2725          * Button config that displays Yes and No buttons
2726          * @type Object
2727          */
2728         YESNO : {yes:true, no:true},
2729         /**
2730          * Button config that displays OK and Cancel buttons
2731          * @type Object
2732          */
2733         OKCANCEL : {ok:true, cancel:true},
2734         /**
2735          * Button config that displays Yes, No and Cancel buttons
2736          * @type Object
2737          */
2738         YESNOCANCEL : {yes:true, no:true, cancel:true},
2739
2740         /**
2741          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2742          * @type Number
2743          */
2744         defaultTextHeight : 75,
2745         /**
2746          * The maximum width in pixels of the message box (defaults to 600)
2747          * @type Number
2748          */
2749         maxWidth : 600,
2750         /**
2751          * The minimum width in pixels of the message box (defaults to 100)
2752          * @type Number
2753          */
2754         minWidth : 100,
2755         /**
2756          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2757          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2758          * @type Number
2759          */
2760         minProgressWidth : 250,
2761         /**
2762          * An object containing the default button text strings that can be overriden for localized language support.
2763          * Supported properties are: ok, cancel, yes and no.
2764          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2765          * @type Object
2766          */
2767         buttonText : {
2768             ok : "OK",
2769             cancel : "Cancel",
2770             yes : "Yes",
2771             no : "No"
2772         }
2773     };
2774 }();
2775
2776 /**
2777  * Shorthand for {@link Roo.MessageBox}
2778  */
2779 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2780 Roo.Msg = Roo.Msg || Roo.MessageBox;
2781 /*
2782  * - LGPL
2783  *
2784  * navbar
2785  * 
2786  */
2787
2788 /**
2789  * @class Roo.bootstrap.Navbar
2790  * @extends Roo.bootstrap.Component
2791  * Bootstrap Navbar class
2792
2793  * @constructor
2794  * Create a new Navbar
2795  * @param {Object} config The config object
2796  */
2797
2798
2799 Roo.bootstrap.Navbar = function(config){
2800     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2801     
2802 };
2803
2804 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2805     
2806     
2807    
2808     // private
2809     navItems : false,
2810     loadMask : false,
2811     
2812     
2813     getAutoCreate : function(){
2814         
2815         
2816         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2817         
2818     },
2819     
2820     initEvents :function ()
2821     {
2822         //Roo.log(this.el.select('.navbar-toggle',true));
2823         this.el.select('.navbar-toggle',true).on('click', function() {
2824            // Roo.log('click');
2825             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2826         }, this);
2827         
2828         var mark = {
2829             tag: "div",
2830             cls:"x-dlg-mask"
2831         }
2832         
2833         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2834         
2835         var size = this.el.getSize();
2836         this.maskEl.setSize(size.width, size.height);
2837         this.maskEl.enableDisplayMode("block");
2838         this.maskEl.hide();
2839         
2840         if(this.loadMask){
2841             this.maskEl.show();
2842         }
2843     },
2844     
2845     
2846     getChildContainer : function()
2847     {
2848         if (this.el.select('.collapse').getCount()) {
2849             return this.el.select('.collapse',true).first();
2850         }
2851         
2852         return this.el;
2853     },
2854     
2855     mask : function()
2856     {
2857         this.maskEl.show();
2858     },
2859     
2860     unmask : function()
2861     {
2862         this.maskEl.hide();
2863     } 
2864     
2865     
2866     
2867     
2868 });
2869
2870
2871
2872  
2873
2874  /*
2875  * - LGPL
2876  *
2877  * navbar
2878  * 
2879  */
2880
2881 /**
2882  * @class Roo.bootstrap.NavSimplebar
2883  * @extends Roo.bootstrap.Navbar
2884  * Bootstrap Sidebar class
2885  *
2886  * @cfg {Boolean} inverse is inverted color
2887  * 
2888  * @cfg {String} type (nav | pills | tabs)
2889  * @cfg {Boolean} arrangement stacked | justified
2890  * @cfg {String} align (left | right) alignment
2891  * 
2892  * @cfg {Boolean} main (true|false) main nav bar? default false
2893  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2894  * 
2895  * @cfg {String} tag (header|footer|nav|div) default is nav 
2896
2897  * 
2898  * 
2899  * 
2900  * @constructor
2901  * Create a new Sidebar
2902  * @param {Object} config The config object
2903  */
2904
2905
2906 Roo.bootstrap.NavSimplebar = function(config){
2907     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2908 };
2909
2910 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2911     
2912     inverse: false,
2913     
2914     type: false,
2915     arrangement: '',
2916     align : false,
2917     
2918     
2919     
2920     main : false,
2921     
2922     
2923     tag : false,
2924     
2925     
2926     getAutoCreate : function(){
2927         
2928         
2929         var cfg = {
2930             tag : this.tag || 'div',
2931             cls : 'navbar'
2932         };
2933           
2934         
2935         cfg.cn = [
2936             {
2937                 cls: 'nav',
2938                 tag : 'ul'
2939             }
2940         ];
2941         
2942          
2943         this.type = this.type || 'nav';
2944         if (['tabs','pills'].indexOf(this.type)!==-1) {
2945             cfg.cn[0].cls += ' nav-' + this.type
2946         
2947         
2948         } else {
2949             if (this.type!=='nav') {
2950                 Roo.log('nav type must be nav/tabs/pills')
2951             }
2952             cfg.cn[0].cls += ' navbar-nav'
2953         }
2954         
2955         
2956         
2957         
2958         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2959             cfg.cn[0].cls += ' nav-' + this.arrangement;
2960         }
2961         
2962         
2963         if (this.align === 'right') {
2964             cfg.cn[0].cls += ' navbar-right';
2965         }
2966         
2967         if (this.inverse) {
2968             cfg.cls += ' navbar-inverse';
2969             
2970         }
2971         
2972         
2973         return cfg;
2974     
2975         
2976     }
2977     
2978     
2979     
2980 });
2981
2982
2983
2984  
2985
2986  
2987        /*
2988  * - LGPL
2989  *
2990  * navbar
2991  * 
2992  */
2993
2994 /**
2995  * @class Roo.bootstrap.NavHeaderbar
2996  * @extends Roo.bootstrap.NavSimplebar
2997  * Bootstrap Sidebar class
2998  *
2999  * @cfg {String} brand what is brand
3000  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3001  * @cfg {String} brand_href href of the brand
3002  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3003  * 
3004  * @constructor
3005  * Create a new Sidebar
3006  * @param {Object} config The config object
3007  */
3008
3009
3010 Roo.bootstrap.NavHeaderbar = function(config){
3011     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3012 };
3013
3014 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3015     
3016     position: '',
3017     brand: '',
3018     brand_href: false,
3019     srButton : true,
3020     
3021     
3022     getAutoCreate : function(){
3023         
3024         var   cfg = {
3025             tag: this.nav || 'nav',
3026             cls: 'navbar',
3027             role: 'navigation',
3028             cn: []
3029         };
3030         
3031         if(this.srButton){
3032             cfg.cn.push({
3033                 tag: 'div',
3034                 cls: 'navbar-header',
3035                 cn: [
3036                     {
3037                         tag: 'button',
3038                         type: 'button',
3039                         cls: 'navbar-toggle',
3040                         'data-toggle': 'collapse',
3041                         cn: [
3042                             {
3043                                 tag: 'span',
3044                                 cls: 'sr-only',
3045                                 html: 'Toggle navigation'
3046                             },
3047                             {
3048                                 tag: 'span',
3049                                 cls: 'icon-bar'
3050                             },
3051                             {
3052                                 tag: 'span',
3053                                 cls: 'icon-bar'
3054                             },
3055                             {
3056                                 tag: 'span',
3057                                 cls: 'icon-bar'
3058                             }
3059                         ]
3060                     }
3061                 ]
3062             });
3063         }
3064         
3065         cfg.cn.push({
3066             tag: 'div',
3067             cls: 'collapse navbar-collapse',
3068             cn : []
3069         });
3070         
3071         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3072         
3073         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3074             cfg.cls += ' navbar-' + this.position;
3075             
3076             // tag can override this..
3077             
3078             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3079         }
3080         
3081         if (this.brand !== '') {
3082             cfg.cn[0].cn.push({
3083                 tag: 'a',
3084                 href: this.brand_href ? this.brand_href : '#',
3085                 cls: 'navbar-brand',
3086                 cn: [
3087                 this.brand
3088                 ]
3089             });
3090         }
3091         
3092         if(this.main){
3093             cfg.cls += ' main-nav';
3094         }
3095         
3096         
3097         return cfg;
3098
3099         
3100     }
3101     
3102     
3103     
3104 });
3105
3106
3107
3108  
3109
3110  /*
3111  * - LGPL
3112  *
3113  * navbar
3114  * 
3115  */
3116
3117 /**
3118  * @class Roo.bootstrap.NavSidebar
3119  * @extends Roo.bootstrap.Navbar
3120  * Bootstrap Sidebar class
3121  * 
3122  * @constructor
3123  * Create a new Sidebar
3124  * @param {Object} config The config object
3125  */
3126
3127
3128 Roo.bootstrap.NavSidebar = function(config){
3129     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3130 };
3131
3132 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3133     
3134     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3135     
3136     getAutoCreate : function(){
3137         
3138         
3139         return  {
3140             tag: 'div',
3141             cls: 'sidebar sidebar-nav'
3142         };
3143     
3144         
3145     }
3146     
3147     
3148     
3149 });
3150
3151
3152
3153  
3154
3155  /*
3156  * - LGPL
3157  *
3158  * nav group
3159  * 
3160  */
3161
3162 /**
3163  * @class Roo.bootstrap.NavGroup
3164  * @extends Roo.bootstrap.Component
3165  * Bootstrap NavGroup class
3166  * @cfg {String} align left | right
3167  * @cfg {Boolean} inverse false | true
3168  * @cfg {String} type (nav|pills|tab) default nav
3169  * @cfg {String} navId - reference Id for navbar.
3170
3171  * 
3172  * @constructor
3173  * Create a new nav group
3174  * @param {Object} config The config object
3175  */
3176
3177 Roo.bootstrap.NavGroup = function(config){
3178     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3179     this.navItems = [];
3180     Roo.bootstrap.NavGroup.register(this);
3181      this.addEvents({
3182         /**
3183              * @event changed
3184              * Fires when the active item changes
3185              * @param {Roo.bootstrap.NavGroup} this
3186              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3187              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3188          */
3189         'changed': true
3190      });
3191     
3192 };
3193
3194 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3195     
3196     align: '',
3197     inverse: false,
3198     form: false,
3199     type: 'nav',
3200     navId : '',
3201     // private
3202     
3203     navItems : false,
3204     
3205     getAutoCreate : function()
3206     {
3207         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3208         
3209         cfg = {
3210             tag : 'ul',
3211             cls: 'nav' 
3212         }
3213         
3214         if (['tabs','pills'].indexOf(this.type)!==-1) {
3215             cfg.cls += ' nav-' + this.type
3216         } else {
3217             if (this.type!=='nav') {
3218                 Roo.log('nav type must be nav/tabs/pills')
3219             }
3220             cfg.cls += ' navbar-nav'
3221         }
3222         
3223         if (this.parent().sidebar) {
3224             cfg = {
3225                 tag: 'ul',
3226                 cls: 'dashboard-menu sidebar-menu'
3227             }
3228             
3229             return cfg;
3230         }
3231         
3232         if (this.form === true) {
3233             cfg = {
3234                 tag: 'form',
3235                 cls: 'navbar-form'
3236             }
3237             
3238             if (this.align === 'right') {
3239                 cfg.cls += ' navbar-right';
3240             } else {
3241                 cfg.cls += ' navbar-left';
3242             }
3243         }
3244         
3245         if (this.align === 'right') {
3246             cfg.cls += ' navbar-right';
3247         }
3248         
3249         if (this.inverse) {
3250             cfg.cls += ' navbar-inverse';
3251             
3252         }
3253         
3254         
3255         return cfg;
3256     },
3257     
3258     setActiveItem : function(item)
3259     {
3260         var prev = false;
3261         Roo.each(this.navItems, function(v){
3262             if (v == item) {
3263                 return ;
3264             }
3265             if (v.isActive()) {
3266                 v.setActive(false, true);
3267                 prev = v;
3268                 
3269             }
3270             
3271         });
3272
3273         item.setActive(true, true);
3274         this.fireEvent('changed', this, item, prev);
3275         
3276         
3277     },
3278     
3279     addItem : function(cfg)
3280     {
3281         var cn = new Roo.bootstrap.NavItem(cfg);
3282         this.register(cn);
3283         cn.parentId = this.id;
3284         cn.onRender(this.el, null);
3285         return cn;
3286     },
3287     
3288     register : function(item)
3289     {
3290         this.navItems.push( item);
3291         item.navId = this.navId;
3292     
3293     },
3294     getNavItem: function(tabId)
3295     {
3296         var ret = false;
3297         Roo.each(this.navItems, function(e) {
3298             if (e.tabId == tabId) {
3299                ret =  e;
3300                return false;
3301             }
3302             return true;
3303             
3304         });
3305         return ret;
3306     }
3307     
3308     
3309     
3310     
3311 });
3312
3313  
3314 Roo.apply(Roo.bootstrap.NavGroup, {
3315     
3316     groups: {},
3317     
3318     register : function(navgrp)
3319     {
3320         this.groups[navgrp.navId] = navgrp;
3321         
3322     },
3323     get: function(navId) {
3324         return this.groups[navId];
3325     }
3326     
3327     
3328     
3329 });
3330
3331  /*
3332  * - LGPL
3333  *
3334  * row
3335  * 
3336  */
3337
3338 /**
3339  * @class Roo.bootstrap.NavItem
3340  * @extends Roo.bootstrap.Component
3341  * Bootstrap Navbar.NavItem class
3342  * @cfg {String} href  link to
3343  * @cfg {String} html content of button
3344  * @cfg {String} badge text inside badge
3345  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3346  * @cfg {String} glyphicon name of glyphicon
3347  * @cfg {String} icon name of font awesome icon
3348  * @cfg {Boolean} active Is item active
3349  * @cfg {Boolean} disabled Is item disabled
3350  
3351  * @cfg {Boolean} preventDefault (true | false) default false
3352  * @cfg {String} tabId the tab that this item activates.
3353  * @cfg {String} tagtype (a|span) render as a href or span?
3354   
3355  * @constructor
3356  * Create a new Navbar Item
3357  * @param {Object} config The config object
3358  */
3359 Roo.bootstrap.NavItem = function(config){
3360     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3361     this.addEvents({
3362         // raw events
3363         /**
3364          * @event click
3365          * The raw click event for the entire grid.
3366          * @param {Roo.EventObject} e
3367          */
3368         "click" : true,
3369          /**
3370             * @event changed
3371             * Fires when the active item active state changes
3372             * @param {Roo.bootstrap.NavItem} this
3373             * @param {boolean} state the new state
3374              
3375          */
3376         'changed': true
3377     });
3378    
3379 };
3380
3381 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3382     
3383     href: false,
3384     html: '',
3385     badge: '',
3386     icon: false,
3387     glyphicon: false,
3388     active: false,
3389     preventDefault : false,
3390     tabId : false,
3391     tagtype : 'a',
3392     disabled : false,
3393     
3394     getAutoCreate : function(){
3395          
3396         var cfg = {
3397             tag: 'li',
3398             cls: 'nav-item'
3399             
3400         }
3401         if (this.active) {
3402             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3403         }
3404         if (this.disabled) {
3405             cfg.cls += ' disabled';
3406         }
3407         
3408         if (this.href || this.html || this.glyphicon || this.icon) {
3409             cfg.cn = [
3410                 {
3411                     tag: this.tagtype,
3412                     href : this.href || "#",
3413                     html: this.html || ''
3414                 }
3415             ];
3416             
3417             if (this.icon) {
3418                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3419             }
3420
3421             if(this.glyphicon) {
3422                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3423             }
3424             
3425             if (this.menu) {
3426                 
3427                 cfg.cn[0].html += " <span class='caret'></span>";
3428              
3429             }
3430             
3431             if (this.badge !== '') {
3432                  
3433                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3434             }
3435         }
3436         
3437         
3438         
3439         return cfg;
3440     },
3441     initEvents: function() {
3442        // Roo.log('init events?');
3443        // Roo.log(this.el.dom);
3444         if (typeof (this.menu) != 'undefined') {
3445             this.menu.parentType = this.xtype;
3446             this.menu.triggerEl = this.el;
3447             this.addxtype(Roo.apply({}, this.menu));
3448         }
3449
3450        
3451         this.el.select('a',true).on('click', this.onClick, this);
3452         // at this point parent should be available..
3453         this.parent().register(this);
3454     },
3455     
3456     onClick : function(e)
3457     {
3458          
3459         if(this.preventDefault){
3460             e.preventDefault();
3461         }
3462         if (this.disabled) {
3463             return;
3464         }
3465         Roo.log("fire event clicked");
3466         if(this.fireEvent('click', this, e) === false){
3467             return;
3468         };
3469         
3470         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3471             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3472                 this.parent().setActiveItem(this);
3473             }
3474             
3475             
3476             
3477         } 
3478     },
3479     
3480     isActive: function () {
3481         return this.active
3482     },
3483     setActive : function(state, fire)
3484     {
3485         this.active = state;
3486         if (!state ) {
3487             this.el.removeClass('active');
3488         } else if (!this.el.hasClass('active')) {
3489             this.el.addClass('active');
3490         }
3491         if (fire) {
3492             this.fireEvent('changed', this, state);
3493         }
3494         
3495         
3496     },
3497      // this should not be here...
3498     setDisabled : function(state)
3499     {
3500         this.disabled = state;
3501         if (!state ) {
3502             this.el.removeClass('disabled');
3503         } else if (!this.el.hasClass('disabled')) {
3504             this.el.addClass('disabled');
3505         }
3506         
3507     }
3508 });
3509  
3510
3511  /*
3512  * - LGPL
3513  *
3514  * sidebar item
3515  *
3516  *  li
3517  *    <span> icon </span>
3518  *    <span> text </span>
3519  *    <span>badge </span>
3520  */
3521
3522 /**
3523  * @class Roo.bootstrap.NavSidebarItem
3524  * @extends Roo.bootstrap.NavItem
3525  * Bootstrap Navbar.NavSidebarItem class
3526  * @constructor
3527  * Create a new Navbar Button
3528  * @param {Object} config The config object
3529  */
3530 Roo.bootstrap.NavSidebarItem = function(config){
3531     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3532     this.addEvents({
3533         // raw events
3534         /**
3535          * @event click
3536          * The raw click event for the entire grid.
3537          * @param {Roo.EventObject} e
3538          */
3539         "click" : true,
3540          /**
3541             * @event changed
3542             * Fires when the active item active state changes
3543             * @param {Roo.bootstrap.NavSidebarItem} this
3544             * @param {boolean} state the new state
3545              
3546          */
3547         'changed': true
3548     });
3549    
3550 };
3551
3552 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3553     
3554     
3555     getAutoCreate : function(){
3556         
3557         
3558         var a = {
3559                 tag: 'a',
3560                 href : this.href || '#',
3561                 cls: '',
3562                 html : '',
3563                 cn : []
3564         };
3565         var cfg = {
3566             tag: 'li',
3567             cls: '',
3568             cn: [ a ]
3569         }
3570         var span = {
3571             tag: 'span',
3572             html : this.html || ''
3573         }
3574         
3575         
3576         if (this.active) {
3577             cfg.cls += ' active';
3578         }
3579         
3580         // left icon..
3581         if (this.glyphicon || this.icon) {
3582             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3583             a.cn.push({ tag : 'i', cls : c }) ;
3584         }
3585         // html..
3586         a.cn.push(span);
3587         // then badge..
3588         if (this.badge !== '') {
3589             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3590         }
3591         // fi
3592         if (this.menu) {
3593             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3594             a.cls += 'dropdown-toggle treeview' ;
3595             
3596         }
3597         
3598         
3599         
3600         return cfg;
3601          
3602            
3603     }
3604    
3605      
3606  
3607 });
3608  
3609
3610  /*
3611  * - LGPL
3612  *
3613  * row
3614  * 
3615  */
3616
3617 /**
3618  * @class Roo.bootstrap.Row
3619  * @extends Roo.bootstrap.Component
3620  * Bootstrap Row class (contains columns...)
3621  * 
3622  * @constructor
3623  * Create a new Row
3624  * @param {Object} config The config object
3625  */
3626
3627 Roo.bootstrap.Row = function(config){
3628     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3629 };
3630
3631 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3632     
3633     getAutoCreate : function(){
3634        return {
3635             cls: 'row clearfix'
3636        };
3637     }
3638     
3639     
3640 });
3641
3642  
3643
3644  /*
3645  * - LGPL
3646  *
3647  * element
3648  * 
3649  */
3650
3651 /**
3652  * @class Roo.bootstrap.Element
3653  * @extends Roo.bootstrap.Component
3654  * Bootstrap Element class
3655  * @cfg {String} html contents of the element
3656  * @cfg {String} tag tag of the element
3657  * @cfg {String} cls class of the element
3658  * 
3659  * @constructor
3660  * Create a new Element
3661  * @param {Object} config The config object
3662  */
3663
3664 Roo.bootstrap.Element = function(config){
3665     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3666 };
3667
3668 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3669     
3670     tag: 'div',
3671     cls: '',
3672     html: '',
3673      
3674     
3675     getAutoCreate : function(){
3676         
3677         var cfg = {
3678             tag: this.tag,
3679             cls: this.cls,
3680             html: this.html
3681         }
3682         
3683         
3684         
3685         return cfg;
3686     }
3687    
3688 });
3689
3690  
3691
3692  /*
3693  * - LGPL
3694  *
3695  * pagination
3696  * 
3697  */
3698
3699 /**
3700  * @class Roo.bootstrap.Pagination
3701  * @extends Roo.bootstrap.Component
3702  * Bootstrap Pagination class
3703  * @cfg {String} size xs | sm | md | lg
3704  * @cfg {Boolean} inverse false | true
3705  * 
3706  * @constructor
3707  * Create a new Pagination
3708  * @param {Object} config The config object
3709  */
3710
3711 Roo.bootstrap.Pagination = function(config){
3712     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3713 };
3714
3715 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3716     
3717     cls: false,
3718     size: false,
3719     inverse: false,
3720     
3721     getAutoCreate : function(){
3722         var cfg = {
3723             tag: 'ul',
3724                 cls: 'pagination'
3725         };
3726         if (this.inverse) {
3727             cfg.cls += ' inverse';
3728         }
3729         if (this.html) {
3730             cfg.html=this.html;
3731         }
3732         if (this.cls) {
3733             cfg.cls += " " + this.cls;
3734         }
3735         return cfg;
3736     }
3737    
3738 });
3739
3740  
3741
3742  /*
3743  * - LGPL
3744  *
3745  * Pagination item
3746  * 
3747  */
3748
3749
3750 /**
3751  * @class Roo.bootstrap.PaginationItem
3752  * @extends Roo.bootstrap.Component
3753  * Bootstrap PaginationItem class
3754  * @cfg {String} html text
3755  * @cfg {String} href the link
3756  * @cfg {Boolean} preventDefault (true | false) default true
3757  * @cfg {Boolean} active (true | false) default false
3758  * 
3759  * 
3760  * @constructor
3761  * Create a new PaginationItem
3762  * @param {Object} config The config object
3763  */
3764
3765
3766 Roo.bootstrap.PaginationItem = function(config){
3767     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3768     this.addEvents({
3769         // raw events
3770         /**
3771          * @event click
3772          * The raw click event for the entire grid.
3773          * @param {Roo.EventObject} e
3774          */
3775         "click" : true
3776     });
3777 };
3778
3779 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3780     
3781     href : false,
3782     html : false,
3783     preventDefault: true,
3784     active : false,
3785     cls : false,
3786     
3787     getAutoCreate : function(){
3788         var cfg= {
3789             tag: 'li',
3790             cn: [
3791                 {
3792                     tag : 'a',
3793                     href : this.href ? this.href : '#',
3794                     html : this.html ? this.html : ''
3795                 }
3796             ]
3797         };
3798         
3799         if(this.cls){
3800             cfg.cls = this.cls;
3801         }
3802         
3803         if(this.active){
3804             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3805         }
3806         
3807         return cfg;
3808     },
3809     
3810     initEvents: function() {
3811         
3812         this.el.on('click', this.onClick, this);
3813         
3814     },
3815     onClick : function(e)
3816     {
3817         Roo.log('PaginationItem on click ');
3818         if(this.preventDefault){
3819             e.preventDefault();
3820         }
3821         
3822         this.fireEvent('click', this, e);
3823     }
3824    
3825 });
3826
3827  
3828
3829  /*
3830  * - LGPL
3831  *
3832  * slider
3833  * 
3834  */
3835
3836
3837 /**
3838  * @class Roo.bootstrap.Slider
3839  * @extends Roo.bootstrap.Component
3840  * Bootstrap Slider class
3841  *    
3842  * @constructor
3843  * Create a new Slider
3844  * @param {Object} config The config object
3845  */
3846
3847 Roo.bootstrap.Slider = function(config){
3848     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3849 };
3850
3851 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3852     
3853     getAutoCreate : function(){
3854         
3855         var cfg = {
3856             tag: 'div',
3857             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3858             cn: [
3859                 {
3860                     tag: 'a',
3861                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3862                 }
3863             ]
3864         }
3865         
3866         return cfg;
3867     }
3868    
3869 });
3870
3871  /*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881  
3882
3883 /**
3884  * @class Roo.grid.ColumnModel
3885  * @extends Roo.util.Observable
3886  * This is the default implementation of a ColumnModel used by the Grid. It defines
3887  * the columns in the grid.
3888  * <br>Usage:<br>
3889  <pre><code>
3890  var colModel = new Roo.grid.ColumnModel([
3891         {header: "Ticker", width: 60, sortable: true, locked: true},
3892         {header: "Company Name", width: 150, sortable: true},
3893         {header: "Market Cap.", width: 100, sortable: true},
3894         {header: "$ Sales", width: 100, sortable: true, renderer: money},
3895         {header: "Employees", width: 100, sortable: true, resizable: false}
3896  ]);
3897  </code></pre>
3898  * <p>
3899  
3900  * The config options listed for this class are options which may appear in each
3901  * individual column definition.
3902  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
3903  * @constructor
3904  * @param {Object} config An Array of column config objects. See this class's
3905  * config objects for details.
3906 */
3907 Roo.grid.ColumnModel = function(config){
3908         /**
3909      * The config passed into the constructor
3910      */
3911     this.config = config;
3912     this.lookup = {};
3913
3914     // if no id, create one
3915     // if the column does not have a dataIndex mapping,
3916     // map it to the order it is in the config
3917     for(var i = 0, len = config.length; i < len; i++){
3918         var c = config[i];
3919         if(typeof c.dataIndex == "undefined"){
3920             c.dataIndex = i;
3921         }
3922         if(typeof c.renderer == "string"){
3923             c.renderer = Roo.util.Format[c.renderer];
3924         }
3925         if(typeof c.id == "undefined"){
3926             c.id = Roo.id();
3927         }
3928         if(c.editor && c.editor.xtype){
3929             c.editor  = Roo.factory(c.editor, Roo.grid);
3930         }
3931         if(c.editor && c.editor.isFormField){
3932             c.editor = new Roo.grid.GridEditor(c.editor);
3933         }
3934         this.lookup[c.id] = c;
3935     }
3936
3937     /**
3938      * The width of columns which have no width specified (defaults to 100)
3939      * @type Number
3940      */
3941     this.defaultWidth = 100;
3942
3943     /**
3944      * Default sortable of columns which have no sortable specified (defaults to false)
3945      * @type Boolean
3946      */
3947     this.defaultSortable = false;
3948
3949     this.addEvents({
3950         /**
3951              * @event widthchange
3952              * Fires when the width of a column changes.
3953              * @param {ColumnModel} this
3954              * @param {Number} columnIndex The column index
3955              * @param {Number} newWidth The new width
3956              */
3957             "widthchange": true,
3958         /**
3959              * @event headerchange
3960              * Fires when the text of a header changes.
3961              * @param {ColumnModel} this
3962              * @param {Number} columnIndex The column index
3963              * @param {Number} newText The new header text
3964              */
3965             "headerchange": true,
3966         /**
3967              * @event hiddenchange
3968              * Fires when a column is hidden or "unhidden".
3969              * @param {ColumnModel} this
3970              * @param {Number} columnIndex The column index
3971              * @param {Boolean} hidden true if hidden, false otherwise
3972              */
3973             "hiddenchange": true,
3974             /**
3975          * @event columnmoved
3976          * Fires when a column is moved.
3977          * @param {ColumnModel} this
3978          * @param {Number} oldIndex
3979          * @param {Number} newIndex
3980          */
3981         "columnmoved" : true,
3982         /**
3983          * @event columlockchange
3984          * Fires when a column's locked state is changed
3985          * @param {ColumnModel} this
3986          * @param {Number} colIndex
3987          * @param {Boolean} locked true if locked
3988          */
3989         "columnlockchange" : true
3990     });
3991     Roo.grid.ColumnModel.superclass.constructor.call(this);
3992 };
3993 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
3994     /**
3995      * @cfg {String} header The header text to display in the Grid view.
3996      */
3997     /**
3998      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
3999      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4000      * specified, the column's index is used as an index into the Record's data Array.
4001      */
4002     /**
4003      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4004      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4005      */
4006     /**
4007      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4008      * Defaults to the value of the {@link #defaultSortable} property.
4009      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4010      */
4011     /**
4012      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4013      */
4014     /**
4015      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4016      */
4017     /**
4018      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4019      */
4020     /**
4021      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4022      */
4023     /**
4024      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4025      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4026      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4027      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4028      */
4029        /**
4030      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4031      */
4032     /**
4033      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4034      */
4035
4036     /**
4037      * Returns the id of the column at the specified index.
4038      * @param {Number} index The column index
4039      * @return {String} the id
4040      */
4041     getColumnId : function(index){
4042         return this.config[index].id;
4043     },
4044
4045     /**
4046      * Returns the column for a specified id.
4047      * @param {String} id The column id
4048      * @return {Object} the column
4049      */
4050     getColumnById : function(id){
4051         return this.lookup[id];
4052     },
4053
4054     
4055     /**
4056      * Returns the column for a specified dataIndex.
4057      * @param {String} dataIndex The column dataIndex
4058      * @return {Object|Boolean} the column or false if not found
4059      */
4060     getColumnByDataIndex: function(dataIndex){
4061         var index = this.findColumnIndex(dataIndex);
4062         return index > -1 ? this.config[index] : false;
4063     },
4064     
4065     /**
4066      * Returns the index for a specified column id.
4067      * @param {String} id The column id
4068      * @return {Number} the index, or -1 if not found
4069      */
4070     getIndexById : function(id){
4071         for(var i = 0, len = this.config.length; i < len; i++){
4072             if(this.config[i].id == id){
4073                 return i;
4074             }
4075         }
4076         return -1;
4077     },
4078     
4079     /**
4080      * Returns the index for a specified column dataIndex.
4081      * @param {String} dataIndex The column dataIndex
4082      * @return {Number} the index, or -1 if not found
4083      */
4084     
4085     findColumnIndex : function(dataIndex){
4086         for(var i = 0, len = this.config.length; i < len; i++){
4087             if(this.config[i].dataIndex == dataIndex){
4088                 return i;
4089             }
4090         }
4091         return -1;
4092     },
4093     
4094     
4095     moveColumn : function(oldIndex, newIndex){
4096         var c = this.config[oldIndex];
4097         this.config.splice(oldIndex, 1);
4098         this.config.splice(newIndex, 0, c);
4099         this.dataMap = null;
4100         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4101     },
4102
4103     isLocked : function(colIndex){
4104         return this.config[colIndex].locked === true;
4105     },
4106
4107     setLocked : function(colIndex, value, suppressEvent){
4108         if(this.isLocked(colIndex) == value){
4109             return;
4110         }
4111         this.config[colIndex].locked = value;
4112         if(!suppressEvent){
4113             this.fireEvent("columnlockchange", this, colIndex, value);
4114         }
4115     },
4116
4117     getTotalLockedWidth : function(){
4118         var totalWidth = 0;
4119         for(var i = 0; i < this.config.length; i++){
4120             if(this.isLocked(i) && !this.isHidden(i)){
4121                 this.totalWidth += this.getColumnWidth(i);
4122             }
4123         }
4124         return totalWidth;
4125     },
4126
4127     getLockedCount : function(){
4128         for(var i = 0, len = this.config.length; i < len; i++){
4129             if(!this.isLocked(i)){
4130                 return i;
4131             }
4132         }
4133     },
4134
4135     /**
4136      * Returns the number of columns.
4137      * @return {Number}
4138      */
4139     getColumnCount : function(visibleOnly){
4140         if(visibleOnly === true){
4141             var c = 0;
4142             for(var i = 0, len = this.config.length; i < len; i++){
4143                 if(!this.isHidden(i)){
4144                     c++;
4145                 }
4146             }
4147             return c;
4148         }
4149         return this.config.length;
4150     },
4151
4152     /**
4153      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4154      * @param {Function} fn
4155      * @param {Object} scope (optional)
4156      * @return {Array} result
4157      */
4158     getColumnsBy : function(fn, scope){
4159         var r = [];
4160         for(var i = 0, len = this.config.length; i < len; i++){
4161             var c = this.config[i];
4162             if(fn.call(scope||this, c, i) === true){
4163                 r[r.length] = c;
4164             }
4165         }
4166         return r;
4167     },
4168
4169     /**
4170      * Returns true if the specified column is sortable.
4171      * @param {Number} col The column index
4172      * @return {Boolean}
4173      */
4174     isSortable : function(col){
4175         if(typeof this.config[col].sortable == "undefined"){
4176             return this.defaultSortable;
4177         }
4178         return this.config[col].sortable;
4179     },
4180
4181     /**
4182      * Returns the rendering (formatting) function defined for the column.
4183      * @param {Number} col The column index.
4184      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4185      */
4186     getRenderer : function(col){
4187         if(!this.config[col].renderer){
4188             return Roo.grid.ColumnModel.defaultRenderer;
4189         }
4190         return this.config[col].renderer;
4191     },
4192
4193     /**
4194      * Sets the rendering (formatting) function for a column.
4195      * @param {Number} col The column index
4196      * @param {Function} fn The function to use to process the cell's raw data
4197      * to return HTML markup for the grid view. The render function is called with
4198      * the following parameters:<ul>
4199      * <li>Data value.</li>
4200      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4201      * <li>css A CSS style string to apply to the table cell.</li>
4202      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4203      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4204      * <li>Row index</li>
4205      * <li>Column index</li>
4206      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4207      */
4208     setRenderer : function(col, fn){
4209         this.config[col].renderer = fn;
4210     },
4211
4212     /**
4213      * Returns the width for the specified column.
4214      * @param {Number} col The column index
4215      * @return {Number}
4216      */
4217     getColumnWidth : function(col){
4218         return this.config[col].width * 1 || this.defaultWidth;
4219     },
4220
4221     /**
4222      * Sets the width for a column.
4223      * @param {Number} col The column index
4224      * @param {Number} width The new width
4225      */
4226     setColumnWidth : function(col, width, suppressEvent){
4227         this.config[col].width = width;
4228         this.totalWidth = null;
4229         if(!suppressEvent){
4230              this.fireEvent("widthchange", this, col, width);
4231         }
4232     },
4233
4234     /**
4235      * Returns the total width of all columns.
4236      * @param {Boolean} includeHidden True to include hidden column widths
4237      * @return {Number}
4238      */
4239     getTotalWidth : function(includeHidden){
4240         if(!this.totalWidth){
4241             this.totalWidth = 0;
4242             for(var i = 0, len = this.config.length; i < len; i++){
4243                 if(includeHidden || !this.isHidden(i)){
4244                     this.totalWidth += this.getColumnWidth(i);
4245                 }
4246             }
4247         }
4248         return this.totalWidth;
4249     },
4250
4251     /**
4252      * Returns the header for the specified column.
4253      * @param {Number} col The column index
4254      * @return {String}
4255      */
4256     getColumnHeader : function(col){
4257         return this.config[col].header;
4258     },
4259
4260     /**
4261      * Sets the header for a column.
4262      * @param {Number} col The column index
4263      * @param {String} header The new header
4264      */
4265     setColumnHeader : function(col, header){
4266         this.config[col].header = header;
4267         this.fireEvent("headerchange", this, col, header);
4268     },
4269
4270     /**
4271      * Returns the tooltip for the specified column.
4272      * @param {Number} col The column index
4273      * @return {String}
4274      */
4275     getColumnTooltip : function(col){
4276             return this.config[col].tooltip;
4277     },
4278     /**
4279      * Sets the tooltip for a column.
4280      * @param {Number} col The column index
4281      * @param {String} tooltip The new tooltip
4282      */
4283     setColumnTooltip : function(col, tooltip){
4284             this.config[col].tooltip = tooltip;
4285     },
4286
4287     /**
4288      * Returns the dataIndex for the specified column.
4289      * @param {Number} col The column index
4290      * @return {Number}
4291      */
4292     getDataIndex : function(col){
4293         return this.config[col].dataIndex;
4294     },
4295
4296     /**
4297      * Sets the dataIndex for a column.
4298      * @param {Number} col The column index
4299      * @param {Number} dataIndex The new dataIndex
4300      */
4301     setDataIndex : function(col, dataIndex){
4302         this.config[col].dataIndex = dataIndex;
4303     },
4304
4305     
4306     
4307     /**
4308      * Returns true if the cell is editable.
4309      * @param {Number} colIndex The column index
4310      * @param {Number} rowIndex The row index
4311      * @return {Boolean}
4312      */
4313     isCellEditable : function(colIndex, rowIndex){
4314         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4315     },
4316
4317     /**
4318      * Returns the editor defined for the cell/column.
4319      * return false or null to disable editing.
4320      * @param {Number} colIndex The column index
4321      * @param {Number} rowIndex The row index
4322      * @return {Object}
4323      */
4324     getCellEditor : function(colIndex, rowIndex){
4325         return this.config[colIndex].editor;
4326     },
4327
4328     /**
4329      * Sets if a column is editable.
4330      * @param {Number} col The column index
4331      * @param {Boolean} editable True if the column is editable
4332      */
4333     setEditable : function(col, editable){
4334         this.config[col].editable = editable;
4335     },
4336
4337
4338     /**
4339      * Returns true if the column is hidden.
4340      * @param {Number} colIndex The column index
4341      * @return {Boolean}
4342      */
4343     isHidden : function(colIndex){
4344         return this.config[colIndex].hidden;
4345     },
4346
4347
4348     /**
4349      * Returns true if the column width cannot be changed
4350      */
4351     isFixed : function(colIndex){
4352         return this.config[colIndex].fixed;
4353     },
4354
4355     /**
4356      * Returns true if the column can be resized
4357      * @return {Boolean}
4358      */
4359     isResizable : function(colIndex){
4360         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4361     },
4362     /**
4363      * Sets if a column is hidden.
4364      * @param {Number} colIndex The column index
4365      * @param {Boolean} hidden True if the column is hidden
4366      */
4367     setHidden : function(colIndex, hidden){
4368         this.config[colIndex].hidden = hidden;
4369         this.totalWidth = null;
4370         this.fireEvent("hiddenchange", this, colIndex, hidden);
4371     },
4372
4373     /**
4374      * Sets the editor for a column.
4375      * @param {Number} col The column index
4376      * @param {Object} editor The editor object
4377      */
4378     setEditor : function(col, editor){
4379         this.config[col].editor = editor;
4380     }
4381 });
4382
4383 Roo.grid.ColumnModel.defaultRenderer = function(value){
4384         if(typeof value == "string" && value.length < 1){
4385             return "&#160;";
4386         }
4387         return value;
4388 };
4389
4390 // Alias for backwards compatibility
4391 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4392 /*
4393  * Based on:
4394  * Ext JS Library 1.1.1
4395  * Copyright(c) 2006-2007, Ext JS, LLC.
4396  *
4397  * Originally Released Under LGPL - original licence link has changed is not relivant.
4398  *
4399  * Fork - LGPL
4400  * <script type="text/javascript">
4401  */
4402  
4403 /**
4404  * @class Roo.LoadMask
4405  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4406  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4407  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4408  * element's UpdateManager load indicator and will be destroyed after the initial load.
4409  * @constructor
4410  * Create a new LoadMask
4411  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4412  * @param {Object} config The config object
4413  */
4414 Roo.LoadMask = function(el, config){
4415     this.el = Roo.get(el);
4416     Roo.apply(this, config);
4417     if(this.store){
4418         this.store.on('beforeload', this.onBeforeLoad, this);
4419         this.store.on('load', this.onLoad, this);
4420         this.store.on('loadexception', this.onLoadException, this);
4421         this.removeMask = false;
4422     }else{
4423         var um = this.el.getUpdateManager();
4424         um.showLoadIndicator = false; // disable the default indicator
4425         um.on('beforeupdate', this.onBeforeLoad, this);
4426         um.on('update', this.onLoad, this);
4427         um.on('failure', this.onLoad, this);
4428         this.removeMask = true;
4429     }
4430 };
4431
4432 Roo.LoadMask.prototype = {
4433     /**
4434      * @cfg {Boolean} removeMask
4435      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4436      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4437      */
4438     /**
4439      * @cfg {String} msg
4440      * The text to display in a centered loading message box (defaults to 'Loading...')
4441      */
4442     msg : 'Loading...',
4443     /**
4444      * @cfg {String} msgCls
4445      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4446      */
4447     msgCls : 'x-mask-loading',
4448
4449     /**
4450      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4451      * @type Boolean
4452      */
4453     disabled: false,
4454
4455     /**
4456      * Disables the mask to prevent it from being displayed
4457      */
4458     disable : function(){
4459        this.disabled = true;
4460     },
4461
4462     /**
4463      * Enables the mask so that it can be displayed
4464      */
4465     enable : function(){
4466         this.disabled = false;
4467     },
4468     
4469     onLoadException : function()
4470     {
4471         Roo.log(arguments);
4472         
4473         if (typeof(arguments[3]) != 'undefined') {
4474             Roo.MessageBox.alert("Error loading",arguments[3]);
4475         } 
4476         /*
4477         try {
4478             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4479                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4480             }   
4481         } catch(e) {
4482             
4483         }
4484         */
4485     
4486         
4487         
4488         this.el.unmask(this.removeMask);
4489     },
4490     // private
4491     onLoad : function()
4492     {
4493         this.el.unmask(this.removeMask);
4494     },
4495
4496     // private
4497     onBeforeLoad : function(){
4498         if(!this.disabled){
4499             this.el.mask(this.msg, this.msgCls);
4500         }
4501     },
4502
4503     // private
4504     destroy : function(){
4505         if(this.store){
4506             this.store.un('beforeload', this.onBeforeLoad, this);
4507             this.store.un('load', this.onLoad, this);
4508             this.store.un('loadexception', this.onLoadException, this);
4509         }else{
4510             var um = this.el.getUpdateManager();
4511             um.un('beforeupdate', this.onBeforeLoad, this);
4512             um.un('update', this.onLoad, this);
4513             um.un('failure', this.onLoad, this);
4514         }
4515     }
4516 };/*
4517  * - LGPL
4518  *
4519  * table
4520  * 
4521  */
4522
4523 /**
4524  * @class Roo.bootstrap.Table
4525  * @extends Roo.bootstrap.Component
4526  * Bootstrap Table class
4527  * @cfg {String} cls table class
4528  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4529  * @cfg {String} bgcolor Specifies the background color for a table
4530  * @cfg {Number} border Specifies whether the table cells should have borders or not
4531  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4532  * @cfg {Number} cellspacing Specifies the space between cells
4533  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4534  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4535  * @cfg {String} sortable Specifies that the table should be sortable
4536  * @cfg {String} summary Specifies a summary of the content of a table
4537  * @cfg {Number} width Specifies the width of a table
4538  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4539  * 
4540  * @cfg {boolean} striped Should the rows be alternative striped
4541  * @cfg {boolean} bordered Add borders to the table
4542  * @cfg {boolean} hover Add hover highlighting
4543  * @cfg {boolean} condensed Format condensed
4544  * @cfg {boolean} responsive Format condensed
4545  * @cfg {Boolean} loadMask (true|false) default false
4546  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4547  * @cfg {Boolean} thead (true|false) generate thead, default true
4548  * @cfg {Boolean} RowSelection (true|false) default false
4549  * @cfg {Boolean} CellSelection (true|false) default false
4550  *
4551  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4552  
4553  * 
4554  * @constructor
4555  * Create a new Table
4556  * @param {Object} config The config object
4557  */
4558
4559 Roo.bootstrap.Table = function(config){
4560     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4561     
4562     if (this.sm) {
4563         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4564         this.sm = this.selModel;
4565         this.sm.xmodule = this.xmodule || false;
4566     }
4567     if (this.cm && typeof(this.cm.config) == 'undefined') {
4568         this.colModel = new Roo.grid.ColumnModel(this.cm);
4569         this.cm = this.colModel;
4570         this.cm.xmodule = this.xmodule || false;
4571     }
4572     if (this.store) {
4573         this.store= Roo.factory(this.store, Roo.data);
4574         this.ds = this.store;
4575         this.ds.xmodule = this.xmodule || false;
4576          
4577     }
4578     if (this.footer && this.store) {
4579         this.footer.dataSource = this.ds;
4580         this.footer = Roo.factory(this.footer);
4581     }
4582     
4583     /** @private */
4584     this.addEvents({
4585         /**
4586          * @event cellclick
4587          * Fires when a cell is clicked
4588          * @param {Roo.bootstrap.Table} this
4589          * @param {Roo.Element} el
4590          * @param {Number} rowIndex
4591          * @param {Number} columnIndex
4592          * @param {Roo.EventObject} e
4593          */
4594         "cellclick" : true,
4595         /**
4596          * @event celldblclick
4597          * Fires when a cell is double clicked
4598          * @param {Roo.bootstrap.Table} this
4599          * @param {Roo.Element} el
4600          * @param {Number} rowIndex
4601          * @param {Number} columnIndex
4602          * @param {Roo.EventObject} e
4603          */
4604         "celldblclick" : true,
4605         /**
4606          * @event rowclick
4607          * Fires when a row is clicked
4608          * @param {Roo.bootstrap.Table} this
4609          * @param {Roo.Element} el
4610          * @param {Number} rowIndex
4611          * @param {Roo.EventObject} e
4612          */
4613         "rowclick" : true,
4614         /**
4615          * @event rowdblclick
4616          * Fires when a row is double clicked
4617          * @param {Roo.bootstrap.Table} this
4618          * @param {Roo.Element} el
4619          * @param {Number} rowIndex
4620          * @param {Roo.EventObject} e
4621          */
4622         "rowdblclick" : true,
4623         /**
4624          * @event mouseover
4625          * Fires when a mouseover occur
4626          * @param {Roo.bootstrap.Table} this
4627          * @param {Roo.Element} el
4628          * @param {Number} rowIndex
4629          * @param {Number} columnIndex
4630          * @param {Roo.EventObject} e
4631          */
4632         "mouseover" : true,
4633         /**
4634          * @event mouseout
4635          * Fires when a mouseout occur
4636          * @param {Roo.bootstrap.Table} this
4637          * @param {Roo.Element} el
4638          * @param {Number} rowIndex
4639          * @param {Number} columnIndex
4640          * @param {Roo.EventObject} e
4641          */
4642         "mouseout" : true,
4643         /**
4644          * @event rowclass
4645          * Fires when a row is rendered, so you can change add a style to it.
4646          * @param {Roo.bootstrap.Table} this
4647          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4648          */
4649         'rowclass' : true
4650         
4651     });
4652 };
4653
4654 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4655     
4656     cls: false,
4657     align: false,
4658     bgcolor: false,
4659     border: false,
4660     cellpadding: false,
4661     cellspacing: false,
4662     frame: false,
4663     rules: false,
4664     sortable: false,
4665     summary: false,
4666     width: false,
4667     striped : false,
4668     bordered: false,
4669     hover:  false,
4670     condensed : false,
4671     responsive : false,
4672     sm : false,
4673     cm : false,
4674     store : false,
4675     loadMask : false,
4676     tfoot : true,
4677     thead : true,
4678     RowSelection : false,
4679     CellSelection : false,
4680     layout : false,
4681     
4682     // Roo.Element - the tbody
4683     mainBody: false, 
4684     
4685     getAutoCreate : function(){
4686         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4687         
4688         cfg = {
4689             tag: 'table',
4690             cls : 'table',
4691             cn : []
4692         }
4693             
4694         if (this.striped) {
4695             cfg.cls += ' table-striped';
4696         }
4697         
4698         if (this.hover) {
4699             cfg.cls += ' table-hover';
4700         }
4701         if (this.bordered) {
4702             cfg.cls += ' table-bordered';
4703         }
4704         if (this.condensed) {
4705             cfg.cls += ' table-condensed';
4706         }
4707         if (this.responsive) {
4708             cfg.cls += ' table-responsive';
4709         }
4710         
4711         if (this.cls) {
4712             cfg.cls+=  ' ' +this.cls;
4713         }
4714         
4715         // this lot should be simplifed...
4716         
4717         if (this.align) {
4718             cfg.align=this.align;
4719         }
4720         if (this.bgcolor) {
4721             cfg.bgcolor=this.bgcolor;
4722         }
4723         if (this.border) {
4724             cfg.border=this.border;
4725         }
4726         if (this.cellpadding) {
4727             cfg.cellpadding=this.cellpadding;
4728         }
4729         if (this.cellspacing) {
4730             cfg.cellspacing=this.cellspacing;
4731         }
4732         if (this.frame) {
4733             cfg.frame=this.frame;
4734         }
4735         if (this.rules) {
4736             cfg.rules=this.rules;
4737         }
4738         if (this.sortable) {
4739             cfg.sortable=this.sortable;
4740         }
4741         if (this.summary) {
4742             cfg.summary=this.summary;
4743         }
4744         if (this.width) {
4745             cfg.width=this.width;
4746         }
4747         if (this.layout) {
4748             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4749         }
4750         
4751         if(this.store || this.cm){
4752             if(this.thead){
4753                 cfg.cn.push(this.renderHeader());
4754             }
4755             
4756             cfg.cn.push(this.renderBody());
4757             
4758             if(this.tfoot){
4759                 cfg.cn.push(this.renderFooter());
4760             }
4761             
4762             cfg.cls+=  ' TableGrid';
4763         }
4764         
4765         return { cn : [ cfg ] };
4766     },
4767     
4768     initEvents : function()
4769     {   
4770         if(!this.store || !this.cm){
4771             return;
4772         }
4773         
4774         //Roo.log('initEvents with ds!!!!');
4775         
4776         this.mainBody = this.el.select('tbody', true).first();
4777         
4778         
4779         var _this = this;
4780         
4781         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4782             e.on('click', _this.sort, _this);
4783         });
4784         
4785         this.el.on("click", this.onClick, this);
4786         this.el.on("dblclick", this.onDblClick, this);
4787         
4788         this.parent().el.setStyle('position', 'relative');
4789         if (this.footer) {
4790             this.footer.parentId = this.id;
4791             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4792         }
4793         
4794         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4795         
4796         this.store.on('load', this.onLoad, this);
4797         this.store.on('beforeload', this.onBeforeLoad, this);
4798         this.store.on('update', this.onUpdate, this);
4799         
4800     },
4801     
4802     onMouseover : function(e, el)
4803     {
4804         var cell = Roo.get(el);
4805         
4806         if(!cell){
4807             return;
4808         }
4809         
4810         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4811             cell = cell.findParent('td', false, true);
4812         }
4813         
4814         var row = cell.findParent('tr', false, true);
4815         var cellIndex = cell.dom.cellIndex;
4816         var rowIndex = row.dom.rowIndex - 1; // start from 0
4817         
4818         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
4819         
4820     },
4821     
4822     onMouseout : function(e, el)
4823     {
4824         var cell = Roo.get(el);
4825         
4826         if(!cell){
4827             return;
4828         }
4829         
4830         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4831             cell = cell.findParent('td', false, true);
4832         }
4833         
4834         var row = cell.findParent('tr', false, true);
4835         var cellIndex = cell.dom.cellIndex;
4836         var rowIndex = row.dom.rowIndex - 1; // start from 0
4837         
4838         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
4839         
4840     },
4841     
4842     onClick : function(e, el)
4843     {
4844         var cell = Roo.get(el);
4845         
4846         if(!cell || (!this.CellSelection && !this.RowSelection)){
4847             return;
4848         }
4849         
4850         
4851         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4852             cell = cell.findParent('td', false, true);
4853         }
4854         
4855         var row = cell.findParent('tr', false, true);
4856         var cellIndex = cell.dom.cellIndex;
4857         var rowIndex = row.dom.rowIndex - 1;
4858         
4859         if(this.CellSelection){
4860             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
4861         }
4862         
4863         if(this.RowSelection){
4864             this.fireEvent('rowclick', this, row, rowIndex, e);
4865         }
4866         
4867         
4868     },
4869     
4870     onDblClick : function(e,el)
4871     {
4872         var cell = Roo.get(el);
4873         
4874         if(!cell || (!this.CellSelection && !this.RowSelection)){
4875             return;
4876         }
4877         
4878         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4879             cell = cell.findParent('td', false, true);
4880         }
4881         
4882         var row = cell.findParent('tr', false, true);
4883         var cellIndex = cell.dom.cellIndex;
4884         var rowIndex = row.dom.rowIndex - 1;
4885         
4886         if(this.CellSelection){
4887             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
4888         }
4889         
4890         if(this.RowSelection){
4891             this.fireEvent('rowdblclick', this, row, rowIndex, e);
4892         }
4893     },
4894     
4895     sort : function(e,el)
4896     {
4897         var col = Roo.get(el)
4898         
4899         if(!col.hasClass('sortable')){
4900             return;
4901         }
4902         
4903         var sort = col.attr('sort');
4904         var dir = 'ASC';
4905         
4906         if(col.hasClass('glyphicon-arrow-up')){
4907             dir = 'DESC';
4908         }
4909         
4910         this.store.sortInfo = {field : sort, direction : dir};
4911         
4912         if (this.footer) {
4913             Roo.log("calling footer first");
4914             this.footer.onClick('first');
4915         } else {
4916         
4917             this.store.load({ params : { start : 0 } });
4918         }
4919     },
4920     
4921     renderHeader : function()
4922     {
4923         var header = {
4924             tag: 'thead',
4925             cn : []
4926         };
4927         
4928         var cm = this.cm;
4929         
4930         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4931             
4932             var config = cm.config[i];
4933                     
4934             var c = {
4935                 tag: 'th',
4936                 style : '',
4937                 html: cm.getColumnHeader(i)
4938             };
4939             
4940             if(typeof(config.hidden) != 'undefined' && config.hidden){
4941                 c.style += ' display:none;';
4942             }
4943             
4944             if(typeof(config.dataIndex) != 'undefined'){
4945                 c.sort = config.dataIndex;
4946             }
4947             
4948             if(typeof(config.sortable) != 'undefined' && config.sortable){
4949                 c.cls = 'sortable';
4950             }
4951             
4952 //            if(typeof(config.align) != 'undefined' && config.align.length){
4953 //                c.style += ' text-align:' + config.align + ';';
4954 //            }
4955             
4956             if(typeof(config.width) != 'undefined'){
4957                 c.style += ' width:' + config.width + 'px;';
4958             }
4959             
4960             header.cn.push(c)
4961         }
4962         
4963         return header;
4964     },
4965     
4966     renderBody : function()
4967     {
4968         var body = {
4969             tag: 'tbody',
4970             cn : [
4971                 {
4972                     tag: 'tr',
4973                     cn : [
4974                         {
4975                             tag : 'td',
4976                             colspan :  this.cm.getColumnCount()
4977                         }
4978                     ]
4979                 }
4980             ]
4981         };
4982         
4983         return body;
4984     },
4985     
4986     renderFooter : function()
4987     {
4988         var footer = {
4989             tag: 'tfoot',
4990             cn : [
4991                 {
4992                     tag: 'tr',
4993                     cn : [
4994                         {
4995                             tag : 'td',
4996                             colspan :  this.cm.getColumnCount()
4997                         }
4998                     ]
4999                 }
5000             ]
5001         };
5002         
5003         return footer;
5004     },
5005     
5006     
5007     
5008     onLoad : function()
5009     {
5010         Roo.log('ds onload');
5011         this.clear();
5012         
5013         var _this = this;
5014         var cm = this.cm;
5015         var ds = this.store;
5016         
5017         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5018             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5019             
5020             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5021                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5022             }
5023             
5024             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5025                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5026             }
5027         });
5028         
5029         var tbody =  this.mainBody;
5030         
5031         var renders = [];
5032                     
5033         if(ds.getCount() > 0){
5034             ds.data.each(function(d,rowIndex){
5035                 var row =  this.renderRow(cm, ds, rowIndex);
5036                 
5037                 tbody.createChild(row);
5038                 
5039                 var _this = this;
5040                 
5041                 if(row.cellObjects.length){
5042                     Roo.each(row.cellObjects, function(r){
5043                         _this.renderCellObject(r);
5044                     })
5045                 }
5046                 
5047             }, this);
5048         }
5049         
5050         Roo.each(this.el.select('tbody td', true).elements, function(e){
5051             e.on('mouseover', _this.onMouseover, _this);
5052         });
5053         
5054         Roo.each(this.el.select('tbody td', true).elements, function(e){
5055             e.on('mouseout', _this.onMouseout, _this);
5056         });
5057
5058         //if(this.loadMask){
5059         //    this.maskEl.hide();
5060         //}
5061     },
5062     
5063     
5064     onUpdate : function(ds,record)
5065     {
5066         this.refreshRow(record);
5067     },
5068     onRemove : function(ds, record, index, isUpdate){
5069         if(isUpdate !== true){
5070             this.fireEvent("beforerowremoved", this, index, record);
5071         }
5072         var bt = this.mainBody.dom;
5073         if(bt.rows[index]){
5074             bt.removeChild(bt.rows[index]);
5075         }
5076         
5077         if(isUpdate !== true){
5078             //this.stripeRows(index);
5079             //this.syncRowHeights(index, index);
5080             //this.layout();
5081             this.fireEvent("rowremoved", this, index, record);
5082         }
5083     },
5084     
5085     
5086     refreshRow : function(record){
5087         var ds = this.store, index;
5088         if(typeof record == 'number'){
5089             index = record;
5090             record = ds.getAt(index);
5091         }else{
5092             index = ds.indexOf(record);
5093         }
5094         this.insertRow(ds, index, true);
5095         this.onRemove(ds, record, index+1, true);
5096         //this.syncRowHeights(index, index);
5097         //this.layout();
5098         this.fireEvent("rowupdated", this, index, record);
5099     },
5100     
5101     insertRow : function(dm, rowIndex, isUpdate){
5102         
5103         if(!isUpdate){
5104             this.fireEvent("beforerowsinserted", this, rowIndex);
5105         }
5106             //var s = this.getScrollState();
5107         var row = this.renderRow(this.cm, this.store, rowIndex);
5108         // insert before rowIndex..
5109         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5110         Roo.log(e);
5111         
5112         var _this = this;
5113                 
5114         if(row.cellObjects.length){
5115             Roo.each(row.cellObjects, function(r){
5116                 _this.renderCellObject(r);
5117             })
5118         }
5119             
5120         if(!isUpdate){
5121             this.fireEvent("rowsinserted", this, rowIndex);
5122             //this.syncRowHeights(firstRow, lastRow);
5123             //this.stripeRows(firstRow);
5124             //this.layout();
5125         }
5126         
5127     },
5128     
5129     
5130     getRowDom : function(rowIndex)
5131     {
5132         // not sure if I need to check this.. but let's do it anyway..
5133         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5134                 this.mainBody.dom.rows[rowIndex] : false
5135     },
5136     // returns the object tree for a tr..
5137   
5138     
5139     renderRow : function(cm, ds, rowIndex) {
5140         
5141         var d = ds.getAt(rowIndex);
5142         
5143         var row = {
5144             tag : 'tr',
5145             cn : []
5146         };
5147             
5148         var cellObjects = [];
5149         
5150         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5151             var config = cm.config[i];
5152             
5153             var renderer = cm.getRenderer(i);
5154             var value = '';
5155             var id = false;
5156             
5157             if(typeof(renderer) !== 'undefined'){
5158                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5159             }
5160             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5161             // and are rendered into the cells after the row is rendered - using the id for the element.
5162             
5163             if(typeof(value) === 'object'){
5164                 id = Roo.id();
5165                 cellObjects.push({
5166                     container : id,
5167                     cfg : value 
5168                 })
5169             }
5170             
5171             var rowcfg = {
5172                 record: d,
5173                 rowIndex : rowIndex,
5174                 colIndex : i,
5175                 rowClass : ''
5176             }
5177
5178             this.fireEvent('rowclass', this, rowcfg);
5179             
5180             var td = {
5181                 tag: 'td',
5182                 cls : rowcfg.rowClass,
5183                 style: '',
5184                 html: (typeof(value) === 'object') ? '' : value
5185             };
5186             
5187             if (id) {
5188                 td.id = id;
5189             }
5190             
5191             if(typeof(config.hidden) != 'undefined' && config.hidden){
5192                 td.style += ' display:none;';
5193             }
5194             
5195             if(typeof(config.align) != 'undefined' && config.align.length){
5196                 td.style += ' text-align:' + config.align + ';';
5197             }
5198             
5199             if(typeof(config.width) != 'undefined'){
5200                 td.style += ' width:' +  config.width + 'px;';
5201             }
5202              
5203             row.cn.push(td);
5204            
5205         }
5206         
5207         row.cellObjects = cellObjects;
5208         
5209         return row;
5210           
5211     },
5212     
5213     
5214     
5215     onBeforeLoad : function()
5216     {
5217         //Roo.log('ds onBeforeLoad');
5218         
5219         //this.clear();
5220         
5221         //if(this.loadMask){
5222         //    this.maskEl.show();
5223         //}
5224     },
5225     
5226     clear : function()
5227     {
5228         this.el.select('tbody', true).first().dom.innerHTML = '';
5229     },
5230     
5231     getSelectionModel : function(){
5232         if(!this.selModel){
5233             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5234         }
5235         return this.selModel;
5236     },
5237     /*
5238      * Render the Roo.bootstrap object from renderder
5239      */
5240     renderCellObject : function(r)
5241     {
5242         var _this = this;
5243         
5244         var t = r.cfg.render(r.container);
5245         
5246         if(r.cfg.cn){
5247             Roo.each(r.cfg.cn, function(c){
5248                 var child = {
5249                     container: t.getChildContainer(),
5250                     cfg: c
5251                 }
5252                 _this.renderCellObject(child);
5253             })
5254         }
5255     }
5256    
5257 });
5258
5259  
5260
5261  /*
5262  * - LGPL
5263  *
5264  * table cell
5265  * 
5266  */
5267
5268 /**
5269  * @class Roo.bootstrap.TableCell
5270  * @extends Roo.bootstrap.Component
5271  * Bootstrap TableCell class
5272  * @cfg {String} html cell contain text
5273  * @cfg {String} cls cell class
5274  * @cfg {String} tag cell tag (td|th) default td
5275  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5276  * @cfg {String} align Aligns the content in a cell
5277  * @cfg {String} axis Categorizes cells
5278  * @cfg {String} bgcolor Specifies the background color of a cell
5279  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5280  * @cfg {Number} colspan Specifies the number of columns a cell should span
5281  * @cfg {String} headers Specifies one or more header cells a cell is related to
5282  * @cfg {Number} height Sets the height of a cell
5283  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5284  * @cfg {Number} rowspan Sets the number of rows a cell should span
5285  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5286  * @cfg {String} valign Vertical aligns the content in a cell
5287  * @cfg {Number} width Specifies the width of a cell
5288  * 
5289  * @constructor
5290  * Create a new TableCell
5291  * @param {Object} config The config object
5292  */
5293
5294 Roo.bootstrap.TableCell = function(config){
5295     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5296 };
5297
5298 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5299     
5300     html: false,
5301     cls: false,
5302     tag: false,
5303     abbr: false,
5304     align: false,
5305     axis: false,
5306     bgcolor: false,
5307     charoff: false,
5308     colspan: false,
5309     headers: false,
5310     height: false,
5311     nowrap: false,
5312     rowspan: false,
5313     scope: false,
5314     valign: false,
5315     width: false,
5316     
5317     
5318     getAutoCreate : function(){
5319         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5320         
5321         cfg = {
5322             tag: 'td'
5323         }
5324         
5325         if(this.tag){
5326             cfg.tag = this.tag;
5327         }
5328         
5329         if (this.html) {
5330             cfg.html=this.html
5331         }
5332         if (this.cls) {
5333             cfg.cls=this.cls
5334         }
5335         if (this.abbr) {
5336             cfg.abbr=this.abbr
5337         }
5338         if (this.align) {
5339             cfg.align=this.align
5340         }
5341         if (this.axis) {
5342             cfg.axis=this.axis
5343         }
5344         if (this.bgcolor) {
5345             cfg.bgcolor=this.bgcolor
5346         }
5347         if (this.charoff) {
5348             cfg.charoff=this.charoff
5349         }
5350         if (this.colspan) {
5351             cfg.colspan=this.colspan
5352         }
5353         if (this.headers) {
5354             cfg.headers=this.headers
5355         }
5356         if (this.height) {
5357             cfg.height=this.height
5358         }
5359         if (this.nowrap) {
5360             cfg.nowrap=this.nowrap
5361         }
5362         if (this.rowspan) {
5363             cfg.rowspan=this.rowspan
5364         }
5365         if (this.scope) {
5366             cfg.scope=this.scope
5367         }
5368         if (this.valign) {
5369             cfg.valign=this.valign
5370         }
5371         if (this.width) {
5372             cfg.width=this.width
5373         }
5374         
5375         
5376         return cfg;
5377     }
5378    
5379 });
5380
5381  
5382
5383  /*
5384  * - LGPL
5385  *
5386  * table row
5387  * 
5388  */
5389
5390 /**
5391  * @class Roo.bootstrap.TableRow
5392  * @extends Roo.bootstrap.Component
5393  * Bootstrap TableRow class
5394  * @cfg {String} cls row class
5395  * @cfg {String} align Aligns the content in a table row
5396  * @cfg {String} bgcolor Specifies a background color for a table row
5397  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5398  * @cfg {String} valign Vertical aligns the content in a table row
5399  * 
5400  * @constructor
5401  * Create a new TableRow
5402  * @param {Object} config The config object
5403  */
5404
5405 Roo.bootstrap.TableRow = function(config){
5406     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5407 };
5408
5409 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5410     
5411     cls: false,
5412     align: false,
5413     bgcolor: false,
5414     charoff: false,
5415     valign: false,
5416     
5417     getAutoCreate : function(){
5418         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5419         
5420         cfg = {
5421             tag: 'tr'
5422         }
5423             
5424         if(this.cls){
5425             cfg.cls = this.cls;
5426         }
5427         if(this.align){
5428             cfg.align = this.align;
5429         }
5430         if(this.bgcolor){
5431             cfg.bgcolor = this.bgcolor;
5432         }
5433         if(this.charoff){
5434             cfg.charoff = this.charoff;
5435         }
5436         if(this.valign){
5437             cfg.valign = this.valign;
5438         }
5439         
5440         return cfg;
5441     }
5442    
5443 });
5444
5445  
5446
5447  /*
5448  * - LGPL
5449  *
5450  * table body
5451  * 
5452  */
5453
5454 /**
5455  * @class Roo.bootstrap.TableBody
5456  * @extends Roo.bootstrap.Component
5457  * Bootstrap TableBody class
5458  * @cfg {String} cls element class
5459  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5460  * @cfg {String} align Aligns the content inside the element
5461  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5462  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5463  * 
5464  * @constructor
5465  * Create a new TableBody
5466  * @param {Object} config The config object
5467  */
5468
5469 Roo.bootstrap.TableBody = function(config){
5470     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5471 };
5472
5473 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5474     
5475     cls: false,
5476     tag: false,
5477     align: false,
5478     charoff: false,
5479     valign: false,
5480     
5481     getAutoCreate : function(){
5482         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5483         
5484         cfg = {
5485             tag: 'tbody'
5486         }
5487             
5488         if (this.cls) {
5489             cfg.cls=this.cls
5490         }
5491         if(this.tag){
5492             cfg.tag = this.tag;
5493         }
5494         
5495         if(this.align){
5496             cfg.align = this.align;
5497         }
5498         if(this.charoff){
5499             cfg.charoff = this.charoff;
5500         }
5501         if(this.valign){
5502             cfg.valign = this.valign;
5503         }
5504         
5505         return cfg;
5506     }
5507     
5508     
5509 //    initEvents : function()
5510 //    {
5511 //        
5512 //        if(!this.store){
5513 //            return;
5514 //        }
5515 //        
5516 //        this.store = Roo.factory(this.store, Roo.data);
5517 //        this.store.on('load', this.onLoad, this);
5518 //        
5519 //        this.store.load();
5520 //        
5521 //    },
5522 //    
5523 //    onLoad: function () 
5524 //    {   
5525 //        this.fireEvent('load', this);
5526 //    }
5527 //    
5528 //   
5529 });
5530
5531  
5532
5533  /*
5534  * Based on:
5535  * Ext JS Library 1.1.1
5536  * Copyright(c) 2006-2007, Ext JS, LLC.
5537  *
5538  * Originally Released Under LGPL - original licence link has changed is not relivant.
5539  *
5540  * Fork - LGPL
5541  * <script type="text/javascript">
5542  */
5543
5544 // as we use this in bootstrap.
5545 Roo.namespace('Roo.form');
5546  /**
5547  * @class Roo.form.Action
5548  * Internal Class used to handle form actions
5549  * @constructor
5550  * @param {Roo.form.BasicForm} el The form element or its id
5551  * @param {Object} config Configuration options
5552  */
5553
5554  
5555  
5556 // define the action interface
5557 Roo.form.Action = function(form, options){
5558     this.form = form;
5559     this.options = options || {};
5560 };
5561 /**
5562  * Client Validation Failed
5563  * @const 
5564  */
5565 Roo.form.Action.CLIENT_INVALID = 'client';
5566 /**
5567  * Server Validation Failed
5568  * @const 
5569  */
5570 Roo.form.Action.SERVER_INVALID = 'server';
5571  /**
5572  * Connect to Server Failed
5573  * @const 
5574  */
5575 Roo.form.Action.CONNECT_FAILURE = 'connect';
5576 /**
5577  * Reading Data from Server Failed
5578  * @const 
5579  */
5580 Roo.form.Action.LOAD_FAILURE = 'load';
5581
5582 Roo.form.Action.prototype = {
5583     type : 'default',
5584     failureType : undefined,
5585     response : undefined,
5586     result : undefined,
5587
5588     // interface method
5589     run : function(options){
5590
5591     },
5592
5593     // interface method
5594     success : function(response){
5595
5596     },
5597
5598     // interface method
5599     handleResponse : function(response){
5600
5601     },
5602
5603     // default connection failure
5604     failure : function(response){
5605         
5606         this.response = response;
5607         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5608         this.form.afterAction(this, false);
5609     },
5610
5611     processResponse : function(response){
5612         this.response = response;
5613         if(!response.responseText){
5614             return true;
5615         }
5616         this.result = this.handleResponse(response);
5617         return this.result;
5618     },
5619
5620     // utility functions used internally
5621     getUrl : function(appendParams){
5622         var url = this.options.url || this.form.url || this.form.el.dom.action;
5623         if(appendParams){
5624             var p = this.getParams();
5625             if(p){
5626                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5627             }
5628         }
5629         return url;
5630     },
5631
5632     getMethod : function(){
5633         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5634     },
5635
5636     getParams : function(){
5637         var bp = this.form.baseParams;
5638         var p = this.options.params;
5639         if(p){
5640             if(typeof p == "object"){
5641                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5642             }else if(typeof p == 'string' && bp){
5643                 p += '&' + Roo.urlEncode(bp);
5644             }
5645         }else if(bp){
5646             p = Roo.urlEncode(bp);
5647         }
5648         return p;
5649     },
5650
5651     createCallback : function(){
5652         return {
5653             success: this.success,
5654             failure: this.failure,
5655             scope: this,
5656             timeout: (this.form.timeout*1000),
5657             upload: this.form.fileUpload ? this.success : undefined
5658         };
5659     }
5660 };
5661
5662 Roo.form.Action.Submit = function(form, options){
5663     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5664 };
5665
5666 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5667     type : 'submit',
5668
5669     haveProgress : false,
5670     uploadComplete : false,
5671     
5672     // uploadProgress indicator.
5673     uploadProgress : function()
5674     {
5675         if (!this.form.progressUrl) {
5676             return;
5677         }
5678         
5679         if (!this.haveProgress) {
5680             Roo.MessageBox.progress("Uploading", "Uploading");
5681         }
5682         if (this.uploadComplete) {
5683            Roo.MessageBox.hide();
5684            return;
5685         }
5686         
5687         this.haveProgress = true;
5688    
5689         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5690         
5691         var c = new Roo.data.Connection();
5692         c.request({
5693             url : this.form.progressUrl,
5694             params: {
5695                 id : uid
5696             },
5697             method: 'GET',
5698             success : function(req){
5699                //console.log(data);
5700                 var rdata = false;
5701                 var edata;
5702                 try  {
5703                    rdata = Roo.decode(req.responseText)
5704                 } catch (e) {
5705                     Roo.log("Invalid data from server..");
5706                     Roo.log(edata);
5707                     return;
5708                 }
5709                 if (!rdata || !rdata.success) {
5710                     Roo.log(rdata);
5711                     Roo.MessageBox.alert(Roo.encode(rdata));
5712                     return;
5713                 }
5714                 var data = rdata.data;
5715                 
5716                 if (this.uploadComplete) {
5717                    Roo.MessageBox.hide();
5718                    return;
5719                 }
5720                    
5721                 if (data){
5722                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5723                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5724                     );
5725                 }
5726                 this.uploadProgress.defer(2000,this);
5727             },
5728        
5729             failure: function(data) {
5730                 Roo.log('progress url failed ');
5731                 Roo.log(data);
5732             },
5733             scope : this
5734         });
5735            
5736     },
5737     
5738     
5739     run : function()
5740     {
5741         // run get Values on the form, so it syncs any secondary forms.
5742         this.form.getValues();
5743         
5744         var o = this.options;
5745         var method = this.getMethod();
5746         var isPost = method == 'POST';
5747         if(o.clientValidation === false || this.form.isValid()){
5748             
5749             if (this.form.progressUrl) {
5750                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5751                     (new Date() * 1) + '' + Math.random());
5752                     
5753             } 
5754             
5755             
5756             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5757                 form:this.form.el.dom,
5758                 url:this.getUrl(!isPost),
5759                 method: method,
5760                 params:isPost ? this.getParams() : null,
5761                 isUpload: this.form.fileUpload
5762             }));
5763             
5764             this.uploadProgress();
5765
5766         }else if (o.clientValidation !== false){ // client validation failed
5767             this.failureType = Roo.form.Action.CLIENT_INVALID;
5768             this.form.afterAction(this, false);
5769         }
5770     },
5771
5772     success : function(response)
5773     {
5774         this.uploadComplete= true;
5775         if (this.haveProgress) {
5776             Roo.MessageBox.hide();
5777         }
5778         
5779         
5780         var result = this.processResponse(response);
5781         if(result === true || result.success){
5782             this.form.afterAction(this, true);
5783             return;
5784         }
5785         if(result.errors){
5786             this.form.markInvalid(result.errors);
5787             this.failureType = Roo.form.Action.SERVER_INVALID;
5788         }
5789         this.form.afterAction(this, false);
5790     },
5791     failure : function(response)
5792     {
5793         this.uploadComplete= true;
5794         if (this.haveProgress) {
5795             Roo.MessageBox.hide();
5796         }
5797         
5798         this.response = response;
5799         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5800         this.form.afterAction(this, false);
5801     },
5802     
5803     handleResponse : function(response){
5804         if(this.form.errorReader){
5805             var rs = this.form.errorReader.read(response);
5806             var errors = [];
5807             if(rs.records){
5808                 for(var i = 0, len = rs.records.length; i < len; i++) {
5809                     var r = rs.records[i];
5810                     errors[i] = r.data;
5811                 }
5812             }
5813             if(errors.length < 1){
5814                 errors = null;
5815             }
5816             return {
5817                 success : rs.success,
5818                 errors : errors
5819             };
5820         }
5821         var ret = false;
5822         try {
5823             ret = Roo.decode(response.responseText);
5824         } catch (e) {
5825             ret = {
5826                 success: false,
5827                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5828                 errors : []
5829             };
5830         }
5831         return ret;
5832         
5833     }
5834 });
5835
5836
5837 Roo.form.Action.Load = function(form, options){
5838     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5839     this.reader = this.form.reader;
5840 };
5841
5842 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5843     type : 'load',
5844
5845     run : function(){
5846         
5847         Roo.Ajax.request(Roo.apply(
5848                 this.createCallback(), {
5849                     method:this.getMethod(),
5850                     url:this.getUrl(false),
5851                     params:this.getParams()
5852         }));
5853     },
5854
5855     success : function(response){
5856         
5857         var result = this.processResponse(response);
5858         if(result === true || !result.success || !result.data){
5859             this.failureType = Roo.form.Action.LOAD_FAILURE;
5860             this.form.afterAction(this, false);
5861             return;
5862         }
5863         this.form.clearInvalid();
5864         this.form.setValues(result.data);
5865         this.form.afterAction(this, true);
5866     },
5867
5868     handleResponse : function(response){
5869         if(this.form.reader){
5870             var rs = this.form.reader.read(response);
5871             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5872             return {
5873                 success : rs.success,
5874                 data : data
5875             };
5876         }
5877         return Roo.decode(response.responseText);
5878     }
5879 });
5880
5881 Roo.form.Action.ACTION_TYPES = {
5882     'load' : Roo.form.Action.Load,
5883     'submit' : Roo.form.Action.Submit
5884 };/*
5885  * - LGPL
5886  *
5887  * form
5888  * 
5889  */
5890
5891 /**
5892  * @class Roo.bootstrap.Form
5893  * @extends Roo.bootstrap.Component
5894  * Bootstrap Form class
5895  * @cfg {String} method  GET | POST (default POST)
5896  * @cfg {String} labelAlign top | left (default top)
5897   * @cfg {String} align left  | right - for navbars
5898
5899  * 
5900  * @constructor
5901  * Create a new Form
5902  * @param {Object} config The config object
5903  */
5904
5905
5906 Roo.bootstrap.Form = function(config){
5907     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5908     this.addEvents({
5909         /**
5910          * @event clientvalidation
5911          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5912          * @param {Form} this
5913          * @param {Boolean} valid true if the form has passed client-side validation
5914          */
5915         clientvalidation: true,
5916         /**
5917          * @event beforeaction
5918          * Fires before any action is performed. Return false to cancel the action.
5919          * @param {Form} this
5920          * @param {Action} action The action to be performed
5921          */
5922         beforeaction: true,
5923         /**
5924          * @event actionfailed
5925          * Fires when an action fails.
5926          * @param {Form} this
5927          * @param {Action} action The action that failed
5928          */
5929         actionfailed : true,
5930         /**
5931          * @event actioncomplete
5932          * Fires when an action is completed.
5933          * @param {Form} this
5934          * @param {Action} action The action that completed
5935          */
5936         actioncomplete : true
5937     });
5938     
5939 };
5940
5941 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5942       
5943      /**
5944      * @cfg {String} method
5945      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5946      */
5947     method : 'POST',
5948     /**
5949      * @cfg {String} url
5950      * The URL to use for form actions if one isn't supplied in the action options.
5951      */
5952     /**
5953      * @cfg {Boolean} fileUpload
5954      * Set to true if this form is a file upload.
5955      */
5956      
5957     /**
5958      * @cfg {Object} baseParams
5959      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5960      */
5961       
5962     /**
5963      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5964      */
5965     timeout: 30,
5966     /**
5967      * @cfg {Sting} align (left|right) for navbar forms
5968      */
5969     align : 'left',
5970
5971     // private
5972     activeAction : null,
5973  
5974     /**
5975      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5976      * element by passing it or its id or mask the form itself by passing in true.
5977      * @type Mixed
5978      */
5979     waitMsgTarget : false,
5980     
5981      
5982     
5983     /**
5984      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5985      * element by passing it or its id or mask the form itself by passing in true.
5986      * @type Mixed
5987      */
5988     
5989     getAutoCreate : function(){
5990         
5991         var cfg = {
5992             tag: 'form',
5993             method : this.method || 'POST',
5994             id : this.id || Roo.id(),
5995             cls : ''
5996         }
5997         if (this.parent().xtype.match(/^Nav/)) {
5998             cfg.cls = 'navbar-form navbar-' + this.align;
5999             
6000         }
6001         
6002         if (this.labelAlign == 'left' ) {
6003             cfg.cls += ' form-horizontal';
6004         }
6005         
6006         
6007         return cfg;
6008     },
6009     initEvents : function()
6010     {
6011         this.el.on('submit', this.onSubmit, this);
6012         // this was added as random key presses on the form where triggering form submit.
6013         this.el.on('keypress', function(e) {
6014             if (e.getCharCode() != 13) {
6015                 return true;
6016             }
6017             // we might need to allow it for textareas.. and some other items.
6018             // check e.getTarget().
6019             
6020             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6021                 return true;
6022             }
6023         
6024             Roo.log("keypress blocked");
6025             
6026             e.preventDefault();
6027             return false;
6028         });
6029         
6030     },
6031     // private
6032     onSubmit : function(e){
6033         e.stopEvent();
6034     },
6035     
6036      /**
6037      * Returns true if client-side validation on the form is successful.
6038      * @return Boolean
6039      */
6040     isValid : function(){
6041         var items = this.getItems();
6042         var valid = true;
6043         items.each(function(f){
6044            if(!f.validate()){
6045                valid = false;
6046                
6047            }
6048         });
6049         return valid;
6050     },
6051     /**
6052      * Returns true if any fields in this form have changed since their original load.
6053      * @return Boolean
6054      */
6055     isDirty : function(){
6056         var dirty = false;
6057         var items = this.getItems();
6058         items.each(function(f){
6059            if(f.isDirty()){
6060                dirty = true;
6061                return false;
6062            }
6063            return true;
6064         });
6065         return dirty;
6066     },
6067      /**
6068      * Performs a predefined action (submit or load) or custom actions you define on this form.
6069      * @param {String} actionName The name of the action type
6070      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6071      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6072      * accept other config options):
6073      * <pre>
6074 Property          Type             Description
6075 ----------------  ---------------  ----------------------------------------------------------------------------------
6076 url               String           The url for the action (defaults to the form's url)
6077 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6078 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6079 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6080                                    validate the form on the client (defaults to false)
6081      * </pre>
6082      * @return {BasicForm} this
6083      */
6084     doAction : function(action, options){
6085         if(typeof action == 'string'){
6086             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6087         }
6088         if(this.fireEvent('beforeaction', this, action) !== false){
6089             this.beforeAction(action);
6090             action.run.defer(100, action);
6091         }
6092         return this;
6093     },
6094     
6095     // private
6096     beforeAction : function(action){
6097         var o = action.options;
6098         
6099         // not really supported yet.. ??
6100         
6101         //if(this.waitMsgTarget === true){
6102             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6103         //}else if(this.waitMsgTarget){
6104         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6105         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6106         //}else {
6107         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6108        // }
6109          
6110     },
6111
6112     // private
6113     afterAction : function(action, success){
6114         this.activeAction = null;
6115         var o = action.options;
6116         
6117         //if(this.waitMsgTarget === true){
6118             this.el.unmask();
6119         //}else if(this.waitMsgTarget){
6120         //    this.waitMsgTarget.unmask();
6121         //}else{
6122         //    Roo.MessageBox.updateProgress(1);
6123         //    Roo.MessageBox.hide();
6124        // }
6125         // 
6126         if(success){
6127             if(o.reset){
6128                 this.reset();
6129             }
6130             Roo.callback(o.success, o.scope, [this, action]);
6131             this.fireEvent('actioncomplete', this, action);
6132             
6133         }else{
6134             
6135             // failure condition..
6136             // we have a scenario where updates need confirming.
6137             // eg. if a locking scenario exists..
6138             // we look for { errors : { needs_confirm : true }} in the response.
6139             if (
6140                 (typeof(action.result) != 'undefined')  &&
6141                 (typeof(action.result.errors) != 'undefined')  &&
6142                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6143            ){
6144                 var _t = this;
6145                 Roo.log("not supported yet");
6146                  /*
6147                 
6148                 Roo.MessageBox.confirm(
6149                     "Change requires confirmation",
6150                     action.result.errorMsg,
6151                     function(r) {
6152                         if (r != 'yes') {
6153                             return;
6154                         }
6155                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6156                     }
6157                     
6158                 );
6159                 */
6160                 
6161                 
6162                 return;
6163             }
6164             
6165             Roo.callback(o.failure, o.scope, [this, action]);
6166             // show an error message if no failed handler is set..
6167             if (!this.hasListener('actionfailed')) {
6168                 Roo.log("need to add dialog support");
6169                 /*
6170                 Roo.MessageBox.alert("Error",
6171                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6172                         action.result.errorMsg :
6173                         "Saving Failed, please check your entries or try again"
6174                 );
6175                 */
6176             }
6177             
6178             this.fireEvent('actionfailed', this, action);
6179         }
6180         
6181     },
6182     /**
6183      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6184      * @param {String} id The value to search for
6185      * @return Field
6186      */
6187     findField : function(id){
6188         var items = this.getItems();
6189         var field = items.get(id);
6190         if(!field){
6191              items.each(function(f){
6192                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6193                     field = f;
6194                     return false;
6195                 }
6196                 return true;
6197             });
6198         }
6199         return field || null;
6200     },
6201      /**
6202      * Mark fields in this form invalid in bulk.
6203      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6204      * @return {BasicForm} this
6205      */
6206     markInvalid : function(errors){
6207         if(errors instanceof Array){
6208             for(var i = 0, len = errors.length; i < len; i++){
6209                 var fieldError = errors[i];
6210                 var f = this.findField(fieldError.id);
6211                 if(f){
6212                     f.markInvalid(fieldError.msg);
6213                 }
6214             }
6215         }else{
6216             var field, id;
6217             for(id in errors){
6218                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6219                     field.markInvalid(errors[id]);
6220                 }
6221             }
6222         }
6223         //Roo.each(this.childForms || [], function (f) {
6224         //    f.markInvalid(errors);
6225         //});
6226         
6227         return this;
6228     },
6229
6230     /**
6231      * Set values for fields in this form in bulk.
6232      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6233      * @return {BasicForm} this
6234      */
6235     setValues : function(values){
6236         if(values instanceof Array){ // array of objects
6237             for(var i = 0, len = values.length; i < len; i++){
6238                 var v = values[i];
6239                 var f = this.findField(v.id);
6240                 if(f){
6241                     f.setValue(v.value);
6242                     if(this.trackResetOnLoad){
6243                         f.originalValue = f.getValue();
6244                     }
6245                 }
6246             }
6247         }else{ // object hash
6248             var field, id;
6249             for(id in values){
6250                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6251                     
6252                     if (field.setFromData && 
6253                         field.valueField && 
6254                         field.displayField &&
6255                         // combos' with local stores can 
6256                         // be queried via setValue()
6257                         // to set their value..
6258                         (field.store && !field.store.isLocal)
6259                         ) {
6260                         // it's a combo
6261                         var sd = { };
6262                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6263                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6264                         field.setFromData(sd);
6265                         
6266                     } else {
6267                         field.setValue(values[id]);
6268                     }
6269                     
6270                     
6271                     if(this.trackResetOnLoad){
6272                         field.originalValue = field.getValue();
6273                     }
6274                 }
6275             }
6276         }
6277          
6278         //Roo.each(this.childForms || [], function (f) {
6279         //    f.setValues(values);
6280         //});
6281                 
6282         return this;
6283     },
6284
6285     /**
6286      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6287      * they are returned as an array.
6288      * @param {Boolean} asString
6289      * @return {Object}
6290      */
6291     getValues : function(asString){
6292         //if (this.childForms) {
6293             // copy values from the child forms
6294         //    Roo.each(this.childForms, function (f) {
6295         //        this.setValues(f.getValues());
6296         //    }, this);
6297         //}
6298         
6299         
6300         
6301         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6302         if(asString === true){
6303             return fs;
6304         }
6305         return Roo.urlDecode(fs);
6306     },
6307     
6308     /**
6309      * Returns the fields in this form as an object with key/value pairs. 
6310      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6311      * @return {Object}
6312      */
6313     getFieldValues : function(with_hidden)
6314     {
6315         var items = this.getItems();
6316         var ret = {};
6317         items.each(function(f){
6318             if (!f.getName()) {
6319                 return;
6320             }
6321             var v = f.getValue();
6322             if (f.inputType =='radio') {
6323                 if (typeof(ret[f.getName()]) == 'undefined') {
6324                     ret[f.getName()] = ''; // empty..
6325                 }
6326                 
6327                 if (!f.el.dom.checked) {
6328                     return;
6329                     
6330                 }
6331                 v = f.el.dom.value;
6332                 
6333             }
6334             
6335             // not sure if this supported any more..
6336             if ((typeof(v) == 'object') && f.getRawValue) {
6337                 v = f.getRawValue() ; // dates..
6338             }
6339             // combo boxes where name != hiddenName...
6340             if (f.name != f.getName()) {
6341                 ret[f.name] = f.getRawValue();
6342             }
6343             ret[f.getName()] = v;
6344         });
6345         
6346         return ret;
6347     },
6348
6349     /**
6350      * Clears all invalid messages in this form.
6351      * @return {BasicForm} this
6352      */
6353     clearInvalid : function(){
6354         var items = this.getItems();
6355         
6356         items.each(function(f){
6357            f.clearInvalid();
6358         });
6359         
6360         
6361         
6362         return this;
6363     },
6364
6365     /**
6366      * Resets this form.
6367      * @return {BasicForm} this
6368      */
6369     reset : function(){
6370         var items = this.getItems();
6371         items.each(function(f){
6372             f.reset();
6373         });
6374         
6375         Roo.each(this.childForms || [], function (f) {
6376             f.reset();
6377         });
6378        
6379         
6380         return this;
6381     },
6382     getItems : function()
6383     {
6384         var r=new Roo.util.MixedCollection(false, function(o){
6385             return o.id || (o.id = Roo.id());
6386         });
6387         var iter = function(el) {
6388             if (el.inputEl) {
6389                 r.add(el);
6390             }
6391             if (!el.items) {
6392                 return;
6393             }
6394             Roo.each(el.items,function(e) {
6395                 iter(e);
6396             });
6397             
6398             
6399         };
6400         iter(this);
6401         return r;
6402         
6403         
6404         
6405         
6406     }
6407     
6408 });
6409
6410  
6411 /*
6412  * Based on:
6413  * Ext JS Library 1.1.1
6414  * Copyright(c) 2006-2007, Ext JS, LLC.
6415  *
6416  * Originally Released Under LGPL - original licence link has changed is not relivant.
6417  *
6418  * Fork - LGPL
6419  * <script type="text/javascript">
6420  */
6421 /**
6422  * @class Roo.form.VTypes
6423  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6424  * @singleton
6425  */
6426 Roo.form.VTypes = function(){
6427     // closure these in so they are only created once.
6428     var alpha = /^[a-zA-Z_]+$/;
6429     var alphanum = /^[a-zA-Z0-9_]+$/;
6430     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6431     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6432
6433     // All these messages and functions are configurable
6434     return {
6435         /**
6436          * The function used to validate email addresses
6437          * @param {String} value The email address
6438          */
6439         'email' : function(v){
6440             return email.test(v);
6441         },
6442         /**
6443          * The error text to display when the email validation function returns false
6444          * @type String
6445          */
6446         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6447         /**
6448          * The keystroke filter mask to be applied on email input
6449          * @type RegExp
6450          */
6451         'emailMask' : /[a-z0-9_\.\-@]/i,
6452
6453         /**
6454          * The function used to validate URLs
6455          * @param {String} value The URL
6456          */
6457         'url' : function(v){
6458             return url.test(v);
6459         },
6460         /**
6461          * The error text to display when the url validation function returns false
6462          * @type String
6463          */
6464         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6465         
6466         /**
6467          * The function used to validate alpha values
6468          * @param {String} value The value
6469          */
6470         'alpha' : function(v){
6471             return alpha.test(v);
6472         },
6473         /**
6474          * The error text to display when the alpha validation function returns false
6475          * @type String
6476          */
6477         'alphaText' : 'This field should only contain letters and _',
6478         /**
6479          * The keystroke filter mask to be applied on alpha input
6480          * @type RegExp
6481          */
6482         'alphaMask' : /[a-z_]/i,
6483
6484         /**
6485          * The function used to validate alphanumeric values
6486          * @param {String} value The value
6487          */
6488         'alphanum' : function(v){
6489             return alphanum.test(v);
6490         },
6491         /**
6492          * The error text to display when the alphanumeric validation function returns false
6493          * @type String
6494          */
6495         'alphanumText' : 'This field should only contain letters, numbers and _',
6496         /**
6497          * The keystroke filter mask to be applied on alphanumeric input
6498          * @type RegExp
6499          */
6500         'alphanumMask' : /[a-z0-9_]/i
6501     };
6502 }();/*
6503  * - LGPL
6504  *
6505  * Input
6506  * 
6507  */
6508
6509 /**
6510  * @class Roo.bootstrap.Input
6511  * @extends Roo.bootstrap.Component
6512  * Bootstrap Input class
6513  * @cfg {Boolean} disabled is it disabled
6514  * @cfg {String} fieldLabel - the label associated
6515  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6516  * @cfg {String} name name of the input
6517  * @cfg {string} fieldLabel - the label associated
6518  * @cfg {string}  inputType - input / file submit ...
6519  * @cfg {string} placeholder - placeholder to put in text.
6520  * @cfg {string}  before - input group add on before
6521  * @cfg {string} after - input group add on after
6522  * @cfg {string} size - (lg|sm) or leave empty..
6523  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6524  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6525  * @cfg {Number} md colspan out of 12 for computer-sized screens
6526  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6527  * @cfg {string} value default value of the input
6528  * @cfg {Number} labelWidth set the width of label (0-12)
6529  * @cfg {String} labelAlign (top|left)
6530  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6531  * @cfg {String} align (left|center|right) Default left
6532  * 
6533  * 
6534  * @constructor
6535  * Create a new Input
6536  * @param {Object} config The config object
6537  */
6538
6539 Roo.bootstrap.Input = function(config){
6540     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6541    
6542         this.addEvents({
6543             /**
6544              * @event focus
6545              * Fires when this field receives input focus.
6546              * @param {Roo.form.Field} this
6547              */
6548             focus : true,
6549             /**
6550              * @event blur
6551              * Fires when this field loses input focus.
6552              * @param {Roo.form.Field} this
6553              */
6554             blur : true,
6555             /**
6556              * @event specialkey
6557              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6558              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6559              * @param {Roo.form.Field} this
6560              * @param {Roo.EventObject} e The event object
6561              */
6562             specialkey : true,
6563             /**
6564              * @event change
6565              * Fires just before the field blurs if the field value has changed.
6566              * @param {Roo.form.Field} this
6567              * @param {Mixed} newValue The new value
6568              * @param {Mixed} oldValue The original value
6569              */
6570             change : true,
6571             /**
6572              * @event invalid
6573              * Fires after the field has been marked as invalid.
6574              * @param {Roo.form.Field} this
6575              * @param {String} msg The validation message
6576              */
6577             invalid : true,
6578             /**
6579              * @event valid
6580              * Fires after the field has been validated with no errors.
6581              * @param {Roo.form.Field} this
6582              */
6583             valid : true,
6584              /**
6585              * @event keyup
6586              * Fires after the key up
6587              * @param {Roo.form.Field} this
6588              * @param {Roo.EventObject}  e The event Object
6589              */
6590             keyup : true
6591         });
6592 };
6593
6594 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6595      /**
6596      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6597       automatic validation (defaults to "keyup").
6598      */
6599     validationEvent : "keyup",
6600      /**
6601      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6602      */
6603     validateOnBlur : true,
6604     /**
6605      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6606      */
6607     validationDelay : 250,
6608      /**
6609      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6610      */
6611     focusClass : "x-form-focus",  // not needed???
6612     
6613        
6614     /**
6615      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6616      */
6617     invalidClass : "has-error",
6618     
6619     /**
6620      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6621      */
6622     selectOnFocus : false,
6623     
6624      /**
6625      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6626      */
6627     maskRe : null,
6628        /**
6629      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6630      */
6631     vtype : null,
6632     
6633       /**
6634      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6635      */
6636     disableKeyFilter : false,
6637     
6638        /**
6639      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6640      */
6641     disabled : false,
6642      /**
6643      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6644      */
6645     allowBlank : true,
6646     /**
6647      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6648      */
6649     blankText : "This field is required",
6650     
6651      /**
6652      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6653      */
6654     minLength : 0,
6655     /**
6656      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6657      */
6658     maxLength : Number.MAX_VALUE,
6659     /**
6660      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6661      */
6662     minLengthText : "The minimum length for this field is {0}",
6663     /**
6664      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6665      */
6666     maxLengthText : "The maximum length for this field is {0}",
6667   
6668     
6669     /**
6670      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6671      * If available, this function will be called only after the basic validators all return true, and will be passed the
6672      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6673      */
6674     validator : null,
6675     /**
6676      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6677      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6678      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6679      */
6680     regex : null,
6681     /**
6682      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6683      */
6684     regexText : "",
6685     
6686     
6687     
6688     fieldLabel : '',
6689     inputType : 'text',
6690     
6691     name : false,
6692     placeholder: false,
6693     before : false,
6694     after : false,
6695     size : false,
6696     // private
6697     hasFocus : false,
6698     preventMark: false,
6699     isFormField : true,
6700     value : '',
6701     labelWidth : 2,
6702     labelAlign : false,
6703     readOnly : false,
6704     align : false,
6705     formatedValue : false,
6706     
6707     parentLabelAlign : function()
6708     {
6709         var parent = this;
6710         while (parent.parent()) {
6711             parent = parent.parent();
6712             if (typeof(parent.labelAlign) !='undefined') {
6713                 return parent.labelAlign;
6714             }
6715         }
6716         return 'left';
6717         
6718     },
6719     
6720     getAutoCreate : function(){
6721         
6722         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6723         
6724         var id = Roo.id();
6725         
6726         var cfg = {};
6727         
6728         if(this.inputType != 'hidden'){
6729             cfg.cls = 'form-group' //input-group
6730         }
6731         
6732         var input =  {
6733             tag: 'input',
6734             id : id,
6735             type : this.inputType,
6736             value : this.value,
6737             cls : 'form-control',
6738             placeholder : this.placeholder || ''
6739             
6740         };
6741         
6742         if(this.align){
6743             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6744         }
6745         
6746         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6747             input.maxLength = this.maxLength;
6748         }
6749         
6750         if (this.disabled) {
6751             input.disabled=true;
6752         }
6753         
6754         if (this.readOnly) {
6755             input.readonly=true;
6756         }
6757         
6758         if (this.name) {
6759             input.name = this.name;
6760         }
6761         if (this.size) {
6762             input.cls += ' input-' + this.size;
6763         }
6764         var settings=this;
6765         ['xs','sm','md','lg'].map(function(size){
6766             if (settings[size]) {
6767                 cfg.cls += ' col-' + size + '-' + settings[size];
6768             }
6769         });
6770         
6771         var inputblock = input;
6772         
6773         if (this.before || this.after) {
6774             
6775             inputblock = {
6776                 cls : 'input-group',
6777                 cn :  [] 
6778             };
6779             if (this.before && typeof(this.before) == 'string') {
6780                 
6781                 inputblock.cn.push({
6782                     tag :'span',
6783                     cls : 'roo-input-before input-group-addon',
6784                     html : this.before
6785                 });
6786             }
6787             if (this.before && typeof(this.before) == 'object') {
6788                 this.before = Roo.factory(this.before);
6789                 Roo.log(this.before);
6790                 inputblock.cn.push({
6791                     tag :'span',
6792                     cls : 'roo-input-before input-group-' +
6793                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6794                 });
6795             }
6796             
6797             inputblock.cn.push(input);
6798             
6799             if (this.after && typeof(this.after) == 'string') {
6800                 inputblock.cn.push({
6801                     tag :'span',
6802                     cls : 'roo-input-after input-group-addon',
6803                     html : this.after
6804                 });
6805             }
6806             if (this.after && typeof(this.after) == 'object') {
6807                 this.after = Roo.factory(this.after);
6808                 Roo.log(this.after);
6809                 inputblock.cn.push({
6810                     tag :'span',
6811                     cls : 'roo-input-after input-group-' +
6812                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6813                 });
6814             }
6815         };
6816         
6817         if (align ==='left' && this.fieldLabel.length) {
6818                 Roo.log("left and has label");
6819                 cfg.cn = [
6820                     
6821                     {
6822                         tag: 'label',
6823                         'for' :  id,
6824                         cls : 'control-label col-sm-' + this.labelWidth,
6825                         html : this.fieldLabel
6826                         
6827                     },
6828                     {
6829                         cls : "col-sm-" + (12 - this.labelWidth), 
6830                         cn: [
6831                             inputblock
6832                         ]
6833                     }
6834                     
6835                 ];
6836         } else if ( this.fieldLabel.length) {
6837                 Roo.log(" label");
6838                  cfg.cn = [
6839                    
6840                     {
6841                         tag: 'label',
6842                         //cls : 'input-group-addon',
6843                         html : this.fieldLabel
6844                         
6845                     },
6846                     
6847                     inputblock
6848                     
6849                 ];
6850
6851         } else {
6852             
6853                 Roo.log(" no label && no align");
6854                 cfg.cn = [
6855                     
6856                         inputblock
6857                     
6858                 ];
6859                 
6860                 
6861         };
6862         Roo.log('input-parentType: ' + this.parentType);
6863         
6864         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6865            cfg.cls += ' navbar-form';
6866            Roo.log(cfg);
6867         }
6868         
6869         return cfg;
6870         
6871     },
6872     /**
6873      * return the real input element.
6874      */
6875     inputEl: function ()
6876     {
6877         return this.el.select('input.form-control',true).first();
6878     },
6879     setDisabled : function(v)
6880     {
6881         var i  = this.inputEl().dom;
6882         if (!v) {
6883             i.removeAttribute('disabled');
6884             return;
6885             
6886         }
6887         i.setAttribute('disabled','true');
6888     },
6889     initEvents : function()
6890     {
6891         
6892         this.inputEl().on("keydown" , this.fireKey,  this);
6893         this.inputEl().on("focus", this.onFocus,  this);
6894         this.inputEl().on("blur", this.onBlur,  this);
6895         
6896         this.inputEl().relayEvent('keyup', this);
6897
6898         // reference to original value for reset
6899         this.originalValue = this.getValue();
6900         //Roo.form.TextField.superclass.initEvents.call(this);
6901         if(this.validationEvent == 'keyup'){
6902             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6903             this.inputEl().on('keyup', this.filterValidation, this);
6904         }
6905         else if(this.validationEvent !== false){
6906             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6907         }
6908         
6909         if(this.selectOnFocus){
6910             this.on("focus", this.preFocus, this);
6911             
6912         }
6913         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6914             this.inputEl().on("keypress", this.filterKeys, this);
6915         }
6916        /* if(this.grow){
6917             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6918             this.el.on("click", this.autoSize,  this);
6919         }
6920         */
6921         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6922             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6923         }
6924         
6925         if (typeof(this.before) == 'object') {
6926             this.before.render(this.el.select('.roo-input-before',true).first());
6927         }
6928         if (typeof(this.after) == 'object') {
6929             this.after.render(this.el.select('.roo-input-after',true).first());
6930         }
6931         
6932         
6933     },
6934     filterValidation : function(e){
6935         if(!e.isNavKeyPress()){
6936             this.validationTask.delay(this.validationDelay);
6937         }
6938     },
6939      /**
6940      * Validates the field value
6941      * @return {Boolean} True if the value is valid, else false
6942      */
6943     validate : function(){
6944         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6945         if(this.disabled || this.validateValue(this.getRawValue())){
6946             this.clearInvalid();
6947             return true;
6948         }
6949         return false;
6950     },
6951     
6952     
6953     /**
6954      * Validates a value according to the field's validation rules and marks the field as invalid
6955      * if the validation fails
6956      * @param {Mixed} value The value to validate
6957      * @return {Boolean} True if the value is valid, else false
6958      */
6959     validateValue : function(value){
6960         if(value.length < 1)  { // if it's blank
6961              if(this.allowBlank){
6962                 this.clearInvalid();
6963                 return true;
6964              }else{
6965                 this.markInvalid(this.blankText);
6966                 return false;
6967              }
6968         }
6969         if(value.length < this.minLength){
6970             this.markInvalid(String.format(this.minLengthText, this.minLength));
6971             return false;
6972         }
6973         if(value.length > this.maxLength){
6974             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6975             return false;
6976         }
6977         if(this.vtype){
6978             var vt = Roo.form.VTypes;
6979             if(!vt[this.vtype](value, this)){
6980                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6981                 return false;
6982             }
6983         }
6984         if(typeof this.validator == "function"){
6985             var msg = this.validator(value);
6986             if(msg !== true){
6987                 this.markInvalid(msg);
6988                 return false;
6989             }
6990         }
6991         if(this.regex && !this.regex.test(value)){
6992             this.markInvalid(this.regexText);
6993             return false;
6994         }
6995         return true;
6996     },
6997
6998     
6999     
7000      // private
7001     fireKey : function(e){
7002         //Roo.log('field ' + e.getKey());
7003         if(e.isNavKeyPress()){
7004             this.fireEvent("specialkey", this, e);
7005         }
7006     },
7007     focus : function (selectText){
7008         if(this.rendered){
7009             this.inputEl().focus();
7010             if(selectText === true){
7011                 this.inputEl().dom.select();
7012             }
7013         }
7014         return this;
7015     } ,
7016     
7017     onFocus : function(){
7018         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7019            // this.el.addClass(this.focusClass);
7020         }
7021         if(!this.hasFocus){
7022             this.hasFocus = true;
7023             this.startValue = this.getValue();
7024             this.fireEvent("focus", this);
7025         }
7026     },
7027     
7028     beforeBlur : Roo.emptyFn,
7029
7030     
7031     // private
7032     onBlur : function(){
7033         this.beforeBlur();
7034         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7035             //this.el.removeClass(this.focusClass);
7036         }
7037         this.hasFocus = false;
7038         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7039             this.validate();
7040         }
7041         var v = this.getValue();
7042         if(String(v) !== String(this.startValue)){
7043             this.fireEvent('change', this, v, this.startValue);
7044         }
7045         this.fireEvent("blur", this);
7046     },
7047     
7048     /**
7049      * Resets the current field value to the originally loaded value and clears any validation messages
7050      */
7051     reset : function(){
7052         this.setValue(this.originalValue);
7053         this.clearInvalid();
7054     },
7055      /**
7056      * Returns the name of the field
7057      * @return {Mixed} name The name field
7058      */
7059     getName: function(){
7060         return this.name;
7061     },
7062      /**
7063      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7064      * @return {Mixed} value The field value
7065      */
7066     getValue : function(){
7067         
7068         var v = this.inputEl().getValue();
7069         
7070         return v;
7071     },
7072     /**
7073      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7074      * @return {Mixed} value The field value
7075      */
7076     getRawValue : function(){
7077         var v = this.inputEl().getValue();
7078         
7079         return v;
7080     },
7081     
7082     /**
7083      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7084      * @param {Mixed} value The value to set
7085      */
7086     setRawValue : function(v){
7087         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7088     },
7089     
7090     selectText : function(start, end){
7091         var v = this.getRawValue();
7092         if(v.length > 0){
7093             start = start === undefined ? 0 : start;
7094             end = end === undefined ? v.length : end;
7095             var d = this.inputEl().dom;
7096             if(d.setSelectionRange){
7097                 d.setSelectionRange(start, end);
7098             }else if(d.createTextRange){
7099                 var range = d.createTextRange();
7100                 range.moveStart("character", start);
7101                 range.moveEnd("character", v.length-end);
7102                 range.select();
7103             }
7104         }
7105     },
7106     
7107     /**
7108      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7109      * @param {Mixed} value The value to set
7110      */
7111     setValue : function(v){
7112         this.value = v;
7113         if(this.rendered){
7114             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7115             this.validate();
7116         }
7117     },
7118     
7119     /*
7120     processValue : function(value){
7121         if(this.stripCharsRe){
7122             var newValue = value.replace(this.stripCharsRe, '');
7123             if(newValue !== value){
7124                 this.setRawValue(newValue);
7125                 return newValue;
7126             }
7127         }
7128         return value;
7129     },
7130   */
7131     preFocus : function(){
7132         
7133         if(this.selectOnFocus){
7134             this.inputEl().dom.select();
7135         }
7136     },
7137     filterKeys : function(e){
7138         var k = e.getKey();
7139         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7140             return;
7141         }
7142         var c = e.getCharCode(), cc = String.fromCharCode(c);
7143         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7144             return;
7145         }
7146         if(!this.maskRe.test(cc)){
7147             e.stopEvent();
7148         }
7149     },
7150      /**
7151      * Clear any invalid styles/messages for this field
7152      */
7153     clearInvalid : function(){
7154         
7155         if(!this.el || this.preventMark){ // not rendered
7156             return;
7157         }
7158         this.el.removeClass(this.invalidClass);
7159         /*
7160         switch(this.msgTarget){
7161             case 'qtip':
7162                 this.el.dom.qtip = '';
7163                 break;
7164             case 'title':
7165                 this.el.dom.title = '';
7166                 break;
7167             case 'under':
7168                 if(this.errorEl){
7169                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7170                 }
7171                 break;
7172             case 'side':
7173                 if(this.errorIcon){
7174                     this.errorIcon.dom.qtip = '';
7175                     this.errorIcon.hide();
7176                     this.un('resize', this.alignErrorIcon, this);
7177                 }
7178                 break;
7179             default:
7180                 var t = Roo.getDom(this.msgTarget);
7181                 t.innerHTML = '';
7182                 t.style.display = 'none';
7183                 break;
7184         }
7185         */
7186         this.fireEvent('valid', this);
7187     },
7188      /**
7189      * Mark this field as invalid
7190      * @param {String} msg The validation message
7191      */
7192     markInvalid : function(msg){
7193         if(!this.el  || this.preventMark){ // not rendered
7194             return;
7195         }
7196         this.el.addClass(this.invalidClass);
7197         /*
7198         msg = msg || this.invalidText;
7199         switch(this.msgTarget){
7200             case 'qtip':
7201                 this.el.dom.qtip = msg;
7202                 this.el.dom.qclass = 'x-form-invalid-tip';
7203                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7204                     Roo.QuickTips.enable();
7205                 }
7206                 break;
7207             case 'title':
7208                 this.el.dom.title = msg;
7209                 break;
7210             case 'under':
7211                 if(!this.errorEl){
7212                     var elp = this.el.findParent('.x-form-element', 5, true);
7213                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7214                     this.errorEl.setWidth(elp.getWidth(true)-20);
7215                 }
7216                 this.errorEl.update(msg);
7217                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7218                 break;
7219             case 'side':
7220                 if(!this.errorIcon){
7221                     var elp = this.el.findParent('.x-form-element', 5, true);
7222                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7223                 }
7224                 this.alignErrorIcon();
7225                 this.errorIcon.dom.qtip = msg;
7226                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7227                 this.errorIcon.show();
7228                 this.on('resize', this.alignErrorIcon, this);
7229                 break;
7230             default:
7231                 var t = Roo.getDom(this.msgTarget);
7232                 t.innerHTML = msg;
7233                 t.style.display = this.msgDisplay;
7234                 break;
7235         }
7236         */
7237         this.fireEvent('invalid', this, msg);
7238     },
7239     // private
7240     SafariOnKeyDown : function(event)
7241     {
7242         // this is a workaround for a password hang bug on chrome/ webkit.
7243         
7244         var isSelectAll = false;
7245         
7246         if(this.inputEl().dom.selectionEnd > 0){
7247             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7248         }
7249         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7250             event.preventDefault();
7251             this.setValue('');
7252             return;
7253         }
7254         
7255         if(isSelectAll){ // backspace and delete key
7256             
7257             event.preventDefault();
7258             // this is very hacky as keydown always get's upper case.
7259             //
7260             var cc = String.fromCharCode(event.getCharCode());
7261             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7262             
7263         }
7264     },
7265     adjustWidth : function(tag, w){
7266         tag = tag.toLowerCase();
7267         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7268             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7269                 if(tag == 'input'){
7270                     return w + 2;
7271                 }
7272                 if(tag == 'textarea'){
7273                     return w-2;
7274                 }
7275             }else if(Roo.isOpera){
7276                 if(tag == 'input'){
7277                     return w + 2;
7278                 }
7279                 if(tag == 'textarea'){
7280                     return w-2;
7281                 }
7282             }
7283         }
7284         return w;
7285     }
7286     
7287 });
7288
7289  
7290 /*
7291  * - LGPL
7292  *
7293  * Input
7294  * 
7295  */
7296
7297 /**
7298  * @class Roo.bootstrap.TextArea
7299  * @extends Roo.bootstrap.Input
7300  * Bootstrap TextArea class
7301  * @cfg {Number} cols Specifies the visible width of a text area
7302  * @cfg {Number} rows Specifies the visible number of lines in a text area
7303  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7304  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7305  * @cfg {string} html text
7306  * 
7307  * @constructor
7308  * Create a new TextArea
7309  * @param {Object} config The config object
7310  */
7311
7312 Roo.bootstrap.TextArea = function(config){
7313     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7314    
7315 };
7316
7317 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7318      
7319     cols : false,
7320     rows : 5,
7321     readOnly : false,
7322     warp : 'soft',
7323     resize : false,
7324     value: false,
7325     html: false,
7326     
7327     getAutoCreate : function(){
7328         
7329         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7330         
7331         var id = Roo.id();
7332         
7333         var cfg = {};
7334         
7335         var input =  {
7336             tag: 'textarea',
7337             id : id,
7338             warp : this.warp,
7339             rows : this.rows,
7340             value : this.value || '',
7341             html: this.html || '',
7342             cls : 'form-control',
7343             placeholder : this.placeholder || '' 
7344             
7345         };
7346         
7347         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7348             input.maxLength = this.maxLength;
7349         }
7350         
7351         if(this.resize){
7352             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7353         }
7354         
7355         if(this.cols){
7356             input.cols = this.cols;
7357         }
7358         
7359         if (this.readOnly) {
7360             input.readonly = true;
7361         }
7362         
7363         if (this.name) {
7364             input.name = this.name;
7365         }
7366         
7367         if (this.size) {
7368             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7369         }
7370         
7371         var settings=this;
7372         ['xs','sm','md','lg'].map(function(size){
7373             if (settings[size]) {
7374                 cfg.cls += ' col-' + size + '-' + settings[size];
7375             }
7376         });
7377         
7378         var inputblock = input;
7379         
7380         if (this.before || this.after) {
7381             
7382             inputblock = {
7383                 cls : 'input-group',
7384                 cn :  [] 
7385             };
7386             if (this.before) {
7387                 inputblock.cn.push({
7388                     tag :'span',
7389                     cls : 'input-group-addon',
7390                     html : this.before
7391                 });
7392             }
7393             inputblock.cn.push(input);
7394             if (this.after) {
7395                 inputblock.cn.push({
7396                     tag :'span',
7397                     cls : 'input-group-addon',
7398                     html : this.after
7399                 });
7400             }
7401             
7402         }
7403         
7404         if (align ==='left' && this.fieldLabel.length) {
7405                 Roo.log("left and has label");
7406                 cfg.cn = [
7407                     
7408                     {
7409                         tag: 'label',
7410                         'for' :  id,
7411                         cls : 'control-label col-sm-' + this.labelWidth,
7412                         html : this.fieldLabel
7413                         
7414                     },
7415                     {
7416                         cls : "col-sm-" + (12 - this.labelWidth), 
7417                         cn: [
7418                             inputblock
7419                         ]
7420                     }
7421                     
7422                 ];
7423         } else if ( this.fieldLabel.length) {
7424                 Roo.log(" label");
7425                  cfg.cn = [
7426                    
7427                     {
7428                         tag: 'label',
7429                         //cls : 'input-group-addon',
7430                         html : this.fieldLabel
7431                         
7432                     },
7433                     
7434                     inputblock
7435                     
7436                 ];
7437
7438         } else {
7439             
7440                    Roo.log(" no label && no align");
7441                 cfg.cn = [
7442                     
7443                         inputblock
7444                     
7445                 ];
7446                 
7447                 
7448         }
7449         
7450         if (this.disabled) {
7451             input.disabled=true;
7452         }
7453         
7454         return cfg;
7455         
7456     },
7457     /**
7458      * return the real textarea element.
7459      */
7460     inputEl: function ()
7461     {
7462         return this.el.select('textarea.form-control',true).first();
7463     }
7464 });
7465
7466  
7467 /*
7468  * - LGPL
7469  *
7470  * trigger field - base class for combo..
7471  * 
7472  */
7473  
7474 /**
7475  * @class Roo.bootstrap.TriggerField
7476  * @extends Roo.bootstrap.Input
7477  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7478  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7479  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7480  * for which you can provide a custom implementation.  For example:
7481  * <pre><code>
7482 var trigger = new Roo.bootstrap.TriggerField();
7483 trigger.onTriggerClick = myTriggerFn;
7484 trigger.applyTo('my-field');
7485 </code></pre>
7486  *
7487  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7488  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7489  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7490  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7491  * @constructor
7492  * Create a new TriggerField.
7493  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7494  * to the base TextField)
7495  */
7496 Roo.bootstrap.TriggerField = function(config){
7497     this.mimicing = false;
7498     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7499 };
7500
7501 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7502     /**
7503      * @cfg {String} triggerClass A CSS class to apply to the trigger
7504      */
7505      /**
7506      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7507      */
7508     hideTrigger:false,
7509
7510     /** @cfg {Boolean} grow @hide */
7511     /** @cfg {Number} growMin @hide */
7512     /** @cfg {Number} growMax @hide */
7513
7514     /**
7515      * @hide 
7516      * @method
7517      */
7518     autoSize: Roo.emptyFn,
7519     // private
7520     monitorTab : true,
7521     // private
7522     deferHeight : true,
7523
7524     
7525     actionMode : 'wrap',
7526     
7527     
7528     
7529     getAutoCreate : function(){
7530        
7531         var parent = this.parent();
7532         
7533         var align = this.labelAlign || this.parentLabelAlign();
7534         
7535         var id = Roo.id();
7536         
7537         var cfg = {
7538             cls: 'form-group' //input-group
7539         };
7540         
7541         
7542         var input =  {
7543             tag: 'input',
7544             id : id,
7545             type : this.inputType,
7546             cls : 'form-control',
7547             autocomplete: 'off',
7548             placeholder : this.placeholder || '' 
7549             
7550         };
7551         if (this.name) {
7552             input.name = this.name;
7553         }
7554         if (this.size) {
7555             input.cls += ' input-' + this.size;
7556         }
7557         
7558         if (this.disabled) {
7559             input.disabled=true;
7560         }
7561         
7562         var inputblock = input;
7563         
7564         if (this.before || this.after) {
7565             
7566             inputblock = {
7567                 cls : 'input-group',
7568                 cn :  [] 
7569             };
7570             if (this.before) {
7571                 inputblock.cn.push({
7572                     tag :'span',
7573                     cls : 'input-group-addon',
7574                     html : this.before
7575                 });
7576             }
7577             inputblock.cn.push(input);
7578             if (this.after) {
7579                 inputblock.cn.push({
7580                     tag :'span',
7581                     cls : 'input-group-addon',
7582                     html : this.after
7583                 });
7584             }
7585             
7586         };
7587         
7588         var box = {
7589             tag: 'div',
7590             cn: [
7591                 {
7592                     tag: 'input',
7593                     type : 'hidden',
7594                     cls: 'form-hidden-field'
7595                 },
7596                 inputblock
7597             ]
7598             
7599         };
7600         
7601         if(this.multiple){
7602             Roo.log('multiple');
7603             
7604             box = {
7605                 tag: 'div',
7606                 cn: [
7607                     {
7608                         tag: 'input',
7609                         type : 'hidden',
7610                         cls: 'form-hidden-field'
7611                     },
7612                     {
7613                         tag: 'ul',
7614                         cls: 'select2-choices',
7615                         cn:[
7616                             {
7617                                 tag: 'li',
7618                                 cls: 'select2-search-field',
7619                                 cn: [
7620
7621                                     inputblock
7622                                 ]
7623                             }
7624                         ]
7625                     }
7626                 ]
7627             }
7628         };
7629         
7630         var combobox = {
7631             cls: 'select2-container input-group',
7632             cn: [
7633                 box,
7634                 {
7635                     tag: 'ul',
7636                     cls: 'typeahead typeahead-long dropdown-menu',
7637                     style: 'display:none'
7638                 }
7639             ]
7640         };
7641         
7642         if(!this.multiple){
7643             combobox.cn.push({
7644                 tag :'span',
7645                 cls : 'input-group-addon btn dropdown-toggle',
7646                 cn : [
7647                     {
7648                         tag: 'span',
7649                         cls: 'caret'
7650                     },
7651                     {
7652                         tag: 'span',
7653                         cls: 'combobox-clear',
7654                         cn  : [
7655                             {
7656                                 tag : 'i',
7657                                 cls: 'icon-remove'
7658                             }
7659                         ]
7660                     }
7661                 ]
7662
7663             })
7664         }
7665         
7666         if(this.multiple){
7667             combobox.cls += ' select2-container-multi';
7668         }
7669         
7670         if (align ==='left' && this.fieldLabel.length) {
7671             
7672                 Roo.log("left and has label");
7673                 cfg.cn = [
7674                     
7675                     {
7676                         tag: 'label',
7677                         'for' :  id,
7678                         cls : 'control-label col-sm-' + this.labelWidth,
7679                         html : this.fieldLabel
7680                         
7681                     },
7682                     {
7683                         cls : "col-sm-" + (12 - this.labelWidth), 
7684                         cn: [
7685                             combobox
7686                         ]
7687                     }
7688                     
7689                 ];
7690         } else if ( this.fieldLabel.length) {
7691                 Roo.log(" label");
7692                  cfg.cn = [
7693                    
7694                     {
7695                         tag: 'label',
7696                         //cls : 'input-group-addon',
7697                         html : this.fieldLabel
7698                         
7699                     },
7700                     
7701                     combobox
7702                     
7703                 ];
7704
7705         } else {
7706             
7707                 Roo.log(" no label && no align");
7708                 cfg = combobox
7709                      
7710                 
7711         }
7712          
7713         var settings=this;
7714         ['xs','sm','md','lg'].map(function(size){
7715             if (settings[size]) {
7716                 cfg.cls += ' col-' + size + '-' + settings[size];
7717             }
7718         });
7719         
7720         return cfg;
7721         
7722     },
7723     
7724     
7725     
7726     // private
7727     onResize : function(w, h){
7728 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7729 //        if(typeof w == 'number'){
7730 //            var x = w - this.trigger.getWidth();
7731 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7732 //            this.trigger.setStyle('left', x+'px');
7733 //        }
7734     },
7735
7736     // private
7737     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7738
7739     // private
7740     getResizeEl : function(){
7741         return this.inputEl();
7742     },
7743
7744     // private
7745     getPositionEl : function(){
7746         return this.inputEl();
7747     },
7748
7749     // private
7750     alignErrorIcon : function(){
7751         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7752     },
7753
7754     // private
7755     initEvents : function(){
7756         
7757         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7758         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7759         if(!this.multiple){
7760             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7761             if(this.hideTrigger){
7762                 this.trigger.setDisplayed(false);
7763             }
7764             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7765         }
7766         
7767         if(this.multiple){
7768             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7769         }
7770         
7771         //this.trigger.addClassOnOver('x-form-trigger-over');
7772         //this.trigger.addClassOnClick('x-form-trigger-click');
7773         
7774         //if(!this.width){
7775         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7776         //}
7777     },
7778
7779     // private
7780     initTrigger : function(){
7781        
7782     },
7783
7784     // private
7785     onDestroy : function(){
7786         if(this.trigger){
7787             this.trigger.removeAllListeners();
7788           //  this.trigger.remove();
7789         }
7790         //if(this.wrap){
7791         //    this.wrap.remove();
7792         //}
7793         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7794     },
7795
7796     // private
7797     onFocus : function(){
7798         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7799         /*
7800         if(!this.mimicing){
7801             this.wrap.addClass('x-trigger-wrap-focus');
7802             this.mimicing = true;
7803             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7804             if(this.monitorTab){
7805                 this.el.on("keydown", this.checkTab, this);
7806             }
7807         }
7808         */
7809     },
7810
7811     // private
7812     checkTab : function(e){
7813         if(e.getKey() == e.TAB){
7814             this.triggerBlur();
7815         }
7816     },
7817
7818     // private
7819     onBlur : function(){
7820         // do nothing
7821     },
7822
7823     // private
7824     mimicBlur : function(e, t){
7825         /*
7826         if(!this.wrap.contains(t) && this.validateBlur()){
7827             this.triggerBlur();
7828         }
7829         */
7830     },
7831
7832     // private
7833     triggerBlur : function(){
7834         this.mimicing = false;
7835         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7836         if(this.monitorTab){
7837             this.el.un("keydown", this.checkTab, this);
7838         }
7839         //this.wrap.removeClass('x-trigger-wrap-focus');
7840         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7841     },
7842
7843     // private
7844     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7845     validateBlur : function(e, t){
7846         return true;
7847     },
7848
7849     // private
7850     onDisable : function(){
7851         this.inputEl().dom.disabled = true;
7852         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7853         //if(this.wrap){
7854         //    this.wrap.addClass('x-item-disabled');
7855         //}
7856     },
7857
7858     // private
7859     onEnable : function(){
7860         this.inputEl().dom.disabled = false;
7861         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7862         //if(this.wrap){
7863         //    this.el.removeClass('x-item-disabled');
7864         //}
7865     },
7866
7867     // private
7868     onShow : function(){
7869         var ae = this.getActionEl();
7870         
7871         if(ae){
7872             ae.dom.style.display = '';
7873             ae.dom.style.visibility = 'visible';
7874         }
7875     },
7876
7877     // private
7878     
7879     onHide : function(){
7880         var ae = this.getActionEl();
7881         ae.dom.style.display = 'none';
7882     },
7883
7884     /**
7885      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7886      * by an implementing function.
7887      * @method
7888      * @param {EventObject} e
7889      */
7890     onTriggerClick : Roo.emptyFn
7891 });
7892  /*
7893  * Based on:
7894  * Ext JS Library 1.1.1
7895  * Copyright(c) 2006-2007, Ext JS, LLC.
7896  *
7897  * Originally Released Under LGPL - original licence link has changed is not relivant.
7898  *
7899  * Fork - LGPL
7900  * <script type="text/javascript">
7901  */
7902
7903
7904 /**
7905  * @class Roo.data.SortTypes
7906  * @singleton
7907  * Defines the default sorting (casting?) comparison functions used when sorting data.
7908  */
7909 Roo.data.SortTypes = {
7910     /**
7911      * Default sort that does nothing
7912      * @param {Mixed} s The value being converted
7913      * @return {Mixed} The comparison value
7914      */
7915     none : function(s){
7916         return s;
7917     },
7918     
7919     /**
7920      * The regular expression used to strip tags
7921      * @type {RegExp}
7922      * @property
7923      */
7924     stripTagsRE : /<\/?[^>]+>/gi,
7925     
7926     /**
7927      * Strips all HTML tags to sort on text only
7928      * @param {Mixed} s The value being converted
7929      * @return {String} The comparison value
7930      */
7931     asText : function(s){
7932         return String(s).replace(this.stripTagsRE, "");
7933     },
7934     
7935     /**
7936      * Strips all HTML tags to sort on text only - Case insensitive
7937      * @param {Mixed} s The value being converted
7938      * @return {String} The comparison value
7939      */
7940     asUCText : function(s){
7941         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7942     },
7943     
7944     /**
7945      * Case insensitive string
7946      * @param {Mixed} s The value being converted
7947      * @return {String} The comparison value
7948      */
7949     asUCString : function(s) {
7950         return String(s).toUpperCase();
7951     },
7952     
7953     /**
7954      * Date sorting
7955      * @param {Mixed} s The value being converted
7956      * @return {Number} The comparison value
7957      */
7958     asDate : function(s) {
7959         if(!s){
7960             return 0;
7961         }
7962         if(s instanceof Date){
7963             return s.getTime();
7964         }
7965         return Date.parse(String(s));
7966     },
7967     
7968     /**
7969      * Float sorting
7970      * @param {Mixed} s The value being converted
7971      * @return {Float} The comparison value
7972      */
7973     asFloat : function(s) {
7974         var val = parseFloat(String(s).replace(/,/g, ""));
7975         if(isNaN(val)) val = 0;
7976         return val;
7977     },
7978     
7979     /**
7980      * Integer sorting
7981      * @param {Mixed} s The value being converted
7982      * @return {Number} The comparison value
7983      */
7984     asInt : function(s) {
7985         var val = parseInt(String(s).replace(/,/g, ""));
7986         if(isNaN(val)) val = 0;
7987         return val;
7988     }
7989 };/*
7990  * Based on:
7991  * Ext JS Library 1.1.1
7992  * Copyright(c) 2006-2007, Ext JS, LLC.
7993  *
7994  * Originally Released Under LGPL - original licence link has changed is not relivant.
7995  *
7996  * Fork - LGPL
7997  * <script type="text/javascript">
7998  */
7999
8000 /**
8001 * @class Roo.data.Record
8002  * Instances of this class encapsulate both record <em>definition</em> information, and record
8003  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8004  * to access Records cached in an {@link Roo.data.Store} object.<br>
8005  * <p>
8006  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8007  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8008  * objects.<br>
8009  * <p>
8010  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8011  * @constructor
8012  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8013  * {@link #create}. The parameters are the same.
8014  * @param {Array} data An associative Array of data values keyed by the field name.
8015  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8016  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8017  * not specified an integer id is generated.
8018  */
8019 Roo.data.Record = function(data, id){
8020     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8021     this.data = data;
8022 };
8023
8024 /**
8025  * Generate a constructor for a specific record layout.
8026  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8027  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8028  * Each field definition object may contain the following properties: <ul>
8029  * <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,
8030  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8031  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8032  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8033  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8034  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8035  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8036  * this may be omitted.</p></li>
8037  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8038  * <ul><li>auto (Default, implies no conversion)</li>
8039  * <li>string</li>
8040  * <li>int</li>
8041  * <li>float</li>
8042  * <li>boolean</li>
8043  * <li>date</li></ul></p></li>
8044  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8045  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8046  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8047  * by the Reader into an object that will be stored in the Record. It is passed the
8048  * following parameters:<ul>
8049  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8050  * </ul></p></li>
8051  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8052  * </ul>
8053  * <br>usage:<br><pre><code>
8054 var TopicRecord = Roo.data.Record.create(
8055     {name: 'title', mapping: 'topic_title'},
8056     {name: 'author', mapping: 'username'},
8057     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8058     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8059     {name: 'lastPoster', mapping: 'user2'},
8060     {name: 'excerpt', mapping: 'post_text'}
8061 );
8062
8063 var myNewRecord = new TopicRecord({
8064     title: 'Do my job please',
8065     author: 'noobie',
8066     totalPosts: 1,
8067     lastPost: new Date(),
8068     lastPoster: 'Animal',
8069     excerpt: 'No way dude!'
8070 });
8071 myStore.add(myNewRecord);
8072 </code></pre>
8073  * @method create
8074  * @static
8075  */
8076 Roo.data.Record.create = function(o){
8077     var f = function(){
8078         f.superclass.constructor.apply(this, arguments);
8079     };
8080     Roo.extend(f, Roo.data.Record);
8081     var p = f.prototype;
8082     p.fields = new Roo.util.MixedCollection(false, function(field){
8083         return field.name;
8084     });
8085     for(var i = 0, len = o.length; i < len; i++){
8086         p.fields.add(new Roo.data.Field(o[i]));
8087     }
8088     f.getField = function(name){
8089         return p.fields.get(name);  
8090     };
8091     return f;
8092 };
8093
8094 Roo.data.Record.AUTO_ID = 1000;
8095 Roo.data.Record.EDIT = 'edit';
8096 Roo.data.Record.REJECT = 'reject';
8097 Roo.data.Record.COMMIT = 'commit';
8098
8099 Roo.data.Record.prototype = {
8100     /**
8101      * Readonly flag - true if this record has been modified.
8102      * @type Boolean
8103      */
8104     dirty : false,
8105     editing : false,
8106     error: null,
8107     modified: null,
8108
8109     // private
8110     join : function(store){
8111         this.store = store;
8112     },
8113
8114     /**
8115      * Set the named field to the specified value.
8116      * @param {String} name The name of the field to set.
8117      * @param {Object} value The value to set the field to.
8118      */
8119     set : function(name, value){
8120         if(this.data[name] == value){
8121             return;
8122         }
8123         this.dirty = true;
8124         if(!this.modified){
8125             this.modified = {};
8126         }
8127         if(typeof this.modified[name] == 'undefined'){
8128             this.modified[name] = this.data[name];
8129         }
8130         this.data[name] = value;
8131         if(!this.editing && this.store){
8132             this.store.afterEdit(this);
8133         }       
8134     },
8135
8136     /**
8137      * Get the value of the named field.
8138      * @param {String} name The name of the field to get the value of.
8139      * @return {Object} The value of the field.
8140      */
8141     get : function(name){
8142         return this.data[name]; 
8143     },
8144
8145     // private
8146     beginEdit : function(){
8147         this.editing = true;
8148         this.modified = {}; 
8149     },
8150
8151     // private
8152     cancelEdit : function(){
8153         this.editing = false;
8154         delete this.modified;
8155     },
8156
8157     // private
8158     endEdit : function(){
8159         this.editing = false;
8160         if(this.dirty && this.store){
8161             this.store.afterEdit(this);
8162         }
8163     },
8164
8165     /**
8166      * Usually called by the {@link Roo.data.Store} which owns the Record.
8167      * Rejects all changes made to the Record since either creation, or the last commit operation.
8168      * Modified fields are reverted to their original values.
8169      * <p>
8170      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8171      * of reject operations.
8172      */
8173     reject : function(){
8174         var m = this.modified;
8175         for(var n in m){
8176             if(typeof m[n] != "function"){
8177                 this.data[n] = m[n];
8178             }
8179         }
8180         this.dirty = false;
8181         delete this.modified;
8182         this.editing = false;
8183         if(this.store){
8184             this.store.afterReject(this);
8185         }
8186     },
8187
8188     /**
8189      * Usually called by the {@link Roo.data.Store} which owns the Record.
8190      * Commits all changes made to the Record since either creation, or the last commit operation.
8191      * <p>
8192      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8193      * of commit operations.
8194      */
8195     commit : function(){
8196         this.dirty = false;
8197         delete this.modified;
8198         this.editing = false;
8199         if(this.store){
8200             this.store.afterCommit(this);
8201         }
8202     },
8203
8204     // private
8205     hasError : function(){
8206         return this.error != null;
8207     },
8208
8209     // private
8210     clearError : function(){
8211         this.error = null;
8212     },
8213
8214     /**
8215      * Creates a copy of this record.
8216      * @param {String} id (optional) A new record id if you don't want to use this record's id
8217      * @return {Record}
8218      */
8219     copy : function(newId) {
8220         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8221     }
8222 };/*
8223  * Based on:
8224  * Ext JS Library 1.1.1
8225  * Copyright(c) 2006-2007, Ext JS, LLC.
8226  *
8227  * Originally Released Under LGPL - original licence link has changed is not relivant.
8228  *
8229  * Fork - LGPL
8230  * <script type="text/javascript">
8231  */
8232
8233
8234
8235 /**
8236  * @class Roo.data.Store
8237  * @extends Roo.util.Observable
8238  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8239  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8240  * <p>
8241  * 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
8242  * has no knowledge of the format of the data returned by the Proxy.<br>
8243  * <p>
8244  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8245  * instances from the data object. These records are cached and made available through accessor functions.
8246  * @constructor
8247  * Creates a new Store.
8248  * @param {Object} config A config object containing the objects needed for the Store to access data,
8249  * and read the data into Records.
8250  */
8251 Roo.data.Store = function(config){
8252     this.data = new Roo.util.MixedCollection(false);
8253     this.data.getKey = function(o){
8254         return o.id;
8255     };
8256     this.baseParams = {};
8257     // private
8258     this.paramNames = {
8259         "start" : "start",
8260         "limit" : "limit",
8261         "sort" : "sort",
8262         "dir" : "dir",
8263         "multisort" : "_multisort"
8264     };
8265
8266     if(config && config.data){
8267         this.inlineData = config.data;
8268         delete config.data;
8269     }
8270
8271     Roo.apply(this, config);
8272     
8273     if(this.reader){ // reader passed
8274         this.reader = Roo.factory(this.reader, Roo.data);
8275         this.reader.xmodule = this.xmodule || false;
8276         if(!this.recordType){
8277             this.recordType = this.reader.recordType;
8278         }
8279         if(this.reader.onMetaChange){
8280             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8281         }
8282     }
8283
8284     if(this.recordType){
8285         this.fields = this.recordType.prototype.fields;
8286     }
8287     this.modified = [];
8288
8289     this.addEvents({
8290         /**
8291          * @event datachanged
8292          * Fires when the data cache has changed, and a widget which is using this Store
8293          * as a Record cache should refresh its view.
8294          * @param {Store} this
8295          */
8296         datachanged : true,
8297         /**
8298          * @event metachange
8299          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8300          * @param {Store} this
8301          * @param {Object} meta The JSON metadata
8302          */
8303         metachange : true,
8304         /**
8305          * @event add
8306          * Fires when Records have been added to the Store
8307          * @param {Store} this
8308          * @param {Roo.data.Record[]} records The array of Records added
8309          * @param {Number} index The index at which the record(s) were added
8310          */
8311         add : true,
8312         /**
8313          * @event remove
8314          * Fires when a Record has been removed from the Store
8315          * @param {Store} this
8316          * @param {Roo.data.Record} record The Record that was removed
8317          * @param {Number} index The index at which the record was removed
8318          */
8319         remove : true,
8320         /**
8321          * @event update
8322          * Fires when a Record has been updated
8323          * @param {Store} this
8324          * @param {Roo.data.Record} record The Record that was updated
8325          * @param {String} operation The update operation being performed.  Value may be one of:
8326          * <pre><code>
8327  Roo.data.Record.EDIT
8328  Roo.data.Record.REJECT
8329  Roo.data.Record.COMMIT
8330          * </code></pre>
8331          */
8332         update : true,
8333         /**
8334          * @event clear
8335          * Fires when the data cache has been cleared.
8336          * @param {Store} this
8337          */
8338         clear : true,
8339         /**
8340          * @event beforeload
8341          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8342          * the load action will be canceled.
8343          * @param {Store} this
8344          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8345          */
8346         beforeload : true,
8347         /**
8348          * @event beforeloadadd
8349          * Fires after a new set of Records has been loaded.
8350          * @param {Store} this
8351          * @param {Roo.data.Record[]} records The Records that were loaded
8352          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8353          */
8354         beforeloadadd : true,
8355         /**
8356          * @event load
8357          * Fires after a new set of Records has been loaded, before they are added to the store.
8358          * @param {Store} this
8359          * @param {Roo.data.Record[]} records The Records that were loaded
8360          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8361          * @params {Object} return from reader
8362          */
8363         load : true,
8364         /**
8365          * @event loadexception
8366          * Fires if an exception occurs in the Proxy during loading.
8367          * Called with the signature of the Proxy's "loadexception" event.
8368          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8369          * 
8370          * @param {Proxy} 
8371          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8372          * @param {Object} load options 
8373          * @param {Object} jsonData from your request (normally this contains the Exception)
8374          */
8375         loadexception : true
8376     });
8377     
8378     if(this.proxy){
8379         this.proxy = Roo.factory(this.proxy, Roo.data);
8380         this.proxy.xmodule = this.xmodule || false;
8381         this.relayEvents(this.proxy,  ["loadexception"]);
8382     }
8383     this.sortToggle = {};
8384     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8385
8386     Roo.data.Store.superclass.constructor.call(this);
8387
8388     if(this.inlineData){
8389         this.loadData(this.inlineData);
8390         delete this.inlineData;
8391     }
8392 };
8393
8394 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8395      /**
8396     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8397     * without a remote query - used by combo/forms at present.
8398     */
8399     
8400     /**
8401     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8402     */
8403     /**
8404     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8405     */
8406     /**
8407     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8408     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8409     */
8410     /**
8411     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8412     * on any HTTP request
8413     */
8414     /**
8415     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8416     */
8417     /**
8418     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8419     */
8420     multiSort: false,
8421     /**
8422     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8423     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8424     */
8425     remoteSort : false,
8426
8427     /**
8428     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8429      * loaded or when a record is removed. (defaults to false).
8430     */
8431     pruneModifiedRecords : false,
8432
8433     // private
8434     lastOptions : null,
8435
8436     /**
8437      * Add Records to the Store and fires the add event.
8438      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8439      */
8440     add : function(records){
8441         records = [].concat(records);
8442         for(var i = 0, len = records.length; i < len; i++){
8443             records[i].join(this);
8444         }
8445         var index = this.data.length;
8446         this.data.addAll(records);
8447         this.fireEvent("add", this, records, index);
8448     },
8449
8450     /**
8451      * Remove a Record from the Store and fires the remove event.
8452      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8453      */
8454     remove : function(record){
8455         var index = this.data.indexOf(record);
8456         this.data.removeAt(index);
8457         if(this.pruneModifiedRecords){
8458             this.modified.remove(record);
8459         }
8460         this.fireEvent("remove", this, record, index);
8461     },
8462
8463     /**
8464      * Remove all Records from the Store and fires the clear event.
8465      */
8466     removeAll : function(){
8467         this.data.clear();
8468         if(this.pruneModifiedRecords){
8469             this.modified = [];
8470         }
8471         this.fireEvent("clear", this);
8472     },
8473
8474     /**
8475      * Inserts Records to the Store at the given index and fires the add event.
8476      * @param {Number} index The start index at which to insert the passed Records.
8477      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8478      */
8479     insert : function(index, records){
8480         records = [].concat(records);
8481         for(var i = 0, len = records.length; i < len; i++){
8482             this.data.insert(index, records[i]);
8483             records[i].join(this);
8484         }
8485         this.fireEvent("add", this, records, index);
8486     },
8487
8488     /**
8489      * Get the index within the cache of the passed Record.
8490      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8491      * @return {Number} The index of the passed Record. Returns -1 if not found.
8492      */
8493     indexOf : function(record){
8494         return this.data.indexOf(record);
8495     },
8496
8497     /**
8498      * Get the index within the cache of the Record with the passed id.
8499      * @param {String} id The id of the Record to find.
8500      * @return {Number} The index of the Record. Returns -1 if not found.
8501      */
8502     indexOfId : function(id){
8503         return this.data.indexOfKey(id);
8504     },
8505
8506     /**
8507      * Get the Record with the specified id.
8508      * @param {String} id The id of the Record to find.
8509      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8510      */
8511     getById : function(id){
8512         return this.data.key(id);
8513     },
8514
8515     /**
8516      * Get the Record at the specified index.
8517      * @param {Number} index The index of the Record to find.
8518      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8519      */
8520     getAt : function(index){
8521         return this.data.itemAt(index);
8522     },
8523
8524     /**
8525      * Returns a range of Records between specified indices.
8526      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8527      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8528      * @return {Roo.data.Record[]} An array of Records
8529      */
8530     getRange : function(start, end){
8531         return this.data.getRange(start, end);
8532     },
8533
8534     // private
8535     storeOptions : function(o){
8536         o = Roo.apply({}, o);
8537         delete o.callback;
8538         delete o.scope;
8539         this.lastOptions = o;
8540     },
8541
8542     /**
8543      * Loads the Record cache from the configured Proxy using the configured Reader.
8544      * <p>
8545      * If using remote paging, then the first load call must specify the <em>start</em>
8546      * and <em>limit</em> properties in the options.params property to establish the initial
8547      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8548      * <p>
8549      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8550      * and this call will return before the new data has been loaded. Perform any post-processing
8551      * in a callback function, or in a "load" event handler.</strong>
8552      * <p>
8553      * @param {Object} options An object containing properties which control loading options:<ul>
8554      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8555      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8556      * passed the following arguments:<ul>
8557      * <li>r : Roo.data.Record[]</li>
8558      * <li>options: Options object from the load call</li>
8559      * <li>success: Boolean success indicator</li></ul></li>
8560      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8561      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8562      * </ul>
8563      */
8564     load : function(options){
8565         options = options || {};
8566         if(this.fireEvent("beforeload", this, options) !== false){
8567             this.storeOptions(options);
8568             var p = Roo.apply(options.params || {}, this.baseParams);
8569             // if meta was not loaded from remote source.. try requesting it.
8570             if (!this.reader.metaFromRemote) {
8571                 p._requestMeta = 1;
8572             }
8573             if(this.sortInfo && this.remoteSort){
8574                 var pn = this.paramNames;
8575                 p[pn["sort"]] = this.sortInfo.field;
8576                 p[pn["dir"]] = this.sortInfo.direction;
8577             }
8578             if (this.multiSort) {
8579                 var pn = this.paramNames;
8580                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8581             }
8582             
8583             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8584         }
8585     },
8586
8587     /**
8588      * Reloads the Record cache from the configured Proxy using the configured Reader and
8589      * the options from the last load operation performed.
8590      * @param {Object} options (optional) An object containing properties which may override the options
8591      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8592      * the most recently used options are reused).
8593      */
8594     reload : function(options){
8595         this.load(Roo.applyIf(options||{}, this.lastOptions));
8596     },
8597
8598     // private
8599     // Called as a callback by the Reader during a load operation.
8600     loadRecords : function(o, options, success){
8601         if(!o || success === false){
8602             if(success !== false){
8603                 this.fireEvent("load", this, [], options, o);
8604             }
8605             if(options.callback){
8606                 options.callback.call(options.scope || this, [], options, false);
8607             }
8608             return;
8609         }
8610         // if data returned failure - throw an exception.
8611         if (o.success === false) {
8612             // show a message if no listener is registered.
8613             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8614                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8615             }
8616             // loadmask wil be hooked into this..
8617             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8618             return;
8619         }
8620         var r = o.records, t = o.totalRecords || r.length;
8621         
8622         this.fireEvent("beforeloadadd", this, r, options, o);
8623         
8624         if(!options || options.add !== true){
8625             if(this.pruneModifiedRecords){
8626                 this.modified = [];
8627             }
8628             for(var i = 0, len = r.length; i < len; i++){
8629                 r[i].join(this);
8630             }
8631             if(this.snapshot){
8632                 this.data = this.snapshot;
8633                 delete this.snapshot;
8634             }
8635             this.data.clear();
8636             this.data.addAll(r);
8637             this.totalLength = t;
8638             this.applySort();
8639             this.fireEvent("datachanged", this);
8640         }else{
8641             this.totalLength = Math.max(t, this.data.length+r.length);
8642             this.add(r);
8643         }
8644         this.fireEvent("load", this, r, options, o);
8645         if(options.callback){
8646             options.callback.call(options.scope || this, r, options, true);
8647         }
8648     },
8649
8650
8651     /**
8652      * Loads data from a passed data block. A Reader which understands the format of the data
8653      * must have been configured in the constructor.
8654      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8655      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8656      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8657      */
8658     loadData : function(o, append){
8659         var r = this.reader.readRecords(o);
8660         this.loadRecords(r, {add: append}, true);
8661     },
8662
8663     /**
8664      * Gets the number of cached records.
8665      * <p>
8666      * <em>If using paging, this may not be the total size of the dataset. If the data object
8667      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8668      * the data set size</em>
8669      */
8670     getCount : function(){
8671         return this.data.length || 0;
8672     },
8673
8674     /**
8675      * Gets the total number of records in the dataset as returned by the server.
8676      * <p>
8677      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8678      * the dataset size</em>
8679      */
8680     getTotalCount : function(){
8681         return this.totalLength || 0;
8682     },
8683
8684     /**
8685      * Returns the sort state of the Store as an object with two properties:
8686      * <pre><code>
8687  field {String} The name of the field by which the Records are sorted
8688  direction {String} The sort order, "ASC" or "DESC"
8689      * </code></pre>
8690      */
8691     getSortState : function(){
8692         return this.sortInfo;
8693     },
8694
8695     // private
8696     applySort : function(){
8697         if(this.sortInfo && !this.remoteSort){
8698             var s = this.sortInfo, f = s.field;
8699             var st = this.fields.get(f).sortType;
8700             var fn = function(r1, r2){
8701                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8702                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8703             };
8704             this.data.sort(s.direction, fn);
8705             if(this.snapshot && this.snapshot != this.data){
8706                 this.snapshot.sort(s.direction, fn);
8707             }
8708         }
8709     },
8710
8711     /**
8712      * Sets the default sort column and order to be used by the next load operation.
8713      * @param {String} fieldName The name of the field to sort by.
8714      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8715      */
8716     setDefaultSort : function(field, dir){
8717         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8718     },
8719
8720     /**
8721      * Sort the Records.
8722      * If remote sorting is used, the sort is performed on the server, and the cache is
8723      * reloaded. If local sorting is used, the cache is sorted internally.
8724      * @param {String} fieldName The name of the field to sort by.
8725      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8726      */
8727     sort : function(fieldName, dir){
8728         var f = this.fields.get(fieldName);
8729         if(!dir){
8730             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8731             
8732             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8733                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8734             }else{
8735                 dir = f.sortDir;
8736             }
8737         }
8738         this.sortToggle[f.name] = dir;
8739         this.sortInfo = {field: f.name, direction: dir};
8740         if(!this.remoteSort){
8741             this.applySort();
8742             this.fireEvent("datachanged", this);
8743         }else{
8744             this.load(this.lastOptions);
8745         }
8746     },
8747
8748     /**
8749      * Calls the specified function for each of the Records in the cache.
8750      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8751      * Returning <em>false</em> aborts and exits the iteration.
8752      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8753      */
8754     each : function(fn, scope){
8755         this.data.each(fn, scope);
8756     },
8757
8758     /**
8759      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8760      * (e.g., during paging).
8761      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8762      */
8763     getModifiedRecords : function(){
8764         return this.modified;
8765     },
8766
8767     // private
8768     createFilterFn : function(property, value, anyMatch){
8769         if(!value.exec){ // not a regex
8770             value = String(value);
8771             if(value.length == 0){
8772                 return false;
8773             }
8774             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8775         }
8776         return function(r){
8777             return value.test(r.data[property]);
8778         };
8779     },
8780
8781     /**
8782      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8783      * @param {String} property A field on your records
8784      * @param {Number} start The record index to start at (defaults to 0)
8785      * @param {Number} end The last record index to include (defaults to length - 1)
8786      * @return {Number} The sum
8787      */
8788     sum : function(property, start, end){
8789         var rs = this.data.items, v = 0;
8790         start = start || 0;
8791         end = (end || end === 0) ? end : rs.length-1;
8792
8793         for(var i = start; i <= end; i++){
8794             v += (rs[i].data[property] || 0);
8795         }
8796         return v;
8797     },
8798
8799     /**
8800      * Filter the records by a specified property.
8801      * @param {String} field A field on your records
8802      * @param {String/RegExp} value Either a string that the field
8803      * should start with or a RegExp to test against the field
8804      * @param {Boolean} anyMatch True to match any part not just the beginning
8805      */
8806     filter : function(property, value, anyMatch){
8807         var fn = this.createFilterFn(property, value, anyMatch);
8808         return fn ? this.filterBy(fn) : this.clearFilter();
8809     },
8810
8811     /**
8812      * Filter by a function. The specified function will be called with each
8813      * record in this data source. If the function returns true the record is included,
8814      * otherwise it is filtered.
8815      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8816      * @param {Object} scope (optional) The scope of the function (defaults to this)
8817      */
8818     filterBy : function(fn, scope){
8819         this.snapshot = this.snapshot || this.data;
8820         this.data = this.queryBy(fn, scope||this);
8821         this.fireEvent("datachanged", this);
8822     },
8823
8824     /**
8825      * Query the records by a specified property.
8826      * @param {String} field A field on your records
8827      * @param {String/RegExp} value Either a string that the field
8828      * should start with or a RegExp to test against the field
8829      * @param {Boolean} anyMatch True to match any part not just the beginning
8830      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8831      */
8832     query : function(property, value, anyMatch){
8833         var fn = this.createFilterFn(property, value, anyMatch);
8834         return fn ? this.queryBy(fn) : this.data.clone();
8835     },
8836
8837     /**
8838      * Query by a function. The specified function will be called with each
8839      * record in this data source. If the function returns true the record is included
8840      * in the results.
8841      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8842      * @param {Object} scope (optional) The scope of the function (defaults to this)
8843       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8844      **/
8845     queryBy : function(fn, scope){
8846         var data = this.snapshot || this.data;
8847         return data.filterBy(fn, scope||this);
8848     },
8849
8850     /**
8851      * Collects unique values for a particular dataIndex from this store.
8852      * @param {String} dataIndex The property to collect
8853      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8854      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8855      * @return {Array} An array of the unique values
8856      **/
8857     collect : function(dataIndex, allowNull, bypassFilter){
8858         var d = (bypassFilter === true && this.snapshot) ?
8859                 this.snapshot.items : this.data.items;
8860         var v, sv, r = [], l = {};
8861         for(var i = 0, len = d.length; i < len; i++){
8862             v = d[i].data[dataIndex];
8863             sv = String(v);
8864             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8865                 l[sv] = true;
8866                 r[r.length] = v;
8867             }
8868         }
8869         return r;
8870     },
8871
8872     /**
8873      * Revert to a view of the Record cache with no filtering applied.
8874      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
8875      */
8876     clearFilter : function(suppressEvent){
8877         if(this.snapshot && this.snapshot != this.data){
8878             this.data = this.snapshot;
8879             delete this.snapshot;
8880             if(suppressEvent !== true){
8881                 this.fireEvent("datachanged", this);
8882             }
8883         }
8884     },
8885
8886     // private
8887     afterEdit : function(record){
8888         if(this.modified.indexOf(record) == -1){
8889             this.modified.push(record);
8890         }
8891         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8892     },
8893     
8894     // private
8895     afterReject : function(record){
8896         this.modified.remove(record);
8897         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8898     },
8899
8900     // private
8901     afterCommit : function(record){
8902         this.modified.remove(record);
8903         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8904     },
8905
8906     /**
8907      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8908      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8909      */
8910     commitChanges : function(){
8911         var m = this.modified.slice(0);
8912         this.modified = [];
8913         for(var i = 0, len = m.length; i < len; i++){
8914             m[i].commit();
8915         }
8916     },
8917
8918     /**
8919      * Cancel outstanding changes on all changed records.
8920      */
8921     rejectChanges : function(){
8922         var m = this.modified.slice(0);
8923         this.modified = [];
8924         for(var i = 0, len = m.length; i < len; i++){
8925             m[i].reject();
8926         }
8927     },
8928
8929     onMetaChange : function(meta, rtype, o){
8930         this.recordType = rtype;
8931         this.fields = rtype.prototype.fields;
8932         delete this.snapshot;
8933         this.sortInfo = meta.sortInfo || this.sortInfo;
8934         this.modified = [];
8935         this.fireEvent('metachange', this, this.reader.meta);
8936     },
8937     
8938     moveIndex : function(data, type)
8939     {
8940         var index = this.indexOf(data);
8941         
8942         var newIndex = index + type;
8943         
8944         this.remove(data);
8945         
8946         this.insert(newIndex, data);
8947         
8948     }
8949 });/*
8950  * Based on:
8951  * Ext JS Library 1.1.1
8952  * Copyright(c) 2006-2007, Ext JS, LLC.
8953  *
8954  * Originally Released Under LGPL - original licence link has changed is not relivant.
8955  *
8956  * Fork - LGPL
8957  * <script type="text/javascript">
8958  */
8959
8960 /**
8961  * @class Roo.data.SimpleStore
8962  * @extends Roo.data.Store
8963  * Small helper class to make creating Stores from Array data easier.
8964  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8965  * @cfg {Array} fields An array of field definition objects, or field name strings.
8966  * @cfg {Array} data The multi-dimensional array of data
8967  * @constructor
8968  * @param {Object} config
8969  */
8970 Roo.data.SimpleStore = function(config){
8971     Roo.data.SimpleStore.superclass.constructor.call(this, {
8972         isLocal : true,
8973         reader: new Roo.data.ArrayReader({
8974                 id: config.id
8975             },
8976             Roo.data.Record.create(config.fields)
8977         ),
8978         proxy : new Roo.data.MemoryProxy(config.data)
8979     });
8980     this.load();
8981 };
8982 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8983  * Based on:
8984  * Ext JS Library 1.1.1
8985  * Copyright(c) 2006-2007, Ext JS, LLC.
8986  *
8987  * Originally Released Under LGPL - original licence link has changed is not relivant.
8988  *
8989  * Fork - LGPL
8990  * <script type="text/javascript">
8991  */
8992
8993 /**
8994 /**
8995  * @extends Roo.data.Store
8996  * @class Roo.data.JsonStore
8997  * Small helper class to make creating Stores for JSON data easier. <br/>
8998 <pre><code>
8999 var store = new Roo.data.JsonStore({
9000     url: 'get-images.php',
9001     root: 'images',
9002     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9003 });
9004 </code></pre>
9005  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9006  * JsonReader and HttpProxy (unless inline data is provided).</b>
9007  * @cfg {Array} fields An array of field definition objects, or field name strings.
9008  * @constructor
9009  * @param {Object} config
9010  */
9011 Roo.data.JsonStore = function(c){
9012     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9013         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9014         reader: new Roo.data.JsonReader(c, c.fields)
9015     }));
9016 };
9017 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9018  * Based on:
9019  * Ext JS Library 1.1.1
9020  * Copyright(c) 2006-2007, Ext JS, LLC.
9021  *
9022  * Originally Released Under LGPL - original licence link has changed is not relivant.
9023  *
9024  * Fork - LGPL
9025  * <script type="text/javascript">
9026  */
9027
9028  
9029 Roo.data.Field = function(config){
9030     if(typeof config == "string"){
9031         config = {name: config};
9032     }
9033     Roo.apply(this, config);
9034     
9035     if(!this.type){
9036         this.type = "auto";
9037     }
9038     
9039     var st = Roo.data.SortTypes;
9040     // named sortTypes are supported, here we look them up
9041     if(typeof this.sortType == "string"){
9042         this.sortType = st[this.sortType];
9043     }
9044     
9045     // set default sortType for strings and dates
9046     if(!this.sortType){
9047         switch(this.type){
9048             case "string":
9049                 this.sortType = st.asUCString;
9050                 break;
9051             case "date":
9052                 this.sortType = st.asDate;
9053                 break;
9054             default:
9055                 this.sortType = st.none;
9056         }
9057     }
9058
9059     // define once
9060     var stripRe = /[\$,%]/g;
9061
9062     // prebuilt conversion function for this field, instead of
9063     // switching every time we're reading a value
9064     if(!this.convert){
9065         var cv, dateFormat = this.dateFormat;
9066         switch(this.type){
9067             case "":
9068             case "auto":
9069             case undefined:
9070                 cv = function(v){ return v; };
9071                 break;
9072             case "string":
9073                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9074                 break;
9075             case "int":
9076                 cv = function(v){
9077                     return v !== undefined && v !== null && v !== '' ?
9078                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9079                     };
9080                 break;
9081             case "float":
9082                 cv = function(v){
9083                     return v !== undefined && v !== null && v !== '' ?
9084                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9085                     };
9086                 break;
9087             case "bool":
9088             case "boolean":
9089                 cv = function(v){ return v === true || v === "true" || v == 1; };
9090                 break;
9091             case "date":
9092                 cv = function(v){
9093                     if(!v){
9094                         return '';
9095                     }
9096                     if(v instanceof Date){
9097                         return v;
9098                     }
9099                     if(dateFormat){
9100                         if(dateFormat == "timestamp"){
9101                             return new Date(v*1000);
9102                         }
9103                         return Date.parseDate(v, dateFormat);
9104                     }
9105                     var parsed = Date.parse(v);
9106                     return parsed ? new Date(parsed) : null;
9107                 };
9108              break;
9109             
9110         }
9111         this.convert = cv;
9112     }
9113 };
9114
9115 Roo.data.Field.prototype = {
9116     dateFormat: null,
9117     defaultValue: "",
9118     mapping: null,
9119     sortType : null,
9120     sortDir : "ASC"
9121 };/*
9122  * Based on:
9123  * Ext JS Library 1.1.1
9124  * Copyright(c) 2006-2007, Ext JS, LLC.
9125  *
9126  * Originally Released Under LGPL - original licence link has changed is not relivant.
9127  *
9128  * Fork - LGPL
9129  * <script type="text/javascript">
9130  */
9131  
9132 // Base class for reading structured data from a data source.  This class is intended to be
9133 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9134
9135 /**
9136  * @class Roo.data.DataReader
9137  * Base class for reading structured data from a data source.  This class is intended to be
9138  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9139  */
9140
9141 Roo.data.DataReader = function(meta, recordType){
9142     
9143     this.meta = meta;
9144     
9145     this.recordType = recordType instanceof Array ? 
9146         Roo.data.Record.create(recordType) : recordType;
9147 };
9148
9149 Roo.data.DataReader.prototype = {
9150      /**
9151      * Create an empty record
9152      * @param {Object} data (optional) - overlay some values
9153      * @return {Roo.data.Record} record created.
9154      */
9155     newRow :  function(d) {
9156         var da =  {};
9157         this.recordType.prototype.fields.each(function(c) {
9158             switch( c.type) {
9159                 case 'int' : da[c.name] = 0; break;
9160                 case 'date' : da[c.name] = new Date(); break;
9161                 case 'float' : da[c.name] = 0.0; break;
9162                 case 'boolean' : da[c.name] = false; break;
9163                 default : da[c.name] = ""; break;
9164             }
9165             
9166         });
9167         return new this.recordType(Roo.apply(da, d));
9168     }
9169     
9170 };/*
9171  * Based on:
9172  * Ext JS Library 1.1.1
9173  * Copyright(c) 2006-2007, Ext JS, LLC.
9174  *
9175  * Originally Released Under LGPL - original licence link has changed is not relivant.
9176  *
9177  * Fork - LGPL
9178  * <script type="text/javascript">
9179  */
9180
9181 /**
9182  * @class Roo.data.DataProxy
9183  * @extends Roo.data.Observable
9184  * This class is an abstract base class for implementations which provide retrieval of
9185  * unformatted data objects.<br>
9186  * <p>
9187  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9188  * (of the appropriate type which knows how to parse the data object) to provide a block of
9189  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9190  * <p>
9191  * Custom implementations must implement the load method as described in
9192  * {@link Roo.data.HttpProxy#load}.
9193  */
9194 Roo.data.DataProxy = function(){
9195     this.addEvents({
9196         /**
9197          * @event beforeload
9198          * Fires before a network request is made to retrieve a data object.
9199          * @param {Object} This DataProxy object.
9200          * @param {Object} params The params parameter to the load function.
9201          */
9202         beforeload : true,
9203         /**
9204          * @event load
9205          * Fires before the load method's callback is called.
9206          * @param {Object} This DataProxy object.
9207          * @param {Object} o The data object.
9208          * @param {Object} arg The callback argument object passed to the load function.
9209          */
9210         load : true,
9211         /**
9212          * @event loadexception
9213          * Fires if an Exception occurs during data retrieval.
9214          * @param {Object} This DataProxy object.
9215          * @param {Object} o The data object.
9216          * @param {Object} arg The callback argument object passed to the load function.
9217          * @param {Object} e The Exception.
9218          */
9219         loadexception : true
9220     });
9221     Roo.data.DataProxy.superclass.constructor.call(this);
9222 };
9223
9224 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9225
9226     /**
9227      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9228      */
9229 /*
9230  * Based on:
9231  * Ext JS Library 1.1.1
9232  * Copyright(c) 2006-2007, Ext JS, LLC.
9233  *
9234  * Originally Released Under LGPL - original licence link has changed is not relivant.
9235  *
9236  * Fork - LGPL
9237  * <script type="text/javascript">
9238  */
9239 /**
9240  * @class Roo.data.MemoryProxy
9241  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9242  * to the Reader when its load method is called.
9243  * @constructor
9244  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9245  */
9246 Roo.data.MemoryProxy = function(data){
9247     if (data.data) {
9248         data = data.data;
9249     }
9250     Roo.data.MemoryProxy.superclass.constructor.call(this);
9251     this.data = data;
9252 };
9253
9254 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9255     /**
9256      * Load data from the requested source (in this case an in-memory
9257      * data object passed to the constructor), read the data object into
9258      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9259      * process that block using the passed callback.
9260      * @param {Object} params This parameter is not used by the MemoryProxy class.
9261      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9262      * object into a block of Roo.data.Records.
9263      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9264      * The function must be passed <ul>
9265      * <li>The Record block object</li>
9266      * <li>The "arg" argument from the load function</li>
9267      * <li>A boolean success indicator</li>
9268      * </ul>
9269      * @param {Object} scope The scope in which to call the callback
9270      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9271      */
9272     load : function(params, reader, callback, scope, arg){
9273         params = params || {};
9274         var result;
9275         try {
9276             result = reader.readRecords(this.data);
9277         }catch(e){
9278             this.fireEvent("loadexception", this, arg, null, e);
9279             callback.call(scope, null, arg, false);
9280             return;
9281         }
9282         callback.call(scope, result, arg, true);
9283     },
9284     
9285     // private
9286     update : function(params, records){
9287         
9288     }
9289 });/*
9290  * Based on:
9291  * Ext JS Library 1.1.1
9292  * Copyright(c) 2006-2007, Ext JS, LLC.
9293  *
9294  * Originally Released Under LGPL - original licence link has changed is not relivant.
9295  *
9296  * Fork - LGPL
9297  * <script type="text/javascript">
9298  */
9299 /**
9300  * @class Roo.data.HttpProxy
9301  * @extends Roo.data.DataProxy
9302  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9303  * configured to reference a certain URL.<br><br>
9304  * <p>
9305  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9306  * from which the running page was served.<br><br>
9307  * <p>
9308  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9309  * <p>
9310  * Be aware that to enable the browser to parse an XML document, the server must set
9311  * the Content-Type header in the HTTP response to "text/xml".
9312  * @constructor
9313  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9314  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9315  * will be used to make the request.
9316  */
9317 Roo.data.HttpProxy = function(conn){
9318     Roo.data.HttpProxy.superclass.constructor.call(this);
9319     // is conn a conn config or a real conn?
9320     this.conn = conn;
9321     this.useAjax = !conn || !conn.events;
9322   
9323 };
9324
9325 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9326     // thse are take from connection...
9327     
9328     /**
9329      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9330      */
9331     /**
9332      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9333      * extra parameters to each request made by this object. (defaults to undefined)
9334      */
9335     /**
9336      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9337      *  to each request made by this object. (defaults to undefined)
9338      */
9339     /**
9340      * @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)
9341      */
9342     /**
9343      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9344      */
9345      /**
9346      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9347      * @type Boolean
9348      */
9349   
9350
9351     /**
9352      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9353      * @type Boolean
9354      */
9355     /**
9356      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9357      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9358      * a finer-grained basis than the DataProxy events.
9359      */
9360     getConnection : function(){
9361         return this.useAjax ? Roo.Ajax : this.conn;
9362     },
9363
9364     /**
9365      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9366      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9367      * process that block using the passed callback.
9368      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9369      * for the request to the remote server.
9370      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9371      * object into a block of Roo.data.Records.
9372      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9373      * The function must be passed <ul>
9374      * <li>The Record block object</li>
9375      * <li>The "arg" argument from the load function</li>
9376      * <li>A boolean success indicator</li>
9377      * </ul>
9378      * @param {Object} scope The scope in which to call the callback
9379      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9380      */
9381     load : function(params, reader, callback, scope, arg){
9382         if(this.fireEvent("beforeload", this, params) !== false){
9383             var  o = {
9384                 params : params || {},
9385                 request: {
9386                     callback : callback,
9387                     scope : scope,
9388                     arg : arg
9389                 },
9390                 reader: reader,
9391                 callback : this.loadResponse,
9392                 scope: this
9393             };
9394             if(this.useAjax){
9395                 Roo.applyIf(o, this.conn);
9396                 if(this.activeRequest){
9397                     Roo.Ajax.abort(this.activeRequest);
9398                 }
9399                 this.activeRequest = Roo.Ajax.request(o);
9400             }else{
9401                 this.conn.request(o);
9402             }
9403         }else{
9404             callback.call(scope||this, null, arg, false);
9405         }
9406     },
9407
9408     // private
9409     loadResponse : function(o, success, response){
9410         delete this.activeRequest;
9411         if(!success){
9412             this.fireEvent("loadexception", this, o, response);
9413             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9414             return;
9415         }
9416         var result;
9417         try {
9418             result = o.reader.read(response);
9419         }catch(e){
9420             this.fireEvent("loadexception", this, o, response, e);
9421             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9422             return;
9423         }
9424         
9425         this.fireEvent("load", this, o, o.request.arg);
9426         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9427     },
9428
9429     // private
9430     update : function(dataSet){
9431
9432     },
9433
9434     // private
9435     updateResponse : function(dataSet){
9436
9437     }
9438 });/*
9439  * Based on:
9440  * Ext JS Library 1.1.1
9441  * Copyright(c) 2006-2007, Ext JS, LLC.
9442  *
9443  * Originally Released Under LGPL - original licence link has changed is not relivant.
9444  *
9445  * Fork - LGPL
9446  * <script type="text/javascript">
9447  */
9448
9449 /**
9450  * @class Roo.data.ScriptTagProxy
9451  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9452  * other than the originating domain of the running page.<br><br>
9453  * <p>
9454  * <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
9455  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9456  * <p>
9457  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9458  * source code that is used as the source inside a &lt;script> tag.<br><br>
9459  * <p>
9460  * In order for the browser to process the returned data, the server must wrap the data object
9461  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9462  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9463  * depending on whether the callback name was passed:
9464  * <p>
9465  * <pre><code>
9466 boolean scriptTag = false;
9467 String cb = request.getParameter("callback");
9468 if (cb != null) {
9469     scriptTag = true;
9470     response.setContentType("text/javascript");
9471 } else {
9472     response.setContentType("application/x-json");
9473 }
9474 Writer out = response.getWriter();
9475 if (scriptTag) {
9476     out.write(cb + "(");
9477 }
9478 out.print(dataBlock.toJsonString());
9479 if (scriptTag) {
9480     out.write(");");
9481 }
9482 </pre></code>
9483  *
9484  * @constructor
9485  * @param {Object} config A configuration object.
9486  */
9487 Roo.data.ScriptTagProxy = function(config){
9488     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9489     Roo.apply(this, config);
9490     this.head = document.getElementsByTagName("head")[0];
9491 };
9492
9493 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9494
9495 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9496     /**
9497      * @cfg {String} url The URL from which to request the data object.
9498      */
9499     /**
9500      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9501      */
9502     timeout : 30000,
9503     /**
9504      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9505      * the server the name of the callback function set up by the load call to process the returned data object.
9506      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9507      * javascript output which calls this named function passing the data object as its only parameter.
9508      */
9509     callbackParam : "callback",
9510     /**
9511      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9512      * name to the request.
9513      */
9514     nocache : true,
9515
9516     /**
9517      * Load data from the configured URL, read the data object into
9518      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9519      * process that block using the passed callback.
9520      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9521      * for the request to the remote server.
9522      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9523      * object into a block of Roo.data.Records.
9524      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9525      * The function must be passed <ul>
9526      * <li>The Record block object</li>
9527      * <li>The "arg" argument from the load function</li>
9528      * <li>A boolean success indicator</li>
9529      * </ul>
9530      * @param {Object} scope The scope in which to call the callback
9531      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9532      */
9533     load : function(params, reader, callback, scope, arg){
9534         if(this.fireEvent("beforeload", this, params) !== false){
9535
9536             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9537
9538             var url = this.url;
9539             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9540             if(this.nocache){
9541                 url += "&_dc=" + (new Date().getTime());
9542             }
9543             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9544             var trans = {
9545                 id : transId,
9546                 cb : "stcCallback"+transId,
9547                 scriptId : "stcScript"+transId,
9548                 params : params,
9549                 arg : arg,
9550                 url : url,
9551                 callback : callback,
9552                 scope : scope,
9553                 reader : reader
9554             };
9555             var conn = this;
9556
9557             window[trans.cb] = function(o){
9558                 conn.handleResponse(o, trans);
9559             };
9560
9561             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9562
9563             if(this.autoAbort !== false){
9564                 this.abort();
9565             }
9566
9567             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9568
9569             var script = document.createElement("script");
9570             script.setAttribute("src", url);
9571             script.setAttribute("type", "text/javascript");
9572             script.setAttribute("id", trans.scriptId);
9573             this.head.appendChild(script);
9574
9575             this.trans = trans;
9576         }else{
9577             callback.call(scope||this, null, arg, false);
9578         }
9579     },
9580
9581     // private
9582     isLoading : function(){
9583         return this.trans ? true : false;
9584     },
9585
9586     /**
9587      * Abort the current server request.
9588      */
9589     abort : function(){
9590         if(this.isLoading()){
9591             this.destroyTrans(this.trans);
9592         }
9593     },
9594
9595     // private
9596     destroyTrans : function(trans, isLoaded){
9597         this.head.removeChild(document.getElementById(trans.scriptId));
9598         clearTimeout(trans.timeoutId);
9599         if(isLoaded){
9600             window[trans.cb] = undefined;
9601             try{
9602                 delete window[trans.cb];
9603             }catch(e){}
9604         }else{
9605             // if hasn't been loaded, wait for load to remove it to prevent script error
9606             window[trans.cb] = function(){
9607                 window[trans.cb] = undefined;
9608                 try{
9609                     delete window[trans.cb];
9610                 }catch(e){}
9611             };
9612         }
9613     },
9614
9615     // private
9616     handleResponse : function(o, trans){
9617         this.trans = false;
9618         this.destroyTrans(trans, true);
9619         var result;
9620         try {
9621             result = trans.reader.readRecords(o);
9622         }catch(e){
9623             this.fireEvent("loadexception", this, o, trans.arg, e);
9624             trans.callback.call(trans.scope||window, null, trans.arg, false);
9625             return;
9626         }
9627         this.fireEvent("load", this, o, trans.arg);
9628         trans.callback.call(trans.scope||window, result, trans.arg, true);
9629     },
9630
9631     // private
9632     handleFailure : function(trans){
9633         this.trans = false;
9634         this.destroyTrans(trans, false);
9635         this.fireEvent("loadexception", this, null, trans.arg);
9636         trans.callback.call(trans.scope||window, null, trans.arg, false);
9637     }
9638 });/*
9639  * Based on:
9640  * Ext JS Library 1.1.1
9641  * Copyright(c) 2006-2007, Ext JS, LLC.
9642  *
9643  * Originally Released Under LGPL - original licence link has changed is not relivant.
9644  *
9645  * Fork - LGPL
9646  * <script type="text/javascript">
9647  */
9648
9649 /**
9650  * @class Roo.data.JsonReader
9651  * @extends Roo.data.DataReader
9652  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9653  * based on mappings in a provided Roo.data.Record constructor.
9654  * 
9655  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9656  * in the reply previously. 
9657  * 
9658  * <p>
9659  * Example code:
9660  * <pre><code>
9661 var RecordDef = Roo.data.Record.create([
9662     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9663     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9664 ]);
9665 var myReader = new Roo.data.JsonReader({
9666     totalProperty: "results",    // The property which contains the total dataset size (optional)
9667     root: "rows",                // The property which contains an Array of row objects
9668     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9669 }, RecordDef);
9670 </code></pre>
9671  * <p>
9672  * This would consume a JSON file like this:
9673  * <pre><code>
9674 { 'results': 2, 'rows': [
9675     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9676     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9677 }
9678 </code></pre>
9679  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9680  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9681  * paged from the remote server.
9682  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9683  * @cfg {String} root name of the property which contains the Array of row objects.
9684  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9685  * @constructor
9686  * Create a new JsonReader
9687  * @param {Object} meta Metadata configuration options
9688  * @param {Object} recordType Either an Array of field definition objects,
9689  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9690  */
9691 Roo.data.JsonReader = function(meta, recordType){
9692     
9693     meta = meta || {};
9694     // set some defaults:
9695     Roo.applyIf(meta, {
9696         totalProperty: 'total',
9697         successProperty : 'success',
9698         root : 'data',
9699         id : 'id'
9700     });
9701     
9702     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9703 };
9704 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9705     
9706     /**
9707      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9708      * Used by Store query builder to append _requestMeta to params.
9709      * 
9710      */
9711     metaFromRemote : false,
9712     /**
9713      * This method is only used by a DataProxy which has retrieved data from a remote server.
9714      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9715      * @return {Object} data A data block which is used by an Roo.data.Store object as
9716      * a cache of Roo.data.Records.
9717      */
9718     read : function(response){
9719         var json = response.responseText;
9720        
9721         var o = /* eval:var:o */ eval("("+json+")");
9722         if(!o) {
9723             throw {message: "JsonReader.read: Json object not found"};
9724         }
9725         
9726         if(o.metaData){
9727             
9728             delete this.ef;
9729             this.metaFromRemote = true;
9730             this.meta = o.metaData;
9731             this.recordType = Roo.data.Record.create(o.metaData.fields);
9732             this.onMetaChange(this.meta, this.recordType, o);
9733         }
9734         return this.readRecords(o);
9735     },
9736
9737     // private function a store will implement
9738     onMetaChange : function(meta, recordType, o){
9739
9740     },
9741
9742     /**
9743          * @ignore
9744          */
9745     simpleAccess: function(obj, subsc) {
9746         return obj[subsc];
9747     },
9748
9749         /**
9750          * @ignore
9751          */
9752     getJsonAccessor: function(){
9753         var re = /[\[\.]/;
9754         return function(expr) {
9755             try {
9756                 return(re.test(expr))
9757                     ? new Function("obj", "return obj." + expr)
9758                     : function(obj){
9759                         return obj[expr];
9760                     };
9761             } catch(e){}
9762             return Roo.emptyFn;
9763         };
9764     }(),
9765
9766     /**
9767      * Create a data block containing Roo.data.Records from an XML document.
9768      * @param {Object} o An object which contains an Array of row objects in the property specified
9769      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9770      * which contains the total size of the dataset.
9771      * @return {Object} data A data block which is used by an Roo.data.Store object as
9772      * a cache of Roo.data.Records.
9773      */
9774     readRecords : function(o){
9775         /**
9776          * After any data loads, the raw JSON data is available for further custom processing.
9777          * @type Object
9778          */
9779         this.o = o;
9780         var s = this.meta, Record = this.recordType,
9781             f = Record.prototype.fields, fi = f.items, fl = f.length;
9782
9783 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9784         if (!this.ef) {
9785             if(s.totalProperty) {
9786                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9787                 }
9788                 if(s.successProperty) {
9789                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9790                 }
9791                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9792                 if (s.id) {
9793                         var g = this.getJsonAccessor(s.id);
9794                         this.getId = function(rec) {
9795                                 var r = g(rec);
9796                                 return (r === undefined || r === "") ? null : r;
9797                         };
9798                 } else {
9799                         this.getId = function(){return null;};
9800                 }
9801             this.ef = [];
9802             for(var jj = 0; jj < fl; jj++){
9803                 f = fi[jj];
9804                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9805                 this.ef[jj] = this.getJsonAccessor(map);
9806             }
9807         }
9808
9809         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9810         if(s.totalProperty){
9811             var vt = parseInt(this.getTotal(o), 10);
9812             if(!isNaN(vt)){
9813                 totalRecords = vt;
9814             }
9815         }
9816         if(s.successProperty){
9817             var vs = this.getSuccess(o);
9818             if(vs === false || vs === 'false'){
9819                 success = false;
9820             }
9821         }
9822         var records = [];
9823             for(var i = 0; i < c; i++){
9824                     var n = root[i];
9825                 var values = {};
9826                 var id = this.getId(n);
9827                 for(var j = 0; j < fl; j++){
9828                     f = fi[j];
9829                 var v = this.ef[j](n);
9830                 if (!f.convert) {
9831                     Roo.log('missing convert for ' + f.name);
9832                     Roo.log(f);
9833                     continue;
9834                 }
9835                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9836                 }
9837                 var record = new Record(values, id);
9838                 record.json = n;
9839                 records[i] = record;
9840             }
9841             return {
9842             raw : o,
9843                 success : success,
9844                 records : records,
9845                 totalRecords : totalRecords
9846             };
9847     }
9848 });/*
9849  * Based on:
9850  * Ext JS Library 1.1.1
9851  * Copyright(c) 2006-2007, Ext JS, LLC.
9852  *
9853  * Originally Released Under LGPL - original licence link has changed is not relivant.
9854  *
9855  * Fork - LGPL
9856  * <script type="text/javascript">
9857  */
9858
9859 /**
9860  * @class Roo.data.ArrayReader
9861  * @extends Roo.data.DataReader
9862  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9863  * Each element of that Array represents a row of data fields. The
9864  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9865  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9866  * <p>
9867  * Example code:.
9868  * <pre><code>
9869 var RecordDef = Roo.data.Record.create([
9870     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
9871     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
9872 ]);
9873 var myReader = new Roo.data.ArrayReader({
9874     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
9875 }, RecordDef);
9876 </code></pre>
9877  * <p>
9878  * This would consume an Array like this:
9879  * <pre><code>
9880 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9881   </code></pre>
9882  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9883  * @constructor
9884  * Create a new JsonReader
9885  * @param {Object} meta Metadata configuration options.
9886  * @param {Object} recordType Either an Array of field definition objects
9887  * as specified to {@link Roo.data.Record#create},
9888  * or an {@link Roo.data.Record} object
9889  * created using {@link Roo.data.Record#create}.
9890  */
9891 Roo.data.ArrayReader = function(meta, recordType){
9892     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9893 };
9894
9895 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9896     /**
9897      * Create a data block containing Roo.data.Records from an XML document.
9898      * @param {Object} o An Array of row objects which represents the dataset.
9899      * @return {Object} data A data block which is used by an Roo.data.Store object as
9900      * a cache of Roo.data.Records.
9901      */
9902     readRecords : function(o){
9903         var sid = this.meta ? this.meta.id : null;
9904         var recordType = this.recordType, fields = recordType.prototype.fields;
9905         var records = [];
9906         var root = o;
9907             for(var i = 0; i < root.length; i++){
9908                     var n = root[i];
9909                 var values = {};
9910                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9911                 for(var j = 0, jlen = fields.length; j < jlen; j++){
9912                 var f = fields.items[j];
9913                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9914                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9915                 v = f.convert(v);
9916                 values[f.name] = v;
9917             }
9918                 var record = new recordType(values, id);
9919                 record.json = n;
9920                 records[records.length] = record;
9921             }
9922             return {
9923                 records : records,
9924                 totalRecords : records.length
9925             };
9926     }
9927 });/*
9928  * - LGPL
9929  * * 
9930  */
9931
9932 /**
9933  * @class Roo.bootstrap.ComboBox
9934  * @extends Roo.bootstrap.TriggerField
9935  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9936  * @cfg {Boolean} append (true|false) default false
9937  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
9938  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
9939  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
9940  * @constructor
9941  * Create a new ComboBox.
9942  * @param {Object} config Configuration options
9943  */
9944 Roo.bootstrap.ComboBox = function(config){
9945     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9946     this.addEvents({
9947         /**
9948          * @event expand
9949          * Fires when the dropdown list is expanded
9950              * @param {Roo.bootstrap.ComboBox} combo This combo box
9951              */
9952         'expand' : true,
9953         /**
9954          * @event collapse
9955          * Fires when the dropdown list is collapsed
9956              * @param {Roo.bootstrap.ComboBox} combo This combo box
9957              */
9958         'collapse' : true,
9959         /**
9960          * @event beforeselect
9961          * Fires before a list item is selected. Return false to cancel the selection.
9962              * @param {Roo.bootstrap.ComboBox} combo This combo box
9963              * @param {Roo.data.Record} record The data record returned from the underlying store
9964              * @param {Number} index The index of the selected item in the dropdown list
9965              */
9966         'beforeselect' : true,
9967         /**
9968          * @event select
9969          * Fires when a list item is selected
9970              * @param {Roo.bootstrap.ComboBox} combo This combo box
9971              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9972              * @param {Number} index The index of the selected item in the dropdown list
9973              */
9974         'select' : true,
9975         /**
9976          * @event beforequery
9977          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9978          * The event object passed has these properties:
9979              * @param {Roo.bootstrap.ComboBox} combo This combo box
9980              * @param {String} query The query
9981              * @param {Boolean} forceAll true to force "all" query
9982              * @param {Boolean} cancel true to cancel the query
9983              * @param {Object} e The query event object
9984              */
9985         'beforequery': true,
9986          /**
9987          * @event add
9988          * Fires when the 'add' icon is pressed (add a listener to enable add button)
9989              * @param {Roo.bootstrap.ComboBox} combo This combo box
9990              */
9991         'add' : true,
9992         /**
9993          * @event edit
9994          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9995              * @param {Roo.bootstrap.ComboBox} combo This combo box
9996              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9997              */
9998         'edit' : true,
9999         /**
10000          * @event remove
10001          * Fires when the remove value from the combobox array
10002              * @param {Roo.bootstrap.ComboBox} combo This combo box
10003              */
10004         'remove' : true
10005         
10006     });
10007     
10008     this.item = [];
10009     this.tickItems = [];
10010     
10011     this.selectedIndex = -1;
10012     if(this.mode == 'local'){
10013         if(config.queryDelay === undefined){
10014             this.queryDelay = 10;
10015         }
10016         if(config.minChars === undefined){
10017             this.minChars = 0;
10018         }
10019     }
10020 };
10021
10022 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10023      
10024     /**
10025      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10026      * rendering into an Roo.Editor, defaults to false)
10027      */
10028     /**
10029      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10030      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10031      */
10032     /**
10033      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10034      */
10035     /**
10036      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10037      * the dropdown list (defaults to undefined, with no header element)
10038      */
10039
10040      /**
10041      * @cfg {String/Roo.Template} tpl The template to use to render the output
10042      */
10043      
10044      /**
10045      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10046      */
10047     listWidth: undefined,
10048     /**
10049      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10050      * mode = 'remote' or 'text' if mode = 'local')
10051      */
10052     displayField: undefined,
10053     /**
10054      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10055      * mode = 'remote' or 'value' if mode = 'local'). 
10056      * Note: use of a valueField requires the user make a selection
10057      * in order for a value to be mapped.
10058      */
10059     valueField: undefined,
10060     
10061     
10062     /**
10063      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10064      * field's data value (defaults to the underlying DOM element's name)
10065      */
10066     hiddenName: undefined,
10067     /**
10068      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10069      */
10070     listClass: '',
10071     /**
10072      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10073      */
10074     selectedClass: 'active',
10075     
10076     /**
10077      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10078      */
10079     shadow:'sides',
10080     /**
10081      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10082      * anchor positions (defaults to 'tl-bl')
10083      */
10084     listAlign: 'tl-bl?',
10085     /**
10086      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10087      */
10088     maxHeight: 300,
10089     /**
10090      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10091      * query specified by the allQuery config option (defaults to 'query')
10092      */
10093     triggerAction: 'query',
10094     /**
10095      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10096      * (defaults to 4, does not apply if editable = false)
10097      */
10098     minChars : 4,
10099     /**
10100      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10101      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10102      */
10103     typeAhead: false,
10104     /**
10105      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10106      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10107      */
10108     queryDelay: 500,
10109     /**
10110      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10111      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10112      */
10113     pageSize: 0,
10114     /**
10115      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10116      * when editable = true (defaults to false)
10117      */
10118     selectOnFocus:false,
10119     /**
10120      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10121      */
10122     queryParam: 'query',
10123     /**
10124      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10125      * when mode = 'remote' (defaults to 'Loading...')
10126      */
10127     loadingText: 'Loading...',
10128     /**
10129      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10130      */
10131     resizable: false,
10132     /**
10133      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10134      */
10135     handleHeight : 8,
10136     /**
10137      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10138      * traditional select (defaults to true)
10139      */
10140     editable: true,
10141     /**
10142      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10143      */
10144     allQuery: '',
10145     /**
10146      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10147      */
10148     mode: 'remote',
10149     /**
10150      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10151      * listWidth has a higher value)
10152      */
10153     minListWidth : 70,
10154     /**
10155      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10156      * allow the user to set arbitrary text into the field (defaults to false)
10157      */
10158     forceSelection:false,
10159     /**
10160      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10161      * if typeAhead = true (defaults to 250)
10162      */
10163     typeAheadDelay : 250,
10164     /**
10165      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10166      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10167      */
10168     valueNotFoundText : undefined,
10169     /**
10170      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10171      */
10172     blockFocus : false,
10173     
10174     /**
10175      * @cfg {Boolean} disableClear Disable showing of clear button.
10176      */
10177     disableClear : false,
10178     /**
10179      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10180      */
10181     alwaysQuery : false,
10182     
10183     /**
10184      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10185      */
10186     multiple : false,
10187     
10188     //private
10189     addicon : false,
10190     editicon: false,
10191     
10192     page: 0,
10193     hasQuery: false,
10194     append: false,
10195     loadNext: false,
10196     autoFocus : true,
10197     tickable : false,
10198     btnPosition : 'right',
10199     
10200     // element that contains real text value.. (when hidden is used..)
10201     
10202     getAutoCreate : function()
10203     {
10204         var cfg = false;
10205         
10206         /*
10207          *  Normal ComboBox
10208          */
10209         if(!this.tickable){
10210             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10211             return cfg;
10212         }
10213         
10214         /*
10215          *  ComboBox with tickable selections
10216          */
10217              
10218         var align = this.labelAlign || this.parentLabelAlign();
10219         
10220         cfg = {
10221             cls : 'form-group roo-combobox-tickable' //input-group
10222         };
10223         
10224         
10225         var buttons = {
10226             tag : 'div',
10227             cls : 'tickable-buttons',
10228             cn : [
10229                 {
10230                     tag : 'button',
10231                     type : 'button',
10232                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10233                     html : 'Edit'
10234                 },
10235                 {
10236                     tag : 'button',
10237                     type : 'button',
10238                     name : 'ok',
10239                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10240                     html : 'Done'
10241                 },
10242                 {
10243                     tag : 'button',
10244                     type : 'button',
10245                     name : 'cancel',
10246                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10247                     html : 'Cancel'
10248                 }
10249             ]
10250         };
10251         
10252         var _this = this;
10253         Roo.each(buttons.cn, function(c){
10254             if (_this.size) {
10255                 c.cls += ' btn-' + _this.size;
10256             }
10257
10258             if (_this.disabled) {
10259                 c.disabled = true;
10260             }
10261         });
10262         
10263         var box = {
10264             tag: 'div',
10265             cn: [
10266                 {
10267                     tag: 'input',
10268                     type : 'hidden',
10269                     cls: 'form-hidden-field'
10270                 },
10271                 {
10272                     tag: 'ul',
10273                     cls: 'select2-choices',
10274                     cn:[
10275                         {
10276                             tag: 'li',
10277                             cls: 'select2-search-field',
10278                             cn: [
10279
10280                                 buttons
10281                             ]
10282                         }
10283                     ]
10284                 }
10285             ]
10286         }
10287         
10288         var combobox = {
10289             cls: 'select2-container input-group select2-container-multi',
10290             cn: [
10291                 box,
10292                 {
10293                     tag: 'ul',
10294                     cls: 'typeahead typeahead-long dropdown-menu',
10295                     style: 'display:none; max-height:' + this.maxHeight + 'px;'
10296                 }
10297             ]
10298         };
10299         
10300         if (align ==='left' && this.fieldLabel.length) {
10301             
10302                 Roo.log("left and has label");
10303                 cfg.cn = [
10304                     
10305                     {
10306                         tag: 'label',
10307                         'for' :  id,
10308                         cls : 'control-label col-sm-' + this.labelWidth,
10309                         html : this.fieldLabel
10310                         
10311                     },
10312                     {
10313                         cls : "col-sm-" + (12 - this.labelWidth), 
10314                         cn: [
10315                             combobox
10316                         ]
10317                     }
10318                     
10319                 ];
10320         } else if ( this.fieldLabel.length) {
10321                 Roo.log(" label");
10322                  cfg.cn = [
10323                    
10324                     {
10325                         tag: 'label',
10326                         //cls : 'input-group-addon',
10327                         html : this.fieldLabel
10328                         
10329                     },
10330                     
10331                     combobox
10332                     
10333                 ];
10334
10335         } else {
10336             
10337                 Roo.log(" no label && no align");
10338                 cfg = combobox
10339                      
10340                 
10341         }
10342          
10343         var settings=this;
10344         ['xs','sm','md','lg'].map(function(size){
10345             if (settings[size]) {
10346                 cfg.cls += ' col-' + size + '-' + settings[size];
10347             }
10348         });
10349         
10350         return cfg;
10351         
10352     },
10353     
10354     // private
10355     initEvents: function()
10356     {
10357         
10358         if (!this.store) {
10359             throw "can not find store for combo";
10360         }
10361         this.store = Roo.factory(this.store, Roo.data);
10362         
10363         if(this.tickable){
10364             this.initTickableEvnets();
10365             return;
10366         }
10367         
10368         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10369         
10370         
10371         if(this.hiddenName){
10372             
10373             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10374             
10375             this.hiddenField.dom.value =
10376                 this.hiddenValue !== undefined ? this.hiddenValue :
10377                 this.value !== undefined ? this.value : '';
10378
10379             // prevent input submission
10380             this.el.dom.removeAttribute('name');
10381             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10382              
10383              
10384         }
10385         //if(Roo.isGecko){
10386         //    this.el.dom.setAttribute('autocomplete', 'off');
10387         //}
10388
10389         var cls = 'x-combo-list';
10390         this.list = this.el.select('ul.dropdown-menu',true).first();
10391
10392         //this.list = new Roo.Layer({
10393         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10394         //});
10395         
10396         var _this = this;
10397         
10398         (function(){
10399             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10400             _this.list.setWidth(lw);
10401         }).defer(100);
10402         
10403         this.list.on('mouseover', this.onViewOver, this);
10404         this.list.on('mousemove', this.onViewMove, this);
10405         
10406         this.list.on('scroll', this.onViewScroll, this);
10407         
10408         /*
10409         this.list.swallowEvent('mousewheel');
10410         this.assetHeight = 0;
10411
10412         if(this.title){
10413             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10414             this.assetHeight += this.header.getHeight();
10415         }
10416
10417         this.innerList = this.list.createChild({cls:cls+'-inner'});
10418         this.innerList.on('mouseover', this.onViewOver, this);
10419         this.innerList.on('mousemove', this.onViewMove, this);
10420         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10421         
10422         if(this.allowBlank && !this.pageSize && !this.disableClear){
10423             this.footer = this.list.createChild({cls:cls+'-ft'});
10424             this.pageTb = new Roo.Toolbar(this.footer);
10425            
10426         }
10427         if(this.pageSize){
10428             this.footer = this.list.createChild({cls:cls+'-ft'});
10429             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10430                     {pageSize: this.pageSize});
10431             
10432         }
10433         
10434         if (this.pageTb && this.allowBlank && !this.disableClear) {
10435             var _this = this;
10436             this.pageTb.add(new Roo.Toolbar.Fill(), {
10437                 cls: 'x-btn-icon x-btn-clear',
10438                 text: '&#160;',
10439                 handler: function()
10440                 {
10441                     _this.collapse();
10442                     _this.clearValue();
10443                     _this.onSelect(false, -1);
10444                 }
10445             });
10446         }
10447         if (this.footer) {
10448             this.assetHeight += this.footer.getHeight();
10449         }
10450         */
10451             
10452         if(!this.tpl){
10453             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10454         }
10455
10456         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10457             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10458         });
10459         //this.view.wrapEl.setDisplayed(false);
10460         this.view.on('click', this.onViewClick, this);
10461         
10462         
10463         
10464         this.store.on('beforeload', this.onBeforeLoad, this);
10465         this.store.on('load', this.onLoad, this);
10466         this.store.on('loadexception', this.onLoadException, this);
10467         /*
10468         if(this.resizable){
10469             this.resizer = new Roo.Resizable(this.list,  {
10470                pinned:true, handles:'se'
10471             });
10472             this.resizer.on('resize', function(r, w, h){
10473                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10474                 this.listWidth = w;
10475                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10476                 this.restrictHeight();
10477             }, this);
10478             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10479         }
10480         */
10481         if(!this.editable){
10482             this.editable = true;
10483             this.setEditable(false);
10484         }
10485         
10486         /*
10487         
10488         if (typeof(this.events.add.listeners) != 'undefined') {
10489             
10490             this.addicon = this.wrap.createChild(
10491                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10492        
10493             this.addicon.on('click', function(e) {
10494                 this.fireEvent('add', this);
10495             }, this);
10496         }
10497         if (typeof(this.events.edit.listeners) != 'undefined') {
10498             
10499             this.editicon = this.wrap.createChild(
10500                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10501             if (this.addicon) {
10502                 this.editicon.setStyle('margin-left', '40px');
10503             }
10504             this.editicon.on('click', function(e) {
10505                 
10506                 // we fire even  if inothing is selected..
10507                 this.fireEvent('edit', this, this.lastData );
10508                 
10509             }, this);
10510         }
10511         */
10512         
10513         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10514             "up" : function(e){
10515                 this.inKeyMode = true;
10516                 this.selectPrev();
10517             },
10518
10519             "down" : function(e){
10520                 if(!this.isExpanded()){
10521                     this.onTriggerClick();
10522                 }else{
10523                     this.inKeyMode = true;
10524                     this.selectNext();
10525                 }
10526             },
10527
10528             "enter" : function(e){
10529 //                this.onViewClick();
10530                 //return true;
10531                 this.collapse();
10532                 
10533                 if(this.fireEvent("specialkey", this, e)){
10534                     this.onViewClick(false);
10535                 }
10536                 
10537                 return true;
10538             },
10539
10540             "esc" : function(e){
10541                 this.collapse();
10542             },
10543
10544             "tab" : function(e){
10545                 this.collapse();
10546                 
10547                 if(this.fireEvent("specialkey", this, e)){
10548                     this.onViewClick(false);
10549                 }
10550                 
10551                 return true;
10552             },
10553
10554             scope : this,
10555
10556             doRelay : function(foo, bar, hname){
10557                 if(hname == 'down' || this.scope.isExpanded()){
10558                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10559                 }
10560                 return true;
10561             },
10562
10563             forceKeyDown: true
10564         });
10565         
10566         
10567         this.queryDelay = Math.max(this.queryDelay || 10,
10568                 this.mode == 'local' ? 10 : 250);
10569         
10570         
10571         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10572         
10573         if(this.typeAhead){
10574             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10575         }
10576         if(this.editable !== false){
10577             this.inputEl().on("keyup", this.onKeyUp, this);
10578         }
10579         if(this.forceSelection){
10580             this.inputEl().on('blur', this.doForce, this);
10581         }
10582         
10583         if(this.multiple){
10584             this.choices = this.el.select('ul.select2-choices', true).first();
10585             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10586         }
10587     },
10588     
10589     initTickableEvnets: function()
10590     {   
10591         if(this.hiddenName){
10592             
10593             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10594             
10595             this.hiddenField.dom.value =
10596                 this.hiddenValue !== undefined ? this.hiddenValue :
10597                 this.value !== undefined ? this.value : '';
10598
10599             // prevent input submission
10600             this.el.dom.removeAttribute('name');
10601             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10602              
10603              
10604         }
10605         
10606         this.list = this.el.select('ul.dropdown-menu',true).first();
10607         
10608         this.choices = this.el.select('ul.select2-choices', true).first();
10609         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10610         this.searchField.on("click", this.onTriggerClick, this, {preventDefault:true});
10611          
10612         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10613         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10614         
10615         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10616         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10617         
10618         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10619         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10620         
10621         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10622         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10623         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10624         
10625         this.okBtn.hide();
10626         this.cancelBtn.hide();
10627         
10628         var _this = this;
10629         
10630         (function(){
10631             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10632             _this.list.setWidth(lw);
10633         }).defer(100);
10634         
10635         this.list.on('mouseover', this.onViewOver, this);
10636         this.list.on('mousemove', this.onViewMove, this);
10637         
10638         this.list.on('scroll', this.onViewScroll, this);
10639         
10640         if(!this.tpl){
10641             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
10642         }
10643
10644         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10645             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10646         });
10647         
10648         //this.view.wrapEl.setDisplayed(false);
10649         this.view.on('click', this.onViewClick, this);
10650         
10651         
10652         
10653         this.store.on('beforeload', this.onBeforeLoad, this);
10654         this.store.on('load', this.onLoad, this);
10655         this.store.on('loadexception', this.onLoadException, this);
10656         
10657 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10658 //            "up" : function(e){
10659 //                this.inKeyMode = true;
10660 //                this.selectPrev();
10661 //            },
10662 //
10663 //            "down" : function(e){
10664 //                if(!this.isExpanded()){
10665 //                    this.onTriggerClick();
10666 //                }else{
10667 //                    this.inKeyMode = true;
10668 //                    this.selectNext();
10669 //                }
10670 //            },
10671 //
10672 //            "enter" : function(e){
10673 ////                this.onViewClick();
10674 //                //return true;
10675 //                this.collapse();
10676 //                
10677 //                if(this.fireEvent("specialkey", this, e)){
10678 //                    this.onViewClick(false);
10679 //                }
10680 //                
10681 //                return true;
10682 //            },
10683 //
10684 //            "esc" : function(e){
10685 //                this.collapse();
10686 //            },
10687 //
10688 //            "tab" : function(e){
10689 //                this.collapse();
10690 //                
10691 //                if(this.fireEvent("specialkey", this, e)){
10692 //                    this.onViewClick(false);
10693 //                }
10694 //                
10695 //                return true;
10696 //            },
10697 //
10698 //            scope : this,
10699 //
10700 //            doRelay : function(foo, bar, hname){
10701 //                if(hname == 'down' || this.scope.isExpanded()){
10702 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10703 //                }
10704 //                return true;
10705 //            },
10706 //
10707 //            forceKeyDown: true
10708 //        });
10709         
10710         
10711         this.queryDelay = Math.max(this.queryDelay || 10,
10712                 this.mode == 'local' ? 10 : 250);
10713         
10714         
10715         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10716         
10717         if(this.typeAhead){
10718             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10719         }
10720     },
10721
10722     onDestroy : function(){
10723         if(this.view){
10724             this.view.setStore(null);
10725             this.view.el.removeAllListeners();
10726             this.view.el.remove();
10727             this.view.purgeListeners();
10728         }
10729         if(this.list){
10730             this.list.dom.innerHTML  = '';
10731         }
10732         
10733         if(this.store){
10734             this.store.un('beforeload', this.onBeforeLoad, this);
10735             this.store.un('load', this.onLoad, this);
10736             this.store.un('loadexception', this.onLoadException, this);
10737         }
10738         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10739     },
10740
10741     // private
10742     fireKey : function(e){
10743         if(e.isNavKeyPress() && !this.list.isVisible()){
10744             this.fireEvent("specialkey", this, e);
10745         }
10746     },
10747
10748     // private
10749     onResize: function(w, h){
10750 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10751 //        
10752 //        if(typeof w != 'number'){
10753 //            // we do not handle it!?!?
10754 //            return;
10755 //        }
10756 //        var tw = this.trigger.getWidth();
10757 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10758 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10759 //        var x = w - tw;
10760 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10761 //            
10762 //        //this.trigger.setStyle('left', x+'px');
10763 //        
10764 //        if(this.list && this.listWidth === undefined){
10765 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10766 //            this.list.setWidth(lw);
10767 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10768 //        }
10769         
10770     
10771         
10772     },
10773
10774     /**
10775      * Allow or prevent the user from directly editing the field text.  If false is passed,
10776      * the user will only be able to select from the items defined in the dropdown list.  This method
10777      * is the runtime equivalent of setting the 'editable' config option at config time.
10778      * @param {Boolean} value True to allow the user to directly edit the field text
10779      */
10780     setEditable : function(value){
10781         if(value == this.editable){
10782             return;
10783         }
10784         this.editable = value;
10785         if(!value){
10786             this.inputEl().dom.setAttribute('readOnly', true);
10787             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10788             this.inputEl().addClass('x-combo-noedit');
10789         }else{
10790             this.inputEl().dom.setAttribute('readOnly', false);
10791             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10792             this.inputEl().removeClass('x-combo-noedit');
10793         }
10794     },
10795
10796     // private
10797     
10798     onBeforeLoad : function(combo,opts){
10799         if(!this.hasFocus){
10800             return;
10801         }
10802          if (!opts.add) {
10803             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10804          }
10805         this.restrictHeight();
10806         this.selectedIndex = -1;
10807     },
10808
10809     // private
10810     onLoad : function(){
10811         
10812         this.hasQuery = false;
10813         
10814         if(!this.hasFocus){
10815             return;
10816         }
10817         
10818         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10819             this.loading.hide();
10820         }
10821         
10822         if(this.store.getCount() > 0){
10823             this.expand();
10824             this.restrictHeight();
10825             if(this.lastQuery == this.allQuery){
10826                 if(this.editable && !this.tickable){
10827                     this.inputEl().dom.select();
10828                 }
10829                 if(!this.selectByValue(this.value, true) && this.autoFocus){
10830                     this.select(0, true);
10831                 }
10832             }else{
10833                 if(this.autoFocus){
10834                     this.selectNext();
10835                 }
10836                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10837                     this.taTask.delay(this.typeAheadDelay);
10838                 }
10839             }
10840         }else{
10841             this.onEmptyResults();
10842         }
10843         
10844         //this.el.focus();
10845     },
10846     // private
10847     onLoadException : function()
10848     {
10849         this.hasQuery = false;
10850         
10851         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10852             this.loading.hide();
10853         }
10854         
10855         this.collapse();
10856         Roo.log(this.store.reader.jsonData);
10857         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
10858             // fixme
10859             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
10860         }
10861         
10862         
10863     },
10864     // private
10865     onTypeAhead : function(){
10866         if(this.store.getCount() > 0){
10867             var r = this.store.getAt(0);
10868             var newValue = r.data[this.displayField];
10869             var len = newValue.length;
10870             var selStart = this.getRawValue().length;
10871             
10872             if(selStart != len){
10873                 this.setRawValue(newValue);
10874                 this.selectText(selStart, newValue.length);
10875             }
10876         }
10877     },
10878
10879     // private
10880     onSelect : function(record, index){
10881         
10882         if(this.fireEvent('beforeselect', this, record, index) !== false){
10883         
10884             this.setFromData(index > -1 ? record.data : false);
10885             
10886             this.collapse();
10887             this.fireEvent('select', this, record, index);
10888         }
10889     },
10890
10891     /**
10892      * Returns the currently selected field value or empty string if no value is set.
10893      * @return {String} value The selected value
10894      */
10895     getValue : function(){
10896         
10897         if(this.multiple){
10898             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
10899         }
10900         
10901         if(this.valueField){
10902             return typeof this.value != 'undefined' ? this.value : '';
10903         }else{
10904             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
10905         }
10906     },
10907
10908     /**
10909      * Clears any text/value currently set in the field
10910      */
10911     clearValue : function(){
10912         if(this.hiddenField){
10913             this.hiddenField.dom.value = '';
10914         }
10915         this.value = '';
10916         this.setRawValue('');
10917         this.lastSelectionText = '';
10918         
10919     },
10920
10921     /**
10922      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
10923      * will be displayed in the field.  If the value does not match the data value of an existing item,
10924      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
10925      * Otherwise the field will be blank (although the value will still be set).
10926      * @param {String} value The value to match
10927      */
10928     setValue : function(v){
10929         if(this.multiple){
10930             this.syncValue();
10931             return;
10932         }
10933         
10934         var text = v;
10935         if(this.valueField){
10936             var r = this.findRecord(this.valueField, v);
10937             if(r){
10938                 text = r.data[this.displayField];
10939             }else if(this.valueNotFoundText !== undefined){
10940                 text = this.valueNotFoundText;
10941             }
10942         }
10943         this.lastSelectionText = text;
10944         if(this.hiddenField){
10945             this.hiddenField.dom.value = v;
10946         }
10947         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
10948         this.value = v;
10949     },
10950     /**
10951      * @property {Object} the last set data for the element
10952      */
10953     
10954     lastData : false,
10955     /**
10956      * Sets the value of the field based on a object which is related to the record format for the store.
10957      * @param {Object} value the value to set as. or false on reset?
10958      */
10959     setFromData : function(o){
10960         
10961         if(this.multiple){
10962             this.addItem(o);
10963             return;
10964         }
10965             
10966         var dv = ''; // display value
10967         var vv = ''; // value value..
10968         this.lastData = o;
10969         if (this.displayField) {
10970             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10971         } else {
10972             // this is an error condition!!!
10973             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10974         }
10975         
10976         if(this.valueField){
10977             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
10978         }
10979         
10980         if(this.hiddenField){
10981             this.hiddenField.dom.value = vv;
10982             
10983             this.lastSelectionText = dv;
10984             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
10985             this.value = vv;
10986             return;
10987         }
10988         // no hidden field.. - we store the value in 'value', but still display
10989         // display field!!!!
10990         this.lastSelectionText = dv;
10991         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
10992         this.value = vv;
10993         
10994         
10995     },
10996     // private
10997     reset : function(){
10998         // overridden so that last data is reset..
10999         this.setValue(this.originalValue);
11000         this.clearInvalid();
11001         this.lastData = false;
11002         if (this.view) {
11003             this.view.clearSelections();
11004         }
11005     },
11006     // private
11007     findRecord : function(prop, value){
11008         var record;
11009         if(this.store.getCount() > 0){
11010             this.store.each(function(r){
11011                 if(r.data[prop] == value){
11012                     record = r;
11013                     return false;
11014                 }
11015                 return true;
11016             });
11017         }
11018         return record;
11019     },
11020     
11021     getName: function()
11022     {
11023         // returns hidden if it's set..
11024         if (!this.rendered) {return ''};
11025         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11026         
11027     },
11028     // private
11029     onViewMove : function(e, t){
11030         this.inKeyMode = false;
11031     },
11032
11033     // private
11034     onViewOver : function(e, t){
11035         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11036             return;
11037         }
11038         var item = this.view.findItemFromChild(t);
11039         
11040         if(item){
11041             var index = this.view.indexOf(item);
11042             this.select(index, false);
11043         }
11044     },
11045
11046     // private
11047     onViewClick : function(view, doFocus, el, e)
11048     {
11049         var index = this.view.getSelectedIndexes()[0];
11050         
11051         var r = this.store.getAt(index);
11052         
11053         if(this.tickable){
11054             
11055             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11056                 return;
11057             }
11058             
11059             var rm = false;
11060             var _this = this;
11061             
11062             Roo.each(this.tickItems, function(v,k){
11063                 
11064                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11065                     _this.tickItems.splice(k, 1);
11066                     rm = true;
11067                     return;
11068                 }
11069             })
11070             
11071             if(rm){
11072                 return;
11073             }
11074             
11075             this.tickItems.push(r.data);
11076             return;
11077         }
11078         
11079         if(r){
11080             this.onSelect(r, index);
11081         }
11082         if(doFocus !== false && !this.blockFocus){
11083             this.inputEl().focus();
11084         }
11085     },
11086
11087     // private
11088     restrictHeight : function(){
11089         //this.innerList.dom.style.height = '';
11090         //var inner = this.innerList.dom;
11091         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11092         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11093         //this.list.beginUpdate();
11094         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11095         this.list.alignTo(this.inputEl(), this.listAlign);
11096         //this.list.endUpdate();
11097     },
11098
11099     // private
11100     onEmptyResults : function(){
11101         this.collapse();
11102     },
11103
11104     /**
11105      * Returns true if the dropdown list is expanded, else false.
11106      */
11107     isExpanded : function(){
11108         return this.list.isVisible();
11109     },
11110
11111     /**
11112      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11113      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11114      * @param {String} value The data value of the item to select
11115      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11116      * selected item if it is not currently in view (defaults to true)
11117      * @return {Boolean} True if the value matched an item in the list, else false
11118      */
11119     selectByValue : function(v, scrollIntoView){
11120         if(v !== undefined && v !== null){
11121             var r = this.findRecord(this.valueField || this.displayField, v);
11122             if(r){
11123                 this.select(this.store.indexOf(r), scrollIntoView);
11124                 return true;
11125             }
11126         }
11127         return false;
11128     },
11129
11130     /**
11131      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11132      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11133      * @param {Number} index The zero-based index of the list item to select
11134      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11135      * selected item if it is not currently in view (defaults to true)
11136      */
11137     select : function(index, scrollIntoView){
11138         this.selectedIndex = index;
11139         this.view.select(index);
11140         if(scrollIntoView !== false){
11141             var el = this.view.getNode(index);
11142             if(el){
11143                 //this.innerList.scrollChildIntoView(el, false);
11144                 
11145             }
11146         }
11147     },
11148
11149     // private
11150     selectNext : function(){
11151         var ct = this.store.getCount();
11152         if(ct > 0){
11153             if(this.selectedIndex == -1){
11154                 this.select(0);
11155             }else if(this.selectedIndex < ct-1){
11156                 this.select(this.selectedIndex+1);
11157             }
11158         }
11159     },
11160
11161     // private
11162     selectPrev : function(){
11163         var ct = this.store.getCount();
11164         if(ct > 0){
11165             if(this.selectedIndex == -1){
11166                 this.select(0);
11167             }else if(this.selectedIndex != 0){
11168                 this.select(this.selectedIndex-1);
11169             }
11170         }
11171     },
11172
11173     // private
11174     onKeyUp : function(e){
11175         if(this.editable !== false && !e.isSpecialKey()){
11176             this.lastKey = e.getKey();
11177             this.dqTask.delay(this.queryDelay);
11178         }
11179     },
11180
11181     // private
11182     validateBlur : function(){
11183         return !this.list || !this.list.isVisible();   
11184     },
11185
11186     // private
11187     initQuery : function(){
11188         this.doQuery(this.getRawValue());
11189     },
11190
11191     // private
11192     doForce : function(){
11193         if(this.inputEl().dom.value.length > 0){
11194             this.inputEl().dom.value =
11195                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11196              
11197         }
11198     },
11199
11200     /**
11201      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11202      * query allowing the query action to be canceled if needed.
11203      * @param {String} query The SQL query to execute
11204      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11205      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11206      * saved in the current store (defaults to false)
11207      */
11208     doQuery : function(q, forceAll){
11209         
11210         if(q === undefined || q === null){
11211             q = '';
11212         }
11213         var qe = {
11214             query: q,
11215             forceAll: forceAll,
11216             combo: this,
11217             cancel:false
11218         };
11219         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11220             return false;
11221         }
11222         q = qe.query;
11223         
11224         forceAll = qe.forceAll;
11225         if(forceAll === true || (q.length >= this.minChars)){
11226             
11227             this.hasQuery = true;
11228             
11229             if(this.lastQuery != q || this.alwaysQuery){
11230                 this.lastQuery = q;
11231                 if(this.mode == 'local'){
11232                     this.selectedIndex = -1;
11233                     if(forceAll){
11234                         this.store.clearFilter();
11235                     }else{
11236                         this.store.filter(this.displayField, q);
11237                     }
11238                     this.onLoad();
11239                 }else{
11240                     this.store.baseParams[this.queryParam] = q;
11241                     
11242                     var options = {params : this.getParams(q)};
11243                     
11244                     if(this.loadNext){
11245                         options.add = true;
11246                         options.params.start = this.page * this.pageSize;
11247                     }
11248                     
11249                     this.store.load(options);
11250                     /*
11251                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11252                      *  we should expand the list on onLoad
11253                      *  so command out it
11254                      */
11255 //                    this.expand();
11256                 }
11257             }else{
11258                 this.selectedIndex = -1;
11259                 this.onLoad();   
11260             }
11261         }
11262         
11263         this.loadNext = false;
11264     },
11265
11266     // private
11267     getParams : function(q){
11268         var p = {};
11269         //p[this.queryParam] = q;
11270         
11271         if(this.pageSize){
11272             p.start = 0;
11273             p.limit = this.pageSize;
11274         }
11275         return p;
11276     },
11277
11278     /**
11279      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11280      */
11281     collapse : function(){
11282         if(!this.isExpanded()){
11283             return;
11284         }
11285         
11286         this.list.hide();
11287         
11288         if(this.tickable){
11289             this.okBtn.hide();
11290             this.cancelBtn.hide();
11291             this.trigger.show();
11292         }
11293         
11294         Roo.get(document).un('mousedown', this.collapseIf, this);
11295         Roo.get(document).un('mousewheel', this.collapseIf, this);
11296         if (!this.editable) {
11297             Roo.get(document).un('keydown', this.listKeyPress, this);
11298         }
11299         this.fireEvent('collapse', this);
11300     },
11301
11302     // private
11303     collapseIf : function(e){
11304         var in_combo  = e.within(this.el);
11305         var in_list =  e.within(this.list);
11306         
11307         if (in_combo || in_list) {
11308             //e.stopPropagation();
11309             return;
11310         }
11311         
11312         if(this.tickable){
11313             this.onTickableFooterButtonClick(e, false, false);
11314         }
11315
11316         this.collapse();
11317         
11318     },
11319
11320     /**
11321      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11322      */
11323     expand : function(){
11324        
11325         if(this.isExpanded() || !this.hasFocus){
11326             return;
11327         }
11328          Roo.log('expand');
11329         this.list.alignTo(this.inputEl(), this.listAlign);
11330         this.list.show();
11331         
11332         if(this.tickable){
11333             
11334             this.tickItems = Roo.apply([], this.item);
11335             
11336             this.okBtn.show();
11337             this.cancelBtn.show();
11338             this.trigger.hide();
11339             
11340         }
11341         
11342         Roo.get(document).on('mousedown', this.collapseIf, this);
11343         Roo.get(document).on('mousewheel', this.collapseIf, this);
11344         if (!this.editable) {
11345             Roo.get(document).on('keydown', this.listKeyPress, this);
11346         }
11347         
11348         this.fireEvent('expand', this);
11349     },
11350
11351     // private
11352     // Implements the default empty TriggerField.onTriggerClick function
11353     onTriggerClick : function()
11354     {
11355         Roo.log('trigger click');
11356         
11357         if(this.disabled){
11358             return;
11359         }
11360         
11361         if(this.tickable){
11362             this.onTickableTriggerClick();
11363             return;
11364         }
11365         
11366         this.page = 0;
11367         this.loadNext = false;
11368         
11369         if(this.isExpanded()){
11370             this.collapse();
11371             if (!this.blockFocus) {
11372                 this.inputEl().focus();
11373             }
11374             
11375         }else {
11376             this.hasFocus = true;
11377             if(this.triggerAction == 'all') {
11378                 this.doQuery(this.allQuery, true);
11379             } else {
11380                 this.doQuery(this.getRawValue());
11381             }
11382             if (!this.blockFocus) {
11383                 this.inputEl().focus();
11384             }
11385         }
11386     },
11387     
11388     onTickableTriggerClick : function()
11389     {
11390         this.page = 0;
11391         this.loadNext = false;
11392         this.hasFocus = true;
11393         
11394         if(this.triggerAction == 'all') {
11395             this.doQuery(this.allQuery, true);
11396         } else {
11397             this.doQuery(this.getRawValue());
11398         }
11399     },
11400     
11401     listKeyPress : function(e)
11402     {
11403         //Roo.log('listkeypress');
11404         // scroll to first matching element based on key pres..
11405         if (e.isSpecialKey()) {
11406             return false;
11407         }
11408         var k = String.fromCharCode(e.getKey()).toUpperCase();
11409         //Roo.log(k);
11410         var match  = false;
11411         var csel = this.view.getSelectedNodes();
11412         var cselitem = false;
11413         if (csel.length) {
11414             var ix = this.view.indexOf(csel[0]);
11415             cselitem  = this.store.getAt(ix);
11416             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11417                 cselitem = false;
11418             }
11419             
11420         }
11421         
11422         this.store.each(function(v) { 
11423             if (cselitem) {
11424                 // start at existing selection.
11425                 if (cselitem.id == v.id) {
11426                     cselitem = false;
11427                 }
11428                 return true;
11429             }
11430                 
11431             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11432                 match = this.store.indexOf(v);
11433                 return false;
11434             }
11435             return true;
11436         }, this);
11437         
11438         if (match === false) {
11439             return true; // no more action?
11440         }
11441         // scroll to?
11442         this.view.select(match);
11443         var sn = Roo.get(this.view.getSelectedNodes()[0])
11444         //sn.scrollIntoView(sn.dom.parentNode, false);
11445     },
11446     
11447     onViewScroll : function(e, t){
11448         
11449         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11450             return;
11451         }
11452         
11453         this.hasQuery = true;
11454         
11455         this.loading = this.list.select('.loading', true).first();
11456         
11457         if(this.loading === null){
11458             this.list.createChild({
11459                 tag: 'div',
11460                 cls: 'loading select2-more-results select2-active',
11461                 html: 'Loading more results...'
11462             })
11463             
11464             this.loading = this.list.select('.loading', true).first();
11465             
11466             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11467             
11468             this.loading.hide();
11469         }
11470         
11471         this.loading.show();
11472         
11473         var _combo = this;
11474         
11475         this.page++;
11476         this.loadNext = true;
11477         
11478         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11479         
11480         return;
11481     },
11482     
11483     addItem : function(o)
11484     {   
11485         var dv = ''; // display value
11486         
11487         if (this.displayField) {
11488             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11489         } else {
11490             // this is an error condition!!!
11491             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11492         }
11493         
11494         if(!dv.length){
11495             return;
11496         }
11497         
11498         var choice = this.choices.createChild({
11499             tag: 'li',
11500             cls: 'select2-search-choice',
11501             cn: [
11502                 {
11503                     tag: 'div',
11504                     html: dv
11505                 },
11506                 {
11507                     tag: 'a',
11508                     href: '#',
11509                     cls: 'select2-search-choice-close',
11510                     tabindex: '-1'
11511                 }
11512             ]
11513             
11514         }, this.searchField);
11515         
11516         var close = choice.select('a.select2-search-choice-close', true).first()
11517         
11518         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11519         
11520         this.item.push(o);
11521         
11522         this.lastData = o;
11523         
11524         this.syncValue();
11525         
11526         this.inputEl().dom.value = '';
11527         
11528     },
11529     
11530     onRemoveItem : function(e, _self, o)
11531     {
11532         e.preventDefault();
11533         var index = this.item.indexOf(o.data) * 1;
11534         
11535         if( index < 0){
11536             Roo.log('not this item?!');
11537             return;
11538         }
11539         
11540         this.item.splice(index, 1);
11541         o.item.remove();
11542         
11543         this.syncValue();
11544         
11545         this.fireEvent('remove', this, e);
11546         
11547     },
11548     
11549     syncValue : function()
11550     {
11551         if(!this.item.length){
11552             this.clearValue();
11553             return;
11554         }
11555             
11556         var value = [];
11557         var _this = this;
11558         Roo.each(this.item, function(i){
11559             if(_this.valueField){
11560                 value.push(i[_this.valueField]);
11561                 return;
11562             }
11563
11564             value.push(i);
11565         });
11566
11567         this.value = value.join(',');
11568
11569         if(this.hiddenField){
11570             this.hiddenField.dom.value = this.value;
11571         }
11572     },
11573     
11574     clearItem : function()
11575     {
11576         if(!this.multiple){
11577             return;
11578         }
11579         
11580         this.item = [];
11581         
11582         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11583            c.remove();
11584         });
11585         
11586         this.syncValue();
11587     },
11588     
11589     inputEl: function ()
11590     {
11591         if(this.tickable){
11592             return this.searchField;
11593         }
11594         return this.el.select('input.form-control',true).first();
11595     },
11596     
11597     
11598     onTickableFooterButtonClick : function(e, btn, el)
11599     {
11600         e.preventDefault();
11601         
11602         if(btn && btn.name == 'cancel'){
11603             this.tickItems = Roo.apply([], this.item);
11604             this.collapse();
11605             return;
11606         }
11607         
11608         this.clearItem();
11609         
11610         var _this = this;
11611         
11612         Roo.each(this.tickItems, function(o){
11613             _this.addItem(o);
11614         });
11615         
11616         this.collapse();
11617         
11618     }
11619     
11620     
11621
11622     /** 
11623     * @cfg {Boolean} grow 
11624     * @hide 
11625     */
11626     /** 
11627     * @cfg {Number} growMin 
11628     * @hide 
11629     */
11630     /** 
11631     * @cfg {Number} growMax 
11632     * @hide 
11633     */
11634     /**
11635      * @hide
11636      * @method autoSize
11637      */
11638 });
11639 /*
11640  * Based on:
11641  * Ext JS Library 1.1.1
11642  * Copyright(c) 2006-2007, Ext JS, LLC.
11643  *
11644  * Originally Released Under LGPL - original licence link has changed is not relivant.
11645  *
11646  * Fork - LGPL
11647  * <script type="text/javascript">
11648  */
11649
11650 /**
11651  * @class Roo.View
11652  * @extends Roo.util.Observable
11653  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11654  * This class also supports single and multi selection modes. <br>
11655  * Create a data model bound view:
11656  <pre><code>
11657  var store = new Roo.data.Store(...);
11658
11659  var view = new Roo.View({
11660     el : "my-element",
11661     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11662  
11663     singleSelect: true,
11664     selectedClass: "ydataview-selected",
11665     store: store
11666  });
11667
11668  // listen for node click?
11669  view.on("click", function(vw, index, node, e){
11670  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11671  });
11672
11673  // load XML data
11674  dataModel.load("foobar.xml");
11675  </code></pre>
11676  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11677  * <br><br>
11678  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11679  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11680  * 
11681  * Note: old style constructor is still suported (container, template, config)
11682  * 
11683  * @constructor
11684  * Create a new View
11685  * @param {Object} config The config object
11686  * 
11687  */
11688 Roo.View = function(config, depreciated_tpl, depreciated_config){
11689     
11690     this.parent = false;
11691     
11692     if (typeof(depreciated_tpl) == 'undefined') {
11693         // new way.. - universal constructor.
11694         Roo.apply(this, config);
11695         this.el  = Roo.get(this.el);
11696     } else {
11697         // old format..
11698         this.el  = Roo.get(config);
11699         this.tpl = depreciated_tpl;
11700         Roo.apply(this, depreciated_config);
11701     }
11702     this.wrapEl  = this.el.wrap().wrap();
11703     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11704     
11705     
11706     if(typeof(this.tpl) == "string"){
11707         this.tpl = new Roo.Template(this.tpl);
11708     } else {
11709         // support xtype ctors..
11710         this.tpl = new Roo.factory(this.tpl, Roo);
11711     }
11712     
11713     
11714     this.tpl.compile();
11715     
11716     /** @private */
11717     this.addEvents({
11718         /**
11719          * @event beforeclick
11720          * Fires before a click is processed. Returns false to cancel the default action.
11721          * @param {Roo.View} this
11722          * @param {Number} index The index of the target node
11723          * @param {HTMLElement} node The target node
11724          * @param {Roo.EventObject} e The raw event object
11725          */
11726             "beforeclick" : true,
11727         /**
11728          * @event click
11729          * Fires when a template node is clicked.
11730          * @param {Roo.View} this
11731          * @param {Number} index The index of the target node
11732          * @param {HTMLElement} node The target node
11733          * @param {Roo.EventObject} e The raw event object
11734          */
11735             "click" : true,
11736         /**
11737          * @event dblclick
11738          * Fires when a template node is double clicked.
11739          * @param {Roo.View} this
11740          * @param {Number} index The index of the target node
11741          * @param {HTMLElement} node The target node
11742          * @param {Roo.EventObject} e The raw event object
11743          */
11744             "dblclick" : true,
11745         /**
11746          * @event contextmenu
11747          * Fires when a template node is right clicked.
11748          * @param {Roo.View} this
11749          * @param {Number} index The index of the target node
11750          * @param {HTMLElement} node The target node
11751          * @param {Roo.EventObject} e The raw event object
11752          */
11753             "contextmenu" : true,
11754         /**
11755          * @event selectionchange
11756          * Fires when the selected nodes change.
11757          * @param {Roo.View} this
11758          * @param {Array} selections Array of the selected nodes
11759          */
11760             "selectionchange" : true,
11761     
11762         /**
11763          * @event beforeselect
11764          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11765          * @param {Roo.View} this
11766          * @param {HTMLElement} node The node to be selected
11767          * @param {Array} selections Array of currently selected nodes
11768          */
11769             "beforeselect" : true,
11770         /**
11771          * @event preparedata
11772          * Fires on every row to render, to allow you to change the data.
11773          * @param {Roo.View} this
11774          * @param {Object} data to be rendered (change this)
11775          */
11776           "preparedata" : true
11777           
11778           
11779         });
11780
11781
11782
11783     this.el.on({
11784         "click": this.onClick,
11785         "dblclick": this.onDblClick,
11786         "contextmenu": this.onContextMenu,
11787         scope:this
11788     });
11789
11790     this.selections = [];
11791     this.nodes = [];
11792     this.cmp = new Roo.CompositeElementLite([]);
11793     if(this.store){
11794         this.store = Roo.factory(this.store, Roo.data);
11795         this.setStore(this.store, true);
11796     }
11797     
11798     if ( this.footer && this.footer.xtype) {
11799            
11800          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11801         
11802         this.footer.dataSource = this.store
11803         this.footer.container = fctr;
11804         this.footer = Roo.factory(this.footer, Roo);
11805         fctr.insertFirst(this.el);
11806         
11807         // this is a bit insane - as the paging toolbar seems to detach the el..
11808 //        dom.parentNode.parentNode.parentNode
11809          // they get detached?
11810     }
11811     
11812     
11813     Roo.View.superclass.constructor.call(this);
11814     
11815     
11816 };
11817
11818 Roo.extend(Roo.View, Roo.util.Observable, {
11819     
11820      /**
11821      * @cfg {Roo.data.Store} store Data store to load data from.
11822      */
11823     store : false,
11824     
11825     /**
11826      * @cfg {String|Roo.Element} el The container element.
11827      */
11828     el : '',
11829     
11830     /**
11831      * @cfg {String|Roo.Template} tpl The template used by this View 
11832      */
11833     tpl : false,
11834     /**
11835      * @cfg {String} dataName the named area of the template to use as the data area
11836      *                          Works with domtemplates roo-name="name"
11837      */
11838     dataName: false,
11839     /**
11840      * @cfg {String} selectedClass The css class to add to selected nodes
11841      */
11842     selectedClass : "x-view-selected",
11843      /**
11844      * @cfg {String} emptyText The empty text to show when nothing is loaded.
11845      */
11846     emptyText : "",
11847     
11848     /**
11849      * @cfg {String} text to display on mask (default Loading)
11850      */
11851     mask : false,
11852     /**
11853      * @cfg {Boolean} multiSelect Allow multiple selection
11854      */
11855     multiSelect : false,
11856     /**
11857      * @cfg {Boolean} singleSelect Allow single selection
11858      */
11859     singleSelect:  false,
11860     
11861     /**
11862      * @cfg {Boolean} toggleSelect - selecting 
11863      */
11864     toggleSelect : false,
11865     
11866     /**
11867      * @cfg {Boolean} tickable - selecting 
11868      */
11869     tickable : false,
11870     
11871     /**
11872      * Returns the element this view is bound to.
11873      * @return {Roo.Element}
11874      */
11875     getEl : function(){
11876         return this.wrapEl;
11877     },
11878     
11879     
11880
11881     /**
11882      * Refreshes the view. - called by datachanged on the store. - do not call directly.
11883      */
11884     refresh : function(){
11885         Roo.log('refresh');
11886         var t = this.tpl;
11887         
11888         // if we are using something like 'domtemplate', then
11889         // the what gets used is:
11890         // t.applySubtemplate(NAME, data, wrapping data..)
11891         // the outer template then get' applied with
11892         //     the store 'extra data'
11893         // and the body get's added to the
11894         //      roo-name="data" node?
11895         //      <span class='roo-tpl-{name}'></span> ?????
11896         
11897         
11898         
11899         this.clearSelections();
11900         this.el.update("");
11901         var html = [];
11902         var records = this.store.getRange();
11903         if(records.length < 1) {
11904             
11905             // is this valid??  = should it render a template??
11906             
11907             this.el.update(this.emptyText);
11908             return;
11909         }
11910         var el = this.el;
11911         if (this.dataName) {
11912             this.el.update(t.apply(this.store.meta)); //????
11913             el = this.el.child('.roo-tpl-' + this.dataName);
11914         }
11915         
11916         for(var i = 0, len = records.length; i < len; i++){
11917             var data = this.prepareData(records[i].data, i, records[i]);
11918             this.fireEvent("preparedata", this, data, i, records[i]);
11919             
11920             var d = Roo.apply({}, data);
11921             
11922             if(this.tickable){
11923                 Roo.apply(d, {'roo-id' : Roo.id()});
11924                 
11925                 var _this = this;
11926             
11927                 Roo.each(this.parent.item, function(item){
11928                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
11929                         return;
11930                     }
11931                     Roo.apply(d, {'roo-data-checked' : 'checked'});
11932                 });
11933             }
11934             
11935             html[html.length] = Roo.util.Format.trim(
11936                 this.dataName ?
11937                     t.applySubtemplate(this.dataName, d, this.store.meta) :
11938                     t.apply(d)
11939             );
11940         }
11941         
11942         
11943         
11944         el.update(html.join(""));
11945         this.nodes = el.dom.childNodes;
11946         this.updateIndexes(0);
11947     },
11948     
11949
11950     /**
11951      * Function to override to reformat the data that is sent to
11952      * the template for each node.
11953      * DEPRICATED - use the preparedata event handler.
11954      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
11955      * a JSON object for an UpdateManager bound view).
11956      */
11957     prepareData : function(data, index, record)
11958     {
11959         this.fireEvent("preparedata", this, data, index, record);
11960         return data;
11961     },
11962
11963     onUpdate : function(ds, record){
11964          Roo.log('on update');   
11965         this.clearSelections();
11966         var index = this.store.indexOf(record);
11967         var n = this.nodes[index];
11968         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
11969         n.parentNode.removeChild(n);
11970         this.updateIndexes(index, index);
11971     },
11972
11973     
11974     
11975 // --------- FIXME     
11976     onAdd : function(ds, records, index)
11977     {
11978         Roo.log(['on Add', ds, records, index] );        
11979         this.clearSelections();
11980         if(this.nodes.length == 0){
11981             this.refresh();
11982             return;
11983         }
11984         var n = this.nodes[index];
11985         for(var i = 0, len = records.length; i < len; i++){
11986             var d = this.prepareData(records[i].data, i, records[i]);
11987             if(n){
11988                 this.tpl.insertBefore(n, d);
11989             }else{
11990                 
11991                 this.tpl.append(this.el, d);
11992             }
11993         }
11994         this.updateIndexes(index);
11995     },
11996
11997     onRemove : function(ds, record, index){
11998         Roo.log('onRemove');
11999         this.clearSelections();
12000         var el = this.dataName  ?
12001             this.el.child('.roo-tpl-' + this.dataName) :
12002             this.el; 
12003         
12004         el.dom.removeChild(this.nodes[index]);
12005         this.updateIndexes(index);
12006     },
12007
12008     /**
12009      * Refresh an individual node.
12010      * @param {Number} index
12011      */
12012     refreshNode : function(index){
12013         this.onUpdate(this.store, this.store.getAt(index));
12014     },
12015
12016     updateIndexes : function(startIndex, endIndex){
12017         var ns = this.nodes;
12018         startIndex = startIndex || 0;
12019         endIndex = endIndex || ns.length - 1;
12020         for(var i = startIndex; i <= endIndex; i++){
12021             ns[i].nodeIndex = i;
12022         }
12023     },
12024
12025     /**
12026      * Changes the data store this view uses and refresh the view.
12027      * @param {Store} store
12028      */
12029     setStore : function(store, initial){
12030         if(!initial && this.store){
12031             this.store.un("datachanged", this.refresh);
12032             this.store.un("add", this.onAdd);
12033             this.store.un("remove", this.onRemove);
12034             this.store.un("update", this.onUpdate);
12035             this.store.un("clear", this.refresh);
12036             this.store.un("beforeload", this.onBeforeLoad);
12037             this.store.un("load", this.onLoad);
12038             this.store.un("loadexception", this.onLoad);
12039         }
12040         if(store){
12041           
12042             store.on("datachanged", this.refresh, this);
12043             store.on("add", this.onAdd, this);
12044             store.on("remove", this.onRemove, this);
12045             store.on("update", this.onUpdate, this);
12046             store.on("clear", this.refresh, this);
12047             store.on("beforeload", this.onBeforeLoad, this);
12048             store.on("load", this.onLoad, this);
12049             store.on("loadexception", this.onLoad, this);
12050         }
12051         
12052         if(store){
12053             this.refresh();
12054         }
12055     },
12056     /**
12057      * onbeforeLoad - masks the loading area.
12058      *
12059      */
12060     onBeforeLoad : function(store,opts)
12061     {
12062          Roo.log('onBeforeLoad');   
12063         if (!opts.add) {
12064             this.el.update("");
12065         }
12066         this.el.mask(this.mask ? this.mask : "Loading" ); 
12067     },
12068     onLoad : function ()
12069     {
12070         this.el.unmask();
12071     },
12072     
12073
12074     /**
12075      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12076      * @param {HTMLElement} node
12077      * @return {HTMLElement} The template node
12078      */
12079     findItemFromChild : function(node){
12080         var el = this.dataName  ?
12081             this.el.child('.roo-tpl-' + this.dataName,true) :
12082             this.el.dom; 
12083         
12084         if(!node || node.parentNode == el){
12085                     return node;
12086             }
12087             var p = node.parentNode;
12088             while(p && p != el){
12089             if(p.parentNode == el){
12090                 return p;
12091             }
12092             p = p.parentNode;
12093         }
12094             return null;
12095     },
12096
12097     /** @ignore */
12098     onClick : function(e){
12099         var item = this.findItemFromChild(e.getTarget());
12100         if(item){
12101             var index = this.indexOf(item);
12102             if(this.onItemClick(item, index, e) !== false){
12103                 this.fireEvent("click", this, index, item, e);
12104             }
12105         }else{
12106             this.clearSelections();
12107         }
12108     },
12109
12110     /** @ignore */
12111     onContextMenu : function(e){
12112         var item = this.findItemFromChild(e.getTarget());
12113         if(item){
12114             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12115         }
12116     },
12117
12118     /** @ignore */
12119     onDblClick : function(e){
12120         var item = this.findItemFromChild(e.getTarget());
12121         if(item){
12122             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12123         }
12124     },
12125
12126     onItemClick : function(item, index, e)
12127     {
12128         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12129             return false;
12130         }
12131         if (this.toggleSelect) {
12132             var m = this.isSelected(item) ? 'unselect' : 'select';
12133             Roo.log(m);
12134             var _t = this;
12135             _t[m](item, true, false);
12136             return true;
12137         }
12138         if(this.multiSelect || this.singleSelect){
12139             if(this.multiSelect && e.shiftKey && this.lastSelection){
12140                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12141             }else{
12142                 this.select(item, this.multiSelect && e.ctrlKey);
12143                 this.lastSelection = item;
12144             }
12145             
12146             if(!this.tickable){
12147                 e.preventDefault();
12148             }
12149             
12150         }
12151         return true;
12152     },
12153
12154     /**
12155      * Get the number of selected nodes.
12156      * @return {Number}
12157      */
12158     getSelectionCount : function(){
12159         return this.selections.length;
12160     },
12161
12162     /**
12163      * Get the currently selected nodes.
12164      * @return {Array} An array of HTMLElements
12165      */
12166     getSelectedNodes : function(){
12167         return this.selections;
12168     },
12169
12170     /**
12171      * Get the indexes of the selected nodes.
12172      * @return {Array}
12173      */
12174     getSelectedIndexes : function(){
12175         var indexes = [], s = this.selections;
12176         for(var i = 0, len = s.length; i < len; i++){
12177             indexes.push(s[i].nodeIndex);
12178         }
12179         return indexes;
12180     },
12181
12182     /**
12183      * Clear all selections
12184      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12185      */
12186     clearSelections : function(suppressEvent){
12187         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12188             this.cmp.elements = this.selections;
12189             this.cmp.removeClass(this.selectedClass);
12190             this.selections = [];
12191             if(!suppressEvent){
12192                 this.fireEvent("selectionchange", this, this.selections);
12193             }
12194         }
12195     },
12196
12197     /**
12198      * Returns true if the passed node is selected
12199      * @param {HTMLElement/Number} node The node or node index
12200      * @return {Boolean}
12201      */
12202     isSelected : function(node){
12203         var s = this.selections;
12204         if(s.length < 1){
12205             return false;
12206         }
12207         node = this.getNode(node);
12208         return s.indexOf(node) !== -1;
12209     },
12210
12211     /**
12212      * Selects nodes.
12213      * @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
12214      * @param {Boolean} keepExisting (optional) true to keep existing selections
12215      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12216      */
12217     select : function(nodeInfo, keepExisting, suppressEvent){
12218         if(nodeInfo instanceof Array){
12219             if(!keepExisting){
12220                 this.clearSelections(true);
12221             }
12222             for(var i = 0, len = nodeInfo.length; i < len; i++){
12223                 this.select(nodeInfo[i], true, true);
12224             }
12225             return;
12226         } 
12227         var node = this.getNode(nodeInfo);
12228         if(!node || this.isSelected(node)){
12229             return; // already selected.
12230         }
12231         if(!keepExisting){
12232             this.clearSelections(true);
12233         }
12234         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12235             Roo.fly(node).addClass(this.selectedClass);
12236             this.selections.push(node);
12237             if(!suppressEvent){
12238                 this.fireEvent("selectionchange", this, this.selections);
12239             }
12240         }
12241         
12242         
12243     },
12244       /**
12245      * Unselects nodes.
12246      * @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
12247      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12248      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12249      */
12250     unselect : function(nodeInfo, keepExisting, suppressEvent)
12251     {
12252         if(nodeInfo instanceof Array){
12253             Roo.each(this.selections, function(s) {
12254                 this.unselect(s, nodeInfo);
12255             }, this);
12256             return;
12257         }
12258         var node = this.getNode(nodeInfo);
12259         if(!node || !this.isSelected(node)){
12260             Roo.log("not selected");
12261             return; // not selected.
12262         }
12263         // fireevent???
12264         var ns = [];
12265         Roo.each(this.selections, function(s) {
12266             if (s == node ) {
12267                 Roo.fly(node).removeClass(this.selectedClass);
12268
12269                 return;
12270             }
12271             ns.push(s);
12272         },this);
12273         
12274         this.selections= ns;
12275         this.fireEvent("selectionchange", this, this.selections);
12276     },
12277
12278     /**
12279      * Gets a template node.
12280      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12281      * @return {HTMLElement} The node or null if it wasn't found
12282      */
12283     getNode : function(nodeInfo){
12284         if(typeof nodeInfo == "string"){
12285             return document.getElementById(nodeInfo);
12286         }else if(typeof nodeInfo == "number"){
12287             return this.nodes[nodeInfo];
12288         }
12289         return nodeInfo;
12290     },
12291
12292     /**
12293      * Gets a range template nodes.
12294      * @param {Number} startIndex
12295      * @param {Number} endIndex
12296      * @return {Array} An array of nodes
12297      */
12298     getNodes : function(start, end){
12299         var ns = this.nodes;
12300         start = start || 0;
12301         end = typeof end == "undefined" ? ns.length - 1 : end;
12302         var nodes = [];
12303         if(start <= end){
12304             for(var i = start; i <= end; i++){
12305                 nodes.push(ns[i]);
12306             }
12307         } else{
12308             for(var i = start; i >= end; i--){
12309                 nodes.push(ns[i]);
12310             }
12311         }
12312         return nodes;
12313     },
12314
12315     /**
12316      * Finds the index of the passed node
12317      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12318      * @return {Number} The index of the node or -1
12319      */
12320     indexOf : function(node){
12321         node = this.getNode(node);
12322         if(typeof node.nodeIndex == "number"){
12323             return node.nodeIndex;
12324         }
12325         var ns = this.nodes;
12326         for(var i = 0, len = ns.length; i < len; i++){
12327             if(ns[i] == node){
12328                 return i;
12329             }
12330         }
12331         return -1;
12332     }
12333 });
12334 /*
12335  * - LGPL
12336  *
12337  * based on jquery fullcalendar
12338  * 
12339  */
12340
12341 Roo.bootstrap = Roo.bootstrap || {};
12342 /**
12343  * @class Roo.bootstrap.Calendar
12344  * @extends Roo.bootstrap.Component
12345  * Bootstrap Calendar class
12346  * @cfg {Boolean} loadMask (true|false) default false
12347  * @cfg {Object} header generate the user specific header of the calendar, default false
12348
12349  * @constructor
12350  * Create a new Container
12351  * @param {Object} config The config object
12352  */
12353
12354
12355
12356 Roo.bootstrap.Calendar = function(config){
12357     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12358      this.addEvents({
12359         /**
12360              * @event select
12361              * Fires when a date is selected
12362              * @param {DatePicker} this
12363              * @param {Date} date The selected date
12364              */
12365         'select': true,
12366         /**
12367              * @event monthchange
12368              * Fires when the displayed month changes 
12369              * @param {DatePicker} this
12370              * @param {Date} date The selected month
12371              */
12372         'monthchange': true,
12373         /**
12374              * @event evententer
12375              * Fires when mouse over an event
12376              * @param {Calendar} this
12377              * @param {event} Event
12378              */
12379         'evententer': true,
12380         /**
12381              * @event eventleave
12382              * Fires when the mouse leaves an
12383              * @param {Calendar} this
12384              * @param {event}
12385              */
12386         'eventleave': true,
12387         /**
12388              * @event eventclick
12389              * Fires when the mouse click an
12390              * @param {Calendar} this
12391              * @param {event}
12392              */
12393         'eventclick': true
12394         
12395     });
12396
12397 };
12398
12399 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12400     
12401      /**
12402      * @cfg {Number} startDay
12403      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12404      */
12405     startDay : 0,
12406     
12407     loadMask : false,
12408     
12409     header : false,
12410       
12411     getAutoCreate : function(){
12412         
12413         
12414         var fc_button = function(name, corner, style, content ) {
12415             return Roo.apply({},{
12416                 tag : 'span',
12417                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12418                          (corner.length ?
12419                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12420                             ''
12421                         ),
12422                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12423                 unselectable: 'on'
12424             });
12425         };
12426         
12427         var header = {};
12428         
12429         if(!this.header){
12430             header = {
12431                 tag : 'table',
12432                 cls : 'fc-header',
12433                 style : 'width:100%',
12434                 cn : [
12435                     {
12436                         tag: 'tr',
12437                         cn : [
12438                             {
12439                                 tag : 'td',
12440                                 cls : 'fc-header-left',
12441                                 cn : [
12442                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12443                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12444                                     { tag: 'span', cls: 'fc-header-space' },
12445                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12446
12447
12448                                 ]
12449                             },
12450
12451                             {
12452                                 tag : 'td',
12453                                 cls : 'fc-header-center',
12454                                 cn : [
12455                                     {
12456                                         tag: 'span',
12457                                         cls: 'fc-header-title',
12458                                         cn : {
12459                                             tag: 'H2',
12460                                             html : 'month / year'
12461                                         }
12462                                     }
12463
12464                                 ]
12465                             },
12466                             {
12467                                 tag : 'td',
12468                                 cls : 'fc-header-right',
12469                                 cn : [
12470                               /*      fc_button('month', 'left', '', 'month' ),
12471                                     fc_button('week', '', '', 'week' ),
12472                                     fc_button('day', 'right', '', 'day' )
12473                                 */    
12474
12475                                 ]
12476                             }
12477
12478                         ]
12479                     }
12480                 ]
12481             };
12482         }
12483         
12484         header = this.header;
12485         
12486        
12487         var cal_heads = function() {
12488             var ret = [];
12489             // fixme - handle this.
12490             
12491             for (var i =0; i < Date.dayNames.length; i++) {
12492                 var d = Date.dayNames[i];
12493                 ret.push({
12494                     tag: 'th',
12495                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12496                     html : d.substring(0,3)
12497                 });
12498                 
12499             }
12500             ret[0].cls += ' fc-first';
12501             ret[6].cls += ' fc-last';
12502             return ret;
12503         };
12504         var cal_cell = function(n) {
12505             return  {
12506                 tag: 'td',
12507                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12508                 cn : [
12509                     {
12510                         cn : [
12511                             {
12512                                 cls: 'fc-day-number',
12513                                 html: 'D'
12514                             },
12515                             {
12516                                 cls: 'fc-day-content',
12517                              
12518                                 cn : [
12519                                      {
12520                                         style: 'position: relative;' // height: 17px;
12521                                     }
12522                                 ]
12523                             }
12524                             
12525                             
12526                         ]
12527                     }
12528                 ]
12529                 
12530             }
12531         };
12532         var cal_rows = function() {
12533             
12534             var ret = []
12535             for (var r = 0; r < 6; r++) {
12536                 var row= {
12537                     tag : 'tr',
12538                     cls : 'fc-week',
12539                     cn : []
12540                 };
12541                 
12542                 for (var i =0; i < Date.dayNames.length; i++) {
12543                     var d = Date.dayNames[i];
12544                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12545
12546                 }
12547                 row.cn[0].cls+=' fc-first';
12548                 row.cn[0].cn[0].style = 'min-height:90px';
12549                 row.cn[6].cls+=' fc-last';
12550                 ret.push(row);
12551                 
12552             }
12553             ret[0].cls += ' fc-first';
12554             ret[4].cls += ' fc-prev-last';
12555             ret[5].cls += ' fc-last';
12556             return ret;
12557             
12558         };
12559         
12560         var cal_table = {
12561             tag: 'table',
12562             cls: 'fc-border-separate',
12563             style : 'width:100%',
12564             cellspacing  : 0,
12565             cn : [
12566                 { 
12567                     tag: 'thead',
12568                     cn : [
12569                         { 
12570                             tag: 'tr',
12571                             cls : 'fc-first fc-last',
12572                             cn : cal_heads()
12573                         }
12574                     ]
12575                 },
12576                 { 
12577                     tag: 'tbody',
12578                     cn : cal_rows()
12579                 }
12580                   
12581             ]
12582         };
12583          
12584          var cfg = {
12585             cls : 'fc fc-ltr',
12586             cn : [
12587                 header,
12588                 {
12589                     cls : 'fc-content',
12590                     style : "position: relative;",
12591                     cn : [
12592                         {
12593                             cls : 'fc-view fc-view-month fc-grid',
12594                             style : 'position: relative',
12595                             unselectable : 'on',
12596                             cn : [
12597                                 {
12598                                     cls : 'fc-event-container',
12599                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12600                                 },
12601                                 cal_table
12602                             ]
12603                         }
12604                     ]
12605     
12606                 }
12607            ] 
12608             
12609         };
12610         
12611          
12612         
12613         return cfg;
12614     },
12615     
12616     
12617     initEvents : function()
12618     {
12619         if(!this.store){
12620             throw "can not find store for calendar";
12621         }
12622         
12623         var mark = {
12624             tag: "div",
12625             cls:"x-dlg-mask",
12626             style: "text-align:center",
12627             cn: [
12628                 {
12629                     tag: "div",
12630                     style: "background-color:white;width:50%;margin:250 auto",
12631                     cn: [
12632                         {
12633                             tag: "img",
12634                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12635                         },
12636                         {
12637                             tag: "span",
12638                             html: "Loading"
12639                         }
12640                         
12641                     ]
12642                 }
12643             ]
12644         }
12645         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12646         
12647         var size = this.el.select('.fc-content', true).first().getSize();
12648         this.maskEl.setSize(size.width, size.height);
12649         this.maskEl.enableDisplayMode("block");
12650         if(!this.loadMask){
12651             this.maskEl.hide();
12652         }
12653         
12654         this.store = Roo.factory(this.store, Roo.data);
12655         this.store.on('load', this.onLoad, this);
12656         this.store.on('beforeload', this.onBeforeLoad, this);
12657         
12658         this.resize();
12659         
12660         this.cells = this.el.select('.fc-day',true);
12661         //Roo.log(this.cells);
12662         this.textNodes = this.el.query('.fc-day-number');
12663         this.cells.addClassOnOver('fc-state-hover');
12664         
12665         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12666         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12667         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12668         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12669         
12670         this.on('monthchange', this.onMonthChange, this);
12671         
12672         this.update(new Date().clearTime());
12673     },
12674     
12675     resize : function() {
12676         var sz  = this.el.getSize();
12677         
12678         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12679         this.el.select('.fc-day-content div',true).setHeight(34);
12680     },
12681     
12682     
12683     // private
12684     showPrevMonth : function(e){
12685         this.update(this.activeDate.add("mo", -1));
12686     },
12687     showToday : function(e){
12688         this.update(new Date().clearTime());
12689     },
12690     // private
12691     showNextMonth : function(e){
12692         this.update(this.activeDate.add("mo", 1));
12693     },
12694
12695     // private
12696     showPrevYear : function(){
12697         this.update(this.activeDate.add("y", -1));
12698     },
12699
12700     // private
12701     showNextYear : function(){
12702         this.update(this.activeDate.add("y", 1));
12703     },
12704
12705     
12706    // private
12707     update : function(date)
12708     {
12709         var vd = this.activeDate;
12710         this.activeDate = date;
12711 //        if(vd && this.el){
12712 //            var t = date.getTime();
12713 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12714 //                Roo.log('using add remove');
12715 //                
12716 //                this.fireEvent('monthchange', this, date);
12717 //                
12718 //                this.cells.removeClass("fc-state-highlight");
12719 //                this.cells.each(function(c){
12720 //                   if(c.dateValue == t){
12721 //                       c.addClass("fc-state-highlight");
12722 //                       setTimeout(function(){
12723 //                            try{c.dom.firstChild.focus();}catch(e){}
12724 //                       }, 50);
12725 //                       return false;
12726 //                   }
12727 //                   return true;
12728 //                });
12729 //                return;
12730 //            }
12731 //        }
12732         
12733         var days = date.getDaysInMonth();
12734         
12735         var firstOfMonth = date.getFirstDateOfMonth();
12736         var startingPos = firstOfMonth.getDay()-this.startDay;
12737         
12738         if(startingPos < this.startDay){
12739             startingPos += 7;
12740         }
12741         
12742         var pm = date.add(Date.MONTH, -1);
12743         var prevStart = pm.getDaysInMonth()-startingPos;
12744 //        
12745         this.cells = this.el.select('.fc-day',true);
12746         this.textNodes = this.el.query('.fc-day-number');
12747         this.cells.addClassOnOver('fc-state-hover');
12748         
12749         var cells = this.cells.elements;
12750         var textEls = this.textNodes;
12751         
12752         Roo.each(cells, function(cell){
12753             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12754         });
12755         
12756         days += startingPos;
12757
12758         // convert everything to numbers so it's fast
12759         var day = 86400000;
12760         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12761         //Roo.log(d);
12762         //Roo.log(pm);
12763         //Roo.log(prevStart);
12764         
12765         var today = new Date().clearTime().getTime();
12766         var sel = date.clearTime().getTime();
12767         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12768         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12769         var ddMatch = this.disabledDatesRE;
12770         var ddText = this.disabledDatesText;
12771         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12772         var ddaysText = this.disabledDaysText;
12773         var format = this.format;
12774         
12775         var setCellClass = function(cal, cell){
12776             cell.row = 0;
12777             cell.events = [];
12778             cell.more = [];
12779             //Roo.log('set Cell Class');
12780             cell.title = "";
12781             var t = d.getTime();
12782             
12783             //Roo.log(d);
12784             
12785             cell.dateValue = t;
12786             if(t == today){
12787                 cell.className += " fc-today";
12788                 cell.className += " fc-state-highlight";
12789                 cell.title = cal.todayText;
12790             }
12791             if(t == sel){
12792                 // disable highlight in other month..
12793                 //cell.className += " fc-state-highlight";
12794                 
12795             }
12796             // disabling
12797             if(t < min) {
12798                 cell.className = " fc-state-disabled";
12799                 cell.title = cal.minText;
12800                 return;
12801             }
12802             if(t > max) {
12803                 cell.className = " fc-state-disabled";
12804                 cell.title = cal.maxText;
12805                 return;
12806             }
12807             if(ddays){
12808                 if(ddays.indexOf(d.getDay()) != -1){
12809                     cell.title = ddaysText;
12810                     cell.className = " fc-state-disabled";
12811                 }
12812             }
12813             if(ddMatch && format){
12814                 var fvalue = d.dateFormat(format);
12815                 if(ddMatch.test(fvalue)){
12816                     cell.title = ddText.replace("%0", fvalue);
12817                     cell.className = " fc-state-disabled";
12818                 }
12819             }
12820             
12821             if (!cell.initialClassName) {
12822                 cell.initialClassName = cell.dom.className;
12823             }
12824             
12825             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
12826         };
12827
12828         var i = 0;
12829         
12830         for(; i < startingPos; i++) {
12831             textEls[i].innerHTML = (++prevStart);
12832             d.setDate(d.getDate()+1);
12833             
12834             cells[i].className = "fc-past fc-other-month";
12835             setCellClass(this, cells[i]);
12836         }
12837         
12838         var intDay = 0;
12839         
12840         for(; i < days; i++){
12841             intDay = i - startingPos + 1;
12842             textEls[i].innerHTML = (intDay);
12843             d.setDate(d.getDate()+1);
12844             
12845             cells[i].className = ''; // "x-date-active";
12846             setCellClass(this, cells[i]);
12847         }
12848         var extraDays = 0;
12849         
12850         for(; i < 42; i++) {
12851             textEls[i].innerHTML = (++extraDays);
12852             d.setDate(d.getDate()+1);
12853             
12854             cells[i].className = "fc-future fc-other-month";
12855             setCellClass(this, cells[i]);
12856         }
12857         
12858         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
12859         
12860         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
12861         
12862         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
12863         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
12864         
12865         if(totalRows != 6){
12866             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
12867             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
12868         }
12869         
12870         this.fireEvent('monthchange', this, date);
12871         
12872         
12873         /*
12874         if(!this.internalRender){
12875             var main = this.el.dom.firstChild;
12876             var w = main.offsetWidth;
12877             this.el.setWidth(w + this.el.getBorderWidth("lr"));
12878             Roo.fly(main).setWidth(w);
12879             this.internalRender = true;
12880             // opera does not respect the auto grow header center column
12881             // then, after it gets a width opera refuses to recalculate
12882             // without a second pass
12883             if(Roo.isOpera && !this.secondPass){
12884                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
12885                 this.secondPass = true;
12886                 this.update.defer(10, this, [date]);
12887             }
12888         }
12889         */
12890         
12891     },
12892     
12893     findCell : function(dt) {
12894         dt = dt.clearTime().getTime();
12895         var ret = false;
12896         this.cells.each(function(c){
12897             //Roo.log("check " +c.dateValue + '?=' + dt);
12898             if(c.dateValue == dt){
12899                 ret = c;
12900                 return false;
12901             }
12902             return true;
12903         });
12904         
12905         return ret;
12906     },
12907     
12908     findCells : function(ev) {
12909         var s = ev.start.clone().clearTime().getTime();
12910        // Roo.log(s);
12911         var e= ev.end.clone().clearTime().getTime();
12912        // Roo.log(e);
12913         var ret = [];
12914         this.cells.each(function(c){
12915              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
12916             
12917             if(c.dateValue > e){
12918                 return ;
12919             }
12920             if(c.dateValue < s){
12921                 return ;
12922             }
12923             ret.push(c);
12924         });
12925         
12926         return ret;    
12927     },
12928     
12929 //    findBestRow: function(cells)
12930 //    {
12931 //        var ret = 0;
12932 //        
12933 //        for (var i =0 ; i < cells.length;i++) {
12934 //            ret  = Math.max(cells[i].rows || 0,ret);
12935 //        }
12936 //        return ret;
12937 //        
12938 //    },
12939     
12940     
12941     addItem : function(ev)
12942     {
12943         // look for vertical location slot in
12944         var cells = this.findCells(ev);
12945         
12946 //        ev.row = this.findBestRow(cells);
12947         
12948         // work out the location.
12949         
12950         var crow = false;
12951         var rows = [];
12952         for(var i =0; i < cells.length; i++) {
12953             
12954             cells[i].row = cells[0].row;
12955             
12956             if(i == 0){
12957                 cells[i].row = cells[i].row + 1;
12958             }
12959             
12960             if (!crow) {
12961                 crow = {
12962                     start : cells[i],
12963                     end :  cells[i]
12964                 };
12965                 continue;
12966             }
12967             if (crow.start.getY() == cells[i].getY()) {
12968                 // on same row.
12969                 crow.end = cells[i];
12970                 continue;
12971             }
12972             // different row.
12973             rows.push(crow);
12974             crow = {
12975                 start: cells[i],
12976                 end : cells[i]
12977             };
12978             
12979         }
12980         
12981         rows.push(crow);
12982         ev.els = [];
12983         ev.rows = rows;
12984         ev.cells = cells;
12985         
12986         cells[0].events.push(ev);
12987         
12988         this.calevents.push(ev);
12989     },
12990     
12991     clearEvents: function() {
12992         
12993         if(!this.calevents){
12994             return;
12995         }
12996         
12997         Roo.each(this.cells.elements, function(c){
12998             c.row = 0;
12999             c.events = [];
13000             c.more = [];
13001         });
13002         
13003         Roo.each(this.calevents, function(e) {
13004             Roo.each(e.els, function(el) {
13005                 el.un('mouseenter' ,this.onEventEnter, this);
13006                 el.un('mouseleave' ,this.onEventLeave, this);
13007                 el.remove();
13008             },this);
13009         },this);
13010         
13011         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13012             e.remove();
13013         });
13014         
13015     },
13016     
13017     renderEvents: function()
13018     {   
13019         var _this = this;
13020         
13021         this.cells.each(function(c) {
13022             
13023             if(c.row < 5){
13024                 return;
13025             }
13026             
13027             var ev = c.events;
13028             
13029             var r = 4;
13030             if(c.row != c.events.length){
13031                 r = 4 - (4 - (c.row - c.events.length));
13032             }
13033             
13034             c.events = ev.slice(0, r);
13035             c.more = ev.slice(r);
13036             
13037             if(c.more.length && c.more.length == 1){
13038                 c.events.push(c.more.pop());
13039             }
13040             
13041             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13042             
13043         });
13044             
13045         this.cells.each(function(c) {
13046             
13047             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13048             
13049             
13050             for (var e = 0; e < c.events.length; e++){
13051                 var ev = c.events[e];
13052                 var rows = ev.rows;
13053                 
13054                 for(var i = 0; i < rows.length; i++) {
13055                 
13056                     // how many rows should it span..
13057
13058                     var  cfg = {
13059                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13060                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13061
13062                         unselectable : "on",
13063                         cn : [
13064                             {
13065                                 cls: 'fc-event-inner',
13066                                 cn : [
13067     //                                {
13068     //                                  tag:'span',
13069     //                                  cls: 'fc-event-time',
13070     //                                  html : cells.length > 1 ? '' : ev.time
13071     //                                },
13072                                     {
13073                                       tag:'span',
13074                                       cls: 'fc-event-title',
13075                                       html : String.format('{0}', ev.title)
13076                                     }
13077
13078
13079                                 ]
13080                             },
13081                             {
13082                                 cls: 'ui-resizable-handle ui-resizable-e',
13083                                 html : '&nbsp;&nbsp;&nbsp'
13084                             }
13085
13086                         ]
13087                     };
13088
13089                     if (i == 0) {
13090                         cfg.cls += ' fc-event-start';
13091                     }
13092                     if ((i+1) == rows.length) {
13093                         cfg.cls += ' fc-event-end';
13094                     }
13095
13096                     var ctr = _this.el.select('.fc-event-container',true).first();
13097                     var cg = ctr.createChild(cfg);
13098
13099                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13100                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13101
13102                     var r = (c.more.length) ? 1 : 0;
13103                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13104                     cg.setWidth(ebox.right - sbox.x -2);
13105
13106                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13107                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13108                     cg.on('click', _this.onEventClick, _this, ev);
13109
13110                     ev.els.push(cg);
13111                     
13112                 }
13113                 
13114             }
13115             
13116             
13117             if(c.more.length){
13118                 var  cfg = {
13119                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13120                     style : 'position: absolute',
13121                     unselectable : "on",
13122                     cn : [
13123                         {
13124                             cls: 'fc-event-inner',
13125                             cn : [
13126                                 {
13127                                   tag:'span',
13128                                   cls: 'fc-event-title',
13129                                   html : 'More'
13130                                 }
13131
13132
13133                             ]
13134                         },
13135                         {
13136                             cls: 'ui-resizable-handle ui-resizable-e',
13137                             html : '&nbsp;&nbsp;&nbsp'
13138                         }
13139
13140                     ]
13141                 };
13142
13143                 var ctr = _this.el.select('.fc-event-container',true).first();
13144                 var cg = ctr.createChild(cfg);
13145
13146                 var sbox = c.select('.fc-day-content',true).first().getBox();
13147                 var ebox = c.select('.fc-day-content',true).first().getBox();
13148                 //Roo.log(cg);
13149                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13150                 cg.setWidth(ebox.right - sbox.x -2);
13151
13152                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13153                 
13154             }
13155             
13156         });
13157         
13158         
13159         
13160     },
13161     
13162     onEventEnter: function (e, el,event,d) {
13163         this.fireEvent('evententer', this, el, event);
13164     },
13165     
13166     onEventLeave: function (e, el,event,d) {
13167         this.fireEvent('eventleave', this, el, event);
13168     },
13169     
13170     onEventClick: function (e, el,event,d) {
13171         this.fireEvent('eventclick', this, el, event);
13172     },
13173     
13174     onMonthChange: function () {
13175         this.store.load();
13176     },
13177     
13178     onMoreEventClick: function(e, el, more)
13179     {
13180         var _this = this;
13181         
13182         this.calpopover.placement = 'right';
13183         this.calpopover.setTitle('More');
13184         
13185         this.calpopover.setContent('');
13186         
13187         var ctr = this.calpopover.el.select('.popover-content', true).first();
13188         
13189         Roo.each(more, function(m){
13190             var cfg = {
13191                 cls : 'fc-event-hori fc-event-draggable',
13192                 html : m.title
13193             }
13194             var cg = ctr.createChild(cfg);
13195             
13196             cg.on('click', _this.onEventClick, _this, m);
13197         });
13198         
13199         this.calpopover.show(el);
13200         
13201         
13202     },
13203     
13204     onLoad: function () 
13205     {   
13206         this.calevents = [];
13207         var cal = this;
13208         
13209         if(this.store.getCount() > 0){
13210             this.store.data.each(function(d){
13211                cal.addItem({
13212                     id : d.data.id,
13213                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13214                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13215                     time : d.data.start_time,
13216                     title : d.data.title,
13217                     description : d.data.description,
13218                     venue : d.data.venue
13219                 });
13220             });
13221         }
13222         
13223         this.renderEvents();
13224         
13225         if(this.calevents.length && this.loadMask){
13226             this.maskEl.hide();
13227         }
13228     },
13229     
13230     onBeforeLoad: function()
13231     {
13232         this.clearEvents();
13233         if(this.loadMask){
13234             this.maskEl.show();
13235         }
13236     }
13237 });
13238
13239  
13240  /*
13241  * - LGPL
13242  *
13243  * element
13244  * 
13245  */
13246
13247 /**
13248  * @class Roo.bootstrap.Popover
13249  * @extends Roo.bootstrap.Component
13250  * Bootstrap Popover class
13251  * @cfg {String} html contents of the popover   (or false to use children..)
13252  * @cfg {String} title of popover (or false to hide)
13253  * @cfg {String} placement how it is placed
13254  * @cfg {String} trigger click || hover (or false to trigger manually)
13255  * @cfg {String} over what (parent or false to trigger manually.)
13256  * 
13257  * @constructor
13258  * Create a new Popover
13259  * @param {Object} config The config object
13260  */
13261
13262 Roo.bootstrap.Popover = function(config){
13263     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13264 };
13265
13266 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13267     
13268     title: 'Fill in a title',
13269     html: false,
13270     
13271     placement : 'right',
13272     trigger : 'hover', // hover
13273     
13274     over: 'parent',
13275     
13276     can_build_overlaid : false,
13277     
13278     getChildContainer : function()
13279     {
13280         return this.el.select('.popover-content',true).first();
13281     },
13282     
13283     getAutoCreate : function(){
13284          Roo.log('make popover?');
13285         var cfg = {
13286            cls : 'popover roo-dynamic',
13287            style: 'display:block',
13288            cn : [
13289                 {
13290                     cls : 'arrow'
13291                 },
13292                 {
13293                     cls : 'popover-inner',
13294                     cn : [
13295                         {
13296                             tag: 'h3',
13297                             cls: 'popover-title',
13298                             html : this.title
13299                         },
13300                         {
13301                             cls : 'popover-content',
13302                             html : this.html
13303                         }
13304                     ]
13305                     
13306                 }
13307            ]
13308         };
13309         
13310         return cfg;
13311     },
13312     setTitle: function(str)
13313     {
13314         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13315     },
13316     setContent: function(str)
13317     {
13318         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13319     },
13320     // as it get's added to the bottom of the page.
13321     onRender : function(ct, position)
13322     {
13323         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13324         if(!this.el){
13325             var cfg = Roo.apply({},  this.getAutoCreate());
13326             cfg.id = Roo.id();
13327             
13328             if (this.cls) {
13329                 cfg.cls += ' ' + this.cls;
13330             }
13331             if (this.style) {
13332                 cfg.style = this.style;
13333             }
13334             Roo.log("adding to ")
13335             this.el = Roo.get(document.body).createChild(cfg, position);
13336             Roo.log(this.el);
13337         }
13338         this.initEvents();
13339     },
13340     
13341     initEvents : function()
13342     {
13343         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13344         this.el.enableDisplayMode('block');
13345         this.el.hide();
13346         if (this.over === false) {
13347             return; 
13348         }
13349         if (this.triggers === false) {
13350             return;
13351         }
13352         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13353         var triggers = this.trigger ? this.trigger.split(' ') : [];
13354         Roo.each(triggers, function(trigger) {
13355         
13356             if (trigger == 'click') {
13357                 on_el.on('click', this.toggle, this);
13358             } else if (trigger != 'manual') {
13359                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13360                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13361       
13362                 on_el.on(eventIn  ,this.enter, this);
13363                 on_el.on(eventOut, this.leave, this);
13364             }
13365         }, this);
13366         
13367     },
13368     
13369     
13370     // private
13371     timeout : null,
13372     hoverState : null,
13373     
13374     toggle : function () {
13375         this.hoverState == 'in' ? this.leave() : this.enter();
13376     },
13377     
13378     enter : function () {
13379        
13380     
13381         clearTimeout(this.timeout);
13382     
13383         this.hoverState = 'in'
13384     
13385         if (!this.delay || !this.delay.show) {
13386             this.show();
13387             return 
13388         }
13389         var _t = this;
13390         this.timeout = setTimeout(function () {
13391             if (_t.hoverState == 'in') {
13392                 _t.show();
13393             }
13394         }, this.delay.show)
13395     },
13396     leave : function() {
13397         clearTimeout(this.timeout);
13398     
13399         this.hoverState = 'out'
13400     
13401         if (!this.delay || !this.delay.hide) {
13402             this.hide();
13403             return 
13404         }
13405         var _t = this;
13406         this.timeout = setTimeout(function () {
13407             if (_t.hoverState == 'out') {
13408                 _t.hide();
13409             }
13410         }, this.delay.hide)
13411     },
13412     
13413     show : function (on_el)
13414     {
13415         if (!on_el) {
13416             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13417         }
13418         // set content.
13419         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13420         if (this.html !== false) {
13421             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13422         }
13423         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13424         if (!this.title.length) {
13425             this.el.select('.popover-title',true).hide();
13426         }
13427         
13428         var placement = typeof this.placement == 'function' ?
13429             this.placement.call(this, this.el, on_el) :
13430             this.placement;
13431             
13432         var autoToken = /\s?auto?\s?/i;
13433         var autoPlace = autoToken.test(placement);
13434         if (autoPlace) {
13435             placement = placement.replace(autoToken, '') || 'top';
13436         }
13437         
13438         //this.el.detach()
13439         //this.el.setXY([0,0]);
13440         this.el.show();
13441         this.el.dom.style.display='block';
13442         this.el.addClass(placement);
13443         
13444         //this.el.appendTo(on_el);
13445         
13446         var p = this.getPosition();
13447         var box = this.el.getBox();
13448         
13449         if (autoPlace) {
13450             // fixme..
13451         }
13452         var align = Roo.bootstrap.Popover.alignment[placement]
13453         this.el.alignTo(on_el, align[0],align[1]);
13454         //var arrow = this.el.select('.arrow',true).first();
13455         //arrow.set(align[2], 
13456         
13457         this.el.addClass('in');
13458         this.hoverState = null;
13459         
13460         if (this.el.hasClass('fade')) {
13461             // fade it?
13462         }
13463         
13464     },
13465     hide : function()
13466     {
13467         this.el.setXY([0,0]);
13468         this.el.removeClass('in');
13469         this.el.hide();
13470         
13471     }
13472     
13473 });
13474
13475 Roo.bootstrap.Popover.alignment = {
13476     'left' : ['r-l', [-10,0], 'right'],
13477     'right' : ['l-r', [10,0], 'left'],
13478     'bottom' : ['t-b', [0,10], 'top'],
13479     'top' : [ 'b-t', [0,-10], 'bottom']
13480 };
13481
13482  /*
13483  * - LGPL
13484  *
13485  * Progress
13486  * 
13487  */
13488
13489 /**
13490  * @class Roo.bootstrap.Progress
13491  * @extends Roo.bootstrap.Component
13492  * Bootstrap Progress class
13493  * @cfg {Boolean} striped striped of the progress bar
13494  * @cfg {Boolean} active animated of the progress bar
13495  * 
13496  * 
13497  * @constructor
13498  * Create a new Progress
13499  * @param {Object} config The config object
13500  */
13501
13502 Roo.bootstrap.Progress = function(config){
13503     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13504 };
13505
13506 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13507     
13508     striped : false,
13509     active: false,
13510     
13511     getAutoCreate : function(){
13512         var cfg = {
13513             tag: 'div',
13514             cls: 'progress'
13515         };
13516         
13517         
13518         if(this.striped){
13519             cfg.cls += ' progress-striped';
13520         }
13521       
13522         if(this.active){
13523             cfg.cls += ' active';
13524         }
13525         
13526         
13527         return cfg;
13528     }
13529    
13530 });
13531
13532  
13533
13534  /*
13535  * - LGPL
13536  *
13537  * ProgressBar
13538  * 
13539  */
13540
13541 /**
13542  * @class Roo.bootstrap.ProgressBar
13543  * @extends Roo.bootstrap.Component
13544  * Bootstrap ProgressBar class
13545  * @cfg {Number} aria_valuenow aria-value now
13546  * @cfg {Number} aria_valuemin aria-value min
13547  * @cfg {Number} aria_valuemax aria-value max
13548  * @cfg {String} label label for the progress bar
13549  * @cfg {String} panel (success | info | warning | danger )
13550  * @cfg {String} role role of the progress bar
13551  * @cfg {String} sr_only text
13552  * 
13553  * 
13554  * @constructor
13555  * Create a new ProgressBar
13556  * @param {Object} config The config object
13557  */
13558
13559 Roo.bootstrap.ProgressBar = function(config){
13560     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13561 };
13562
13563 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13564     
13565     aria_valuenow : 0,
13566     aria_valuemin : 0,
13567     aria_valuemax : 100,
13568     label : false,
13569     panel : false,
13570     role : false,
13571     sr_only: false,
13572     
13573     getAutoCreate : function()
13574     {
13575         
13576         var cfg = {
13577             tag: 'div',
13578             cls: 'progress-bar',
13579             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13580         };
13581         
13582         if(this.sr_only){
13583             cfg.cn = {
13584                 tag: 'span',
13585                 cls: 'sr-only',
13586                 html: this.sr_only
13587             }
13588         }
13589         
13590         if(this.role){
13591             cfg.role = this.role;
13592         }
13593         
13594         if(this.aria_valuenow){
13595             cfg['aria-valuenow'] = this.aria_valuenow;
13596         }
13597         
13598         if(this.aria_valuemin){
13599             cfg['aria-valuemin'] = this.aria_valuemin;
13600         }
13601         
13602         if(this.aria_valuemax){
13603             cfg['aria-valuemax'] = this.aria_valuemax;
13604         }
13605         
13606         if(this.label && !this.sr_only){
13607             cfg.html = this.label;
13608         }
13609         
13610         if(this.panel){
13611             cfg.cls += ' progress-bar-' + this.panel;
13612         }
13613         
13614         return cfg;
13615     },
13616     
13617     update : function(aria_valuenow)
13618     {
13619         this.aria_valuenow = aria_valuenow;
13620         
13621         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13622     }
13623    
13624 });
13625
13626  
13627
13628  /*
13629  * - LGPL
13630  *
13631  * TabPanel
13632  * 
13633  */
13634
13635 /**
13636  * @class Roo.bootstrap.TabPanel
13637  * @extends Roo.bootstrap.Component
13638  * Bootstrap TabPanel class
13639  * @cfg {Boolean} active panel active
13640  * @cfg {String} html panel content
13641  * @cfg {String} tabId tab relate id
13642  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
13643  * 
13644  * 
13645  * @constructor
13646  * Create a new TabPanel
13647  * @param {Object} config The config object
13648  */
13649
13650 Roo.bootstrap.TabPanel = function(config){
13651     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
13652      this.addEvents({
13653         /**
13654              * @event changed
13655              * Fires when the active status changes
13656              * @param {Roo.bootstrap.TabPanel} this
13657              * @param {Boolean} state the new state
13658             
13659          */
13660         'changed': true
13661      });
13662 };
13663
13664 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
13665     
13666     active: false,
13667     html: false,
13668     tabId: false,
13669     navId : false,
13670     
13671     getAutoCreate : function(){
13672         var cfg = {
13673             tag: 'div',
13674             cls: 'tab-pane',
13675             html: this.html || ''
13676         };
13677         
13678         if(this.active){
13679             cfg.cls += ' active';
13680         }
13681         
13682         if(this.tabId){
13683             cfg.tabId = this.tabId;
13684         }
13685         
13686         return cfg;
13687     },
13688     onRender : function(ct, position)
13689     {
13690        // Roo.log("Call onRender: " + this.xtype);
13691         
13692         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
13693         
13694         if (this.navId && this.tabId) {
13695             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
13696             if (!item) {
13697                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
13698             } else {
13699                 item.on('changed', function(item, state) {
13700                     this.setActive(state);
13701                 }, this);
13702             }
13703         }
13704         
13705     },
13706     setActive: function(state)
13707     {
13708         Roo.log("panel - set active " + this.tabId + "=" + state);
13709         
13710         this.active = state;
13711         if (!state) {
13712             this.el.removeClass('active');
13713             
13714         } else  if (!this.el.hasClass('active')) {
13715             this.el.addClass('active');
13716         }
13717         this.fireEvent('changed', this, state);
13718     }
13719     
13720     
13721 });
13722  
13723
13724  
13725
13726  /*
13727  * - LGPL
13728  *
13729  * DateField
13730  * 
13731  */
13732
13733 /**
13734  * @class Roo.bootstrap.DateField
13735  * @extends Roo.bootstrap.Input
13736  * Bootstrap DateField class
13737  * @cfg {Number} weekStart default 0
13738  * @cfg {Number} weekStart default 0
13739  * @cfg {Number} viewMode default empty, (months|years)
13740  * @cfg {Number} minViewMode default empty, (months|years)
13741  * @cfg {Number} startDate default -Infinity
13742  * @cfg {Number} endDate default Infinity
13743  * @cfg {Boolean} todayHighlight default false
13744  * @cfg {Boolean} todayBtn default false
13745  * @cfg {Boolean} calendarWeeks default false
13746  * @cfg {Object} daysOfWeekDisabled default empty
13747  * 
13748  * @cfg {Boolean} keyboardNavigation default true
13749  * @cfg {String} language default en
13750  * 
13751  * @constructor
13752  * Create a new DateField
13753  * @param {Object} config The config object
13754  */
13755
13756 Roo.bootstrap.DateField = function(config){
13757     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
13758      this.addEvents({
13759             /**
13760              * @event show
13761              * Fires when this field show.
13762              * @param {Roo.bootstrap.DateField} this
13763              * @param {Mixed} date The date value
13764              */
13765             show : true,
13766             /**
13767              * @event show
13768              * Fires when this field hide.
13769              * @param {Roo.bootstrap.DateField} this
13770              * @param {Mixed} date The date value
13771              */
13772             hide : true,
13773             /**
13774              * @event select
13775              * Fires when select a date.
13776              * @param {Roo.bootstrap.DateField} this
13777              * @param {Mixed} date The date value
13778              */
13779             select : true
13780         });
13781 };
13782
13783 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
13784     
13785     /**
13786      * @cfg {String} format
13787      * The default date format string which can be overriden for localization support.  The format must be
13788      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
13789      */
13790     format : "m/d/y",
13791     /**
13792      * @cfg {String} altFormats
13793      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
13794      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
13795      */
13796     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
13797     
13798     weekStart : 0,
13799     
13800     viewMode : '',
13801     
13802     minViewMode : '',
13803     
13804     todayHighlight : false,
13805     
13806     todayBtn: false,
13807     
13808     language: 'en',
13809     
13810     keyboardNavigation: true,
13811     
13812     calendarWeeks: false,
13813     
13814     startDate: -Infinity,
13815     
13816     endDate: Infinity,
13817     
13818     daysOfWeekDisabled: [],
13819     
13820     _events: [],
13821     
13822     UTCDate: function()
13823     {
13824         return new Date(Date.UTC.apply(Date, arguments));
13825     },
13826     
13827     UTCToday: function()
13828     {
13829         var today = new Date();
13830         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
13831     },
13832     
13833     getDate: function() {
13834             var d = this.getUTCDate();
13835             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
13836     },
13837     
13838     getUTCDate: function() {
13839             return this.date;
13840     },
13841     
13842     setDate: function(d) {
13843             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
13844     },
13845     
13846     setUTCDate: function(d) {
13847             this.date = d;
13848             this.setValue(this.formatDate(this.date));
13849     },
13850         
13851     onRender: function(ct, position)
13852     {
13853         
13854         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
13855         
13856         this.language = this.language || 'en';
13857         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
13858         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
13859         
13860         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
13861         this.format = this.format || 'm/d/y';
13862         this.isInline = false;
13863         this.isInput = true;
13864         this.component = this.el.select('.add-on', true).first() || false;
13865         this.component = (this.component && this.component.length === 0) ? false : this.component;
13866         this.hasInput = this.component && this.inputEL().length;
13867         
13868         if (typeof(this.minViewMode === 'string')) {
13869             switch (this.minViewMode) {
13870                 case 'months':
13871                     this.minViewMode = 1;
13872                     break;
13873                 case 'years':
13874                     this.minViewMode = 2;
13875                     break;
13876                 default:
13877                     this.minViewMode = 0;
13878                     break;
13879             }
13880         }
13881         
13882         if (typeof(this.viewMode === 'string')) {
13883             switch (this.viewMode) {
13884                 case 'months':
13885                     this.viewMode = 1;
13886                     break;
13887                 case 'years':
13888                     this.viewMode = 2;
13889                     break;
13890                 default:
13891                     this.viewMode = 0;
13892                     break;
13893             }
13894         }
13895                 
13896         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
13897         
13898         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13899         
13900         this.picker().on('mousedown', this.onMousedown, this);
13901         this.picker().on('click', this.onClick, this);
13902         
13903         this.picker().addClass('datepicker-dropdown');
13904         
13905         this.startViewMode = this.viewMode;
13906         
13907         
13908         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
13909             if(!this.calendarWeeks){
13910                 v.remove();
13911                 return;
13912             };
13913             
13914             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
13915             v.attr('colspan', function(i, val){
13916                 return parseInt(val) + 1;
13917             });
13918         })
13919                         
13920         
13921         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
13922         
13923         this.setStartDate(this.startDate);
13924         this.setEndDate(this.endDate);
13925         
13926         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
13927         
13928         this.fillDow();
13929         this.fillMonths();
13930         this.update();
13931         this.showMode();
13932         
13933         if(this.isInline) {
13934             this.show();
13935         }
13936     },
13937     
13938     picker : function()
13939     {
13940         return this.el.select('.datepicker', true).first();
13941     },
13942     
13943     fillDow: function()
13944     {
13945         var dowCnt = this.weekStart;
13946         
13947         var dow = {
13948             tag: 'tr',
13949             cn: [
13950                 
13951             ]
13952         };
13953         
13954         if(this.calendarWeeks){
13955             dow.cn.push({
13956                 tag: 'th',
13957                 cls: 'cw',
13958                 html: '&nbsp;'
13959             })
13960         }
13961         
13962         while (dowCnt < this.weekStart + 7) {
13963             dow.cn.push({
13964                 tag: 'th',
13965                 cls: 'dow',
13966                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
13967             });
13968         }
13969         
13970         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
13971     },
13972     
13973     fillMonths: function()
13974     {    
13975         var i = 0
13976         var months = this.picker().select('>.datepicker-months td', true).first();
13977         
13978         months.dom.innerHTML = '';
13979         
13980         while (i < 12) {
13981             var month = {
13982                 tag: 'span',
13983                 cls: 'month',
13984                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
13985             }
13986             
13987             months.createChild(month);
13988         }
13989         
13990     },
13991     
13992     update: function()
13993     {
13994         
13995         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
13996         
13997         if (this.date < this.startDate) {
13998             this.viewDate = new Date(this.startDate);
13999         } else if (this.date > this.endDate) {
14000             this.viewDate = new Date(this.endDate);
14001         } else {
14002             this.viewDate = new Date(this.date);
14003         }
14004         
14005         this.fill();
14006     },
14007     
14008     fill: function() 
14009     {
14010         var d = new Date(this.viewDate),
14011                 year = d.getUTCFullYear(),
14012                 month = d.getUTCMonth(),
14013                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14014                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14015                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14016                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14017                 currentDate = this.date && this.date.valueOf(),
14018                 today = this.UTCToday();
14019         
14020         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14021         
14022 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14023         
14024 //        this.picker.select('>tfoot th.today').
14025 //                                              .text(dates[this.language].today)
14026 //                                              .toggle(this.todayBtn !== false);
14027     
14028         this.updateNavArrows();
14029         this.fillMonths();
14030                                                 
14031         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14032         
14033         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14034          
14035         prevMonth.setUTCDate(day);
14036         
14037         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14038         
14039         var nextMonth = new Date(prevMonth);
14040         
14041         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14042         
14043         nextMonth = nextMonth.valueOf();
14044         
14045         var fillMonths = false;
14046         
14047         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14048         
14049         while(prevMonth.valueOf() < nextMonth) {
14050             var clsName = '';
14051             
14052             if (prevMonth.getUTCDay() === this.weekStart) {
14053                 if(fillMonths){
14054                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14055                 }
14056                     
14057                 fillMonths = {
14058                     tag: 'tr',
14059                     cn: []
14060                 };
14061                 
14062                 if(this.calendarWeeks){
14063                     // ISO 8601: First week contains first thursday.
14064                     // ISO also states week starts on Monday, but we can be more abstract here.
14065                     var
14066                     // Start of current week: based on weekstart/current date
14067                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14068                     // Thursday of this week
14069                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14070                     // First Thursday of year, year from thursday
14071                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14072                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14073                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14074                     
14075                     fillMonths.cn.push({
14076                         tag: 'td',
14077                         cls: 'cw',
14078                         html: calWeek
14079                     });
14080                 }
14081             }
14082             
14083             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14084                 clsName += ' old';
14085             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14086                 clsName += ' new';
14087             }
14088             if (this.todayHighlight &&
14089                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14090                 prevMonth.getUTCMonth() == today.getMonth() &&
14091                 prevMonth.getUTCDate() == today.getDate()) {
14092                 clsName += ' today';
14093             }
14094             
14095             if (currentDate && prevMonth.valueOf() === currentDate) {
14096                 clsName += ' active';
14097             }
14098             
14099             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14100                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14101                     clsName += ' disabled';
14102             }
14103             
14104             fillMonths.cn.push({
14105                 tag: 'td',
14106                 cls: 'day ' + clsName,
14107                 html: prevMonth.getDate()
14108             })
14109             
14110             prevMonth.setDate(prevMonth.getDate()+1);
14111         }
14112           
14113         var currentYear = this.date && this.date.getUTCFullYear();
14114         var currentMonth = this.date && this.date.getUTCMonth();
14115         
14116         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14117         
14118         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14119             v.removeClass('active');
14120             
14121             if(currentYear === year && k === currentMonth){
14122                 v.addClass('active');
14123             }
14124             
14125             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14126                 v.addClass('disabled');
14127             }
14128             
14129         });
14130         
14131         
14132         year = parseInt(year/10, 10) * 10;
14133         
14134         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14135         
14136         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14137         
14138         year -= 1;
14139         for (var i = -1; i < 11; i++) {
14140             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14141                 tag: 'span',
14142                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14143                 html: year
14144             })
14145             
14146             year += 1;
14147         }
14148     },
14149     
14150     showMode: function(dir) 
14151     {
14152         if (dir) {
14153             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14154         }
14155         Roo.each(this.picker().select('>div',true).elements, function(v){
14156             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14157             v.hide();
14158         });
14159         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14160     },
14161     
14162     place: function()
14163     {
14164         if(this.isInline) return;
14165         
14166         this.picker().removeClass(['bottom', 'top']);
14167         
14168         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14169             /*
14170              * place to the top of element!
14171              *
14172              */
14173             
14174             this.picker().addClass('top');
14175             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14176             
14177             return;
14178         }
14179         
14180         this.picker().addClass('bottom');
14181         
14182         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14183     },
14184     
14185     parseDate : function(value)
14186     {
14187         if(!value || value instanceof Date){
14188             return value;
14189         }
14190         var v = Date.parseDate(value, this.format);
14191         if (!v && this.useIso) {
14192             v = Date.parseDate(value, 'Y-m-d');
14193         }
14194         if(!v && this.altFormats){
14195             if(!this.altFormatsArray){
14196                 this.altFormatsArray = this.altFormats.split("|");
14197             }
14198             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14199                 v = Date.parseDate(value, this.altFormatsArray[i]);
14200             }
14201         }
14202         return v;
14203     },
14204     
14205     formatDate : function(date, fmt)
14206     {
14207         return (!date || !(date instanceof Date)) ?
14208         date : date.dateFormat(fmt || this.format);
14209     },
14210     
14211     onFocus : function()
14212     {
14213         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14214         this.show();
14215     },
14216     
14217     onBlur : function()
14218     {
14219         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14220         
14221         var d = this.inputEl().getValue();
14222         
14223         if(d && d.length){
14224             this.setValue(d);
14225         }
14226                 
14227         this.hide();
14228     },
14229     
14230     show : function()
14231     {
14232         this.picker().show();
14233         this.update();
14234         this.place();
14235         
14236         this.fireEvent('show', this, this.date);
14237     },
14238     
14239     hide : function()
14240     {
14241         if(this.isInline) return;
14242         this.picker().hide();
14243         this.viewMode = this.startViewMode;
14244         this.showMode();
14245         
14246         this.fireEvent('hide', this, this.date);
14247         
14248     },
14249     
14250     onMousedown: function(e)
14251     {
14252         e.stopPropagation();
14253         e.preventDefault();
14254     },
14255     
14256     keyup: function(e)
14257     {
14258         Roo.bootstrap.DateField.superclass.keyup.call(this);
14259         this.update();
14260     },
14261
14262     setValue: function(v)
14263     {
14264         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14265         
14266         var d = new Date(v);
14267         
14268         if(isNaN(d.getTime())){
14269             return;
14270         }
14271         
14272         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14273
14274         this.update();
14275
14276         this.fireEvent('select', this, this.date);
14277         
14278     },
14279     
14280     getValue: function()
14281     {
14282         return this.formatDate(this.date);
14283     },
14284     
14285     fireKey: function(e)
14286     {
14287         if (!this.picker().isVisible()){
14288             if (e.keyCode == 27) // allow escape to hide and re-show picker
14289                 this.show();
14290             return;
14291         }
14292         var dateChanged = false,
14293         dir, day, month,
14294         newDate, newViewDate;
14295         
14296         switch(e.keyCode){
14297             case 27: // escape
14298                 this.hide();
14299                 e.preventDefault();
14300                 break;
14301             case 37: // left
14302             case 39: // right
14303                 if (!this.keyboardNavigation) break;
14304                 dir = e.keyCode == 37 ? -1 : 1;
14305                 
14306                 if (e.ctrlKey){
14307                     newDate = this.moveYear(this.date, dir);
14308                     newViewDate = this.moveYear(this.viewDate, dir);
14309                 } else if (e.shiftKey){
14310                     newDate = this.moveMonth(this.date, dir);
14311                     newViewDate = this.moveMonth(this.viewDate, dir);
14312                 } else {
14313                     newDate = new Date(this.date);
14314                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14315                     newViewDate = new Date(this.viewDate);
14316                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14317                 }
14318                 if (this.dateWithinRange(newDate)){
14319                     this.date = newDate;
14320                     this.viewDate = newViewDate;
14321                     this.setValue(this.formatDate(this.date));
14322 //                    this.update();
14323                     e.preventDefault();
14324                     dateChanged = true;
14325                 }
14326                 break;
14327             case 38: // up
14328             case 40: // down
14329                 if (!this.keyboardNavigation) break;
14330                 dir = e.keyCode == 38 ? -1 : 1;
14331                 if (e.ctrlKey){
14332                     newDate = this.moveYear(this.date, dir);
14333                     newViewDate = this.moveYear(this.viewDate, dir);
14334                 } else if (e.shiftKey){
14335                     newDate = this.moveMonth(this.date, dir);
14336                     newViewDate = this.moveMonth(this.viewDate, dir);
14337                 } else {
14338                     newDate = new Date(this.date);
14339                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14340                     newViewDate = new Date(this.viewDate);
14341                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14342                 }
14343                 if (this.dateWithinRange(newDate)){
14344                     this.date = newDate;
14345                     this.viewDate = newViewDate;
14346                     this.setValue(this.formatDate(this.date));
14347 //                    this.update();
14348                     e.preventDefault();
14349                     dateChanged = true;
14350                 }
14351                 break;
14352             case 13: // enter
14353                 this.setValue(this.formatDate(this.date));
14354                 this.hide();
14355                 e.preventDefault();
14356                 break;
14357             case 9: // tab
14358                 this.setValue(this.formatDate(this.date));
14359                 this.hide();
14360                 break;
14361                 
14362         }
14363     },
14364     
14365     
14366     onClick: function(e) 
14367     {
14368         e.stopPropagation();
14369         e.preventDefault();
14370         
14371         var target = e.getTarget();
14372         
14373         if(target.nodeName.toLowerCase() === 'i'){
14374             target = Roo.get(target).dom.parentNode;
14375         }
14376         
14377         var nodeName = target.nodeName;
14378         var className = target.className;
14379         var html = target.innerHTML;
14380         
14381         switch(nodeName.toLowerCase()) {
14382             case 'th':
14383                 switch(className) {
14384                     case 'switch':
14385                         this.showMode(1);
14386                         break;
14387                     case 'prev':
14388                     case 'next':
14389                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14390                         switch(this.viewMode){
14391                                 case 0:
14392                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14393                                         break;
14394                                 case 1:
14395                                 case 2:
14396                                         this.viewDate = this.moveYear(this.viewDate, dir);
14397                                         break;
14398                         }
14399                         this.fill();
14400                         break;
14401                     case 'today':
14402                         var date = new Date();
14403                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14404 //                        this.fill()
14405                         this.setValue(this.formatDate(this.date));
14406                         
14407                         this.hide();
14408                         break;
14409                 }
14410                 break;
14411             case 'span':
14412                 if (className.indexOf('disabled') === -1) {
14413                     this.viewDate.setUTCDate(1);
14414                     if (className.indexOf('month') !== -1) {
14415                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14416                     } else {
14417                         var year = parseInt(html, 10) || 0;
14418                         this.viewDate.setUTCFullYear(year);
14419                         
14420                     }
14421                     this.showMode(-1);
14422                     this.fill();
14423                 }
14424                 break;
14425                 
14426             case 'td':
14427                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14428                     var day = parseInt(html, 10) || 1;
14429                     var year = this.viewDate.getUTCFullYear(),
14430                         month = this.viewDate.getUTCMonth();
14431
14432                     if (className.indexOf('old') !== -1) {
14433                         if(month === 0 ){
14434                             month = 11;
14435                             year -= 1;
14436                         }else{
14437                             month -= 1;
14438                         }
14439                     } else if (className.indexOf('new') !== -1) {
14440                         if (month == 11) {
14441                             month = 0;
14442                             year += 1;
14443                         } else {
14444                             month += 1;
14445                         }
14446                     }
14447                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14448                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14449 //                    this.fill();
14450                     this.setValue(this.formatDate(this.date));
14451                     this.hide();
14452                 }
14453                 break;
14454         }
14455     },
14456     
14457     setStartDate: function(startDate)
14458     {
14459         this.startDate = startDate || -Infinity;
14460         if (this.startDate !== -Infinity) {
14461             this.startDate = this.parseDate(this.startDate);
14462         }
14463         this.update();
14464         this.updateNavArrows();
14465     },
14466
14467     setEndDate: function(endDate)
14468     {
14469         this.endDate = endDate || Infinity;
14470         if (this.endDate !== Infinity) {
14471             this.endDate = this.parseDate(this.endDate);
14472         }
14473         this.update();
14474         this.updateNavArrows();
14475     },
14476     
14477     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14478     {
14479         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14480         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14481             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14482         }
14483         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14484             return parseInt(d, 10);
14485         });
14486         this.update();
14487         this.updateNavArrows();
14488     },
14489     
14490     updateNavArrows: function() 
14491     {
14492         var d = new Date(this.viewDate),
14493         year = d.getUTCFullYear(),
14494         month = d.getUTCMonth();
14495         
14496         Roo.each(this.picker().select('.prev', true).elements, function(v){
14497             v.show();
14498             switch (this.viewMode) {
14499                 case 0:
14500
14501                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
14502                         v.hide();
14503                     }
14504                     break;
14505                 case 1:
14506                 case 2:
14507                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
14508                         v.hide();
14509                     }
14510                     break;
14511             }
14512         });
14513         
14514         Roo.each(this.picker().select('.next', true).elements, function(v){
14515             v.show();
14516             switch (this.viewMode) {
14517                 case 0:
14518
14519                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
14520                         v.hide();
14521                     }
14522                     break;
14523                 case 1:
14524                 case 2:
14525                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
14526                         v.hide();
14527                     }
14528                     break;
14529             }
14530         })
14531     },
14532     
14533     moveMonth: function(date, dir)
14534     {
14535         if (!dir) return date;
14536         var new_date = new Date(date.valueOf()),
14537         day = new_date.getUTCDate(),
14538         month = new_date.getUTCMonth(),
14539         mag = Math.abs(dir),
14540         new_month, test;
14541         dir = dir > 0 ? 1 : -1;
14542         if (mag == 1){
14543             test = dir == -1
14544             // If going back one month, make sure month is not current month
14545             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
14546             ? function(){
14547                 return new_date.getUTCMonth() == month;
14548             }
14549             // If going forward one month, make sure month is as expected
14550             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
14551             : function(){
14552                 return new_date.getUTCMonth() != new_month;
14553             };
14554             new_month = month + dir;
14555             new_date.setUTCMonth(new_month);
14556             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
14557             if (new_month < 0 || new_month > 11)
14558                 new_month = (new_month + 12) % 12;
14559         } else {
14560             // For magnitudes >1, move one month at a time...
14561             for (var i=0; i<mag; i++)
14562                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
14563                 new_date = this.moveMonth(new_date, dir);
14564             // ...then reset the day, keeping it in the new month
14565             new_month = new_date.getUTCMonth();
14566             new_date.setUTCDate(day);
14567             test = function(){
14568                 return new_month != new_date.getUTCMonth();
14569             };
14570         }
14571         // Common date-resetting loop -- if date is beyond end of month, make it
14572         // end of month
14573         while (test()){
14574             new_date.setUTCDate(--day);
14575             new_date.setUTCMonth(new_month);
14576         }
14577         return new_date;
14578     },
14579
14580     moveYear: function(date, dir)
14581     {
14582         return this.moveMonth(date, dir*12);
14583     },
14584
14585     dateWithinRange: function(date)
14586     {
14587         return date >= this.startDate && date <= this.endDate;
14588     },
14589
14590     
14591     remove: function() 
14592     {
14593         this.picker().remove();
14594     }
14595    
14596 });
14597
14598 Roo.apply(Roo.bootstrap.DateField,  {
14599     
14600     head : {
14601         tag: 'thead',
14602         cn: [
14603         {
14604             tag: 'tr',
14605             cn: [
14606             {
14607                 tag: 'th',
14608                 cls: 'prev',
14609                 html: '<i class="fa fa-arrow-left"/>'
14610             },
14611             {
14612                 tag: 'th',
14613                 cls: 'switch',
14614                 colspan: '5'
14615             },
14616             {
14617                 tag: 'th',
14618                 cls: 'next',
14619                 html: '<i class="fa fa-arrow-right"/>'
14620             }
14621
14622             ]
14623         }
14624         ]
14625     },
14626     
14627     content : {
14628         tag: 'tbody',
14629         cn: [
14630         {
14631             tag: 'tr',
14632             cn: [
14633             {
14634                 tag: 'td',
14635                 colspan: '7'
14636             }
14637             ]
14638         }
14639         ]
14640     },
14641     
14642     footer : {
14643         tag: 'tfoot',
14644         cn: [
14645         {
14646             tag: 'tr',
14647             cn: [
14648             {
14649                 tag: 'th',
14650                 colspan: '7',
14651                 cls: 'today'
14652             }
14653                     
14654             ]
14655         }
14656         ]
14657     },
14658     
14659     dates:{
14660         en: {
14661             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
14662             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
14663             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
14664             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
14665             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
14666             today: "Today"
14667         }
14668     },
14669     
14670     modes: [
14671     {
14672         clsName: 'days',
14673         navFnc: 'Month',
14674         navStep: 1
14675     },
14676     {
14677         clsName: 'months',
14678         navFnc: 'FullYear',
14679         navStep: 1
14680     },
14681     {
14682         clsName: 'years',
14683         navFnc: 'FullYear',
14684         navStep: 10
14685     }]
14686 });
14687
14688 Roo.apply(Roo.bootstrap.DateField,  {
14689   
14690     template : {
14691         tag: 'div',
14692         cls: 'datepicker dropdown-menu',
14693         cn: [
14694         {
14695             tag: 'div',
14696             cls: 'datepicker-days',
14697             cn: [
14698             {
14699                 tag: 'table',
14700                 cls: 'table-condensed',
14701                 cn:[
14702                 Roo.bootstrap.DateField.head,
14703                 {
14704                     tag: 'tbody'
14705                 },
14706                 Roo.bootstrap.DateField.footer
14707                 ]
14708             }
14709             ]
14710         },
14711         {
14712             tag: 'div',
14713             cls: 'datepicker-months',
14714             cn: [
14715             {
14716                 tag: 'table',
14717                 cls: 'table-condensed',
14718                 cn:[
14719                 Roo.bootstrap.DateField.head,
14720                 Roo.bootstrap.DateField.content,
14721                 Roo.bootstrap.DateField.footer
14722                 ]
14723             }
14724             ]
14725         },
14726         {
14727             tag: 'div',
14728             cls: 'datepicker-years',
14729             cn: [
14730             {
14731                 tag: 'table',
14732                 cls: 'table-condensed',
14733                 cn:[
14734                 Roo.bootstrap.DateField.head,
14735                 Roo.bootstrap.DateField.content,
14736                 Roo.bootstrap.DateField.footer
14737                 ]
14738             }
14739             ]
14740         }
14741         ]
14742     }
14743 });
14744
14745  
14746
14747  /*
14748  * - LGPL
14749  *
14750  * TimeField
14751  * 
14752  */
14753
14754 /**
14755  * @class Roo.bootstrap.TimeField
14756  * @extends Roo.bootstrap.Input
14757  * Bootstrap DateField class
14758  * 
14759  * 
14760  * @constructor
14761  * Create a new TimeField
14762  * @param {Object} config The config object
14763  */
14764
14765 Roo.bootstrap.TimeField = function(config){
14766     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
14767     this.addEvents({
14768             /**
14769              * @event show
14770              * Fires when this field show.
14771              * @param {Roo.bootstrap.DateField} this
14772              * @param {Mixed} date The date value
14773              */
14774             show : true,
14775             /**
14776              * @event show
14777              * Fires when this field hide.
14778              * @param {Roo.bootstrap.DateField} this
14779              * @param {Mixed} date The date value
14780              */
14781             hide : true,
14782             /**
14783              * @event select
14784              * Fires when select a date.
14785              * @param {Roo.bootstrap.DateField} this
14786              * @param {Mixed} date The date value
14787              */
14788             select : true
14789         });
14790 };
14791
14792 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
14793     
14794     /**
14795      * @cfg {String} format
14796      * The default time format string which can be overriden for localization support.  The format must be
14797      * valid according to {@link Date#parseDate} (defaults to 'H:i').
14798      */
14799     format : "H:i",
14800        
14801     onRender: function(ct, position)
14802     {
14803         
14804         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
14805                 
14806         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
14807         
14808         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14809         
14810         this.pop = this.picker().select('>.datepicker-time',true).first();
14811         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
14812         
14813         this.picker().on('mousedown', this.onMousedown, this);
14814         this.picker().on('click', this.onClick, this);
14815         
14816         this.picker().addClass('datepicker-dropdown');
14817     
14818         this.fillTime();
14819         this.update();
14820             
14821         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
14822         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
14823         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
14824         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
14825         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
14826         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
14827
14828     },
14829     
14830     fireKey: function(e){
14831         if (!this.picker().isVisible()){
14832             if (e.keyCode == 27) // allow escape to hide and re-show picker
14833                 this.show();
14834             return;
14835         }
14836
14837         e.preventDefault();
14838         
14839         switch(e.keyCode){
14840             case 27: // escape
14841                 this.hide();
14842                 break;
14843             case 37: // left
14844             case 39: // right
14845                 this.onTogglePeriod();
14846                 break;
14847             case 38: // up
14848                 this.onIncrementMinutes();
14849                 break;
14850             case 40: // down
14851                 this.onDecrementMinutes();
14852                 break;
14853             case 13: // enter
14854             case 9: // tab
14855                 this.setTime();
14856                 break;
14857         }
14858     },
14859     
14860     onClick: function(e) {
14861         e.stopPropagation();
14862         e.preventDefault();
14863     },
14864     
14865     picker : function()
14866     {
14867         return this.el.select('.datepicker', true).first();
14868     },
14869     
14870     fillTime: function()
14871     {    
14872         var time = this.pop.select('tbody', true).first();
14873         
14874         time.dom.innerHTML = '';
14875         
14876         time.createChild({
14877             tag: 'tr',
14878             cn: [
14879                 {
14880                     tag: 'td',
14881                     cn: [
14882                         {
14883                             tag: 'a',
14884                             href: '#',
14885                             cls: 'btn',
14886                             cn: [
14887                                 {
14888                                     tag: 'span',
14889                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
14890                                 }
14891                             ]
14892                         } 
14893                     ]
14894                 },
14895                 {
14896                     tag: 'td',
14897                     cls: 'separator'
14898                 },
14899                 {
14900                     tag: 'td',
14901                     cn: [
14902                         {
14903                             tag: 'a',
14904                             href: '#',
14905                             cls: 'btn',
14906                             cn: [
14907                                 {
14908                                     tag: 'span',
14909                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
14910                                 }
14911                             ]
14912                         }
14913                     ]
14914                 },
14915                 {
14916                     tag: 'td',
14917                     cls: 'separator'
14918                 }
14919             ]
14920         });
14921         
14922         time.createChild({
14923             tag: 'tr',
14924             cn: [
14925                 {
14926                     tag: 'td',
14927                     cn: [
14928                         {
14929                             tag: 'span',
14930                             cls: 'timepicker-hour',
14931                             html: '00'
14932                         }  
14933                     ]
14934                 },
14935                 {
14936                     tag: 'td',
14937                     cls: 'separator',
14938                     html: ':'
14939                 },
14940                 {
14941                     tag: 'td',
14942                     cn: [
14943                         {
14944                             tag: 'span',
14945                             cls: 'timepicker-minute',
14946                             html: '00'
14947                         }  
14948                     ]
14949                 },
14950                 {
14951                     tag: 'td',
14952                     cls: 'separator'
14953                 },
14954                 {
14955                     tag: 'td',
14956                     cn: [
14957                         {
14958                             tag: 'button',
14959                             type: 'button',
14960                             cls: 'btn btn-primary period',
14961                             html: 'AM'
14962                             
14963                         }
14964                     ]
14965                 }
14966             ]
14967         });
14968         
14969         time.createChild({
14970             tag: 'tr',
14971             cn: [
14972                 {
14973                     tag: 'td',
14974                     cn: [
14975                         {
14976                             tag: 'a',
14977                             href: '#',
14978                             cls: 'btn',
14979                             cn: [
14980                                 {
14981                                     tag: 'span',
14982                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
14983                                 }
14984                             ]
14985                         }
14986                     ]
14987                 },
14988                 {
14989                     tag: 'td',
14990                     cls: 'separator'
14991                 },
14992                 {
14993                     tag: 'td',
14994                     cn: [
14995                         {
14996                             tag: 'a',
14997                             href: '#',
14998                             cls: 'btn',
14999                             cn: [
15000                                 {
15001                                     tag: 'span',
15002                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15003                                 }
15004                             ]
15005                         }
15006                     ]
15007                 },
15008                 {
15009                     tag: 'td',
15010                     cls: 'separator'
15011                 }
15012             ]
15013         });
15014         
15015     },
15016     
15017     update: function()
15018     {
15019         
15020         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15021         
15022         this.fill();
15023     },
15024     
15025     fill: function() 
15026     {
15027         var hours = this.time.getHours();
15028         var minutes = this.time.getMinutes();
15029         var period = 'AM';
15030         
15031         if(hours > 11){
15032             period = 'PM';
15033         }
15034         
15035         if(hours == 0){
15036             hours = 12;
15037         }
15038         
15039         
15040         if(hours > 12){
15041             hours = hours - 12;
15042         }
15043         
15044         if(hours < 10){
15045             hours = '0' + hours;
15046         }
15047         
15048         if(minutes < 10){
15049             minutes = '0' + minutes;
15050         }
15051         
15052         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15053         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15054         this.pop.select('button', true).first().dom.innerHTML = period;
15055         
15056     },
15057     
15058     place: function()
15059     {   
15060         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15061         
15062         var cls = ['bottom'];
15063         
15064         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15065             cls.pop();
15066             cls.push('top');
15067         }
15068         
15069         cls.push('right');
15070         
15071         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15072             cls.pop();
15073             cls.push('left');
15074         }
15075         
15076         this.picker().addClass(cls.join('-'));
15077         
15078         var _this = this;
15079         
15080         Roo.each(cls, function(c){
15081             if(c == 'bottom'){
15082                 _this.picker().setTop(_this.inputEl().getHeight());
15083                 return;
15084             }
15085             if(c == 'top'){
15086                 _this.picker().setTop(0 - _this.picker().getHeight());
15087                 return;
15088             }
15089             
15090             if(c == 'left'){
15091                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15092                 return;
15093             }
15094             if(c == 'right'){
15095                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15096                 return;
15097             }
15098         });
15099         
15100     },
15101   
15102     onFocus : function()
15103     {
15104         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15105         this.show();
15106     },
15107     
15108     onBlur : function()
15109     {
15110         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15111         this.hide();
15112     },
15113     
15114     show : function()
15115     {
15116         this.picker().show();
15117         this.pop.show();
15118         this.update();
15119         this.place();
15120         
15121         this.fireEvent('show', this, this.date);
15122     },
15123     
15124     hide : function()
15125     {
15126         this.picker().hide();
15127         this.pop.hide();
15128         
15129         this.fireEvent('hide', this, this.date);
15130     },
15131     
15132     setTime : function()
15133     {
15134         this.hide();
15135         this.setValue(this.time.format(this.format));
15136         
15137         this.fireEvent('select', this, this.date);
15138         
15139         
15140     },
15141     
15142     onMousedown: function(e){
15143         e.stopPropagation();
15144         e.preventDefault();
15145     },
15146     
15147     onIncrementHours: function()
15148     {
15149         Roo.log('onIncrementHours');
15150         this.time = this.time.add(Date.HOUR, 1);
15151         this.update();
15152         
15153     },
15154     
15155     onDecrementHours: function()
15156     {
15157         Roo.log('onDecrementHours');
15158         this.time = this.time.add(Date.HOUR, -1);
15159         this.update();
15160     },
15161     
15162     onIncrementMinutes: function()
15163     {
15164         Roo.log('onIncrementMinutes');
15165         this.time = this.time.add(Date.MINUTE, 1);
15166         this.update();
15167     },
15168     
15169     onDecrementMinutes: function()
15170     {
15171         Roo.log('onDecrementMinutes');
15172         this.time = this.time.add(Date.MINUTE, -1);
15173         this.update();
15174     },
15175     
15176     onTogglePeriod: function()
15177     {
15178         Roo.log('onTogglePeriod');
15179         this.time = this.time.add(Date.HOUR, 12);
15180         this.update();
15181     }
15182     
15183    
15184 });
15185
15186 Roo.apply(Roo.bootstrap.TimeField,  {
15187     
15188     content : {
15189         tag: 'tbody',
15190         cn: [
15191             {
15192                 tag: 'tr',
15193                 cn: [
15194                 {
15195                     tag: 'td',
15196                     colspan: '7'
15197                 }
15198                 ]
15199             }
15200         ]
15201     },
15202     
15203     footer : {
15204         tag: 'tfoot',
15205         cn: [
15206             {
15207                 tag: 'tr',
15208                 cn: [
15209                 {
15210                     tag: 'th',
15211                     colspan: '7',
15212                     cls: '',
15213                     cn: [
15214                         {
15215                             tag: 'button',
15216                             cls: 'btn btn-info ok',
15217                             html: 'OK'
15218                         }
15219                     ]
15220                 }
15221
15222                 ]
15223             }
15224         ]
15225     }
15226 });
15227
15228 Roo.apply(Roo.bootstrap.TimeField,  {
15229   
15230     template : {
15231         tag: 'div',
15232         cls: 'datepicker dropdown-menu',
15233         cn: [
15234             {
15235                 tag: 'div',
15236                 cls: 'datepicker-time',
15237                 cn: [
15238                 {
15239                     tag: 'table',
15240                     cls: 'table-condensed',
15241                     cn:[
15242                     Roo.bootstrap.TimeField.content,
15243                     Roo.bootstrap.TimeField.footer
15244                     ]
15245                 }
15246                 ]
15247             }
15248         ]
15249     }
15250 });
15251
15252  
15253
15254  /*
15255  * - LGPL
15256  *
15257  * CheckBox
15258  * 
15259  */
15260
15261 /**
15262  * @class Roo.bootstrap.CheckBox
15263  * @extends Roo.bootstrap.Input
15264  * Bootstrap CheckBox class
15265  * 
15266  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15267  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15268  * @cfg {String} boxLabel The text that appears beside the checkbox
15269  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15270  * @cfg {Boolean} checked initnal the element
15271  * 
15272  * 
15273  * @constructor
15274  * Create a new CheckBox
15275  * @param {Object} config The config object
15276  */
15277
15278 Roo.bootstrap.CheckBox = function(config){
15279     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15280    
15281         this.addEvents({
15282             /**
15283             * @event check
15284             * Fires when the element is checked or unchecked.
15285             * @param {Roo.bootstrap.CheckBox} this This input
15286             * @param {Boolean} checked The new checked value
15287             */
15288            check : true
15289         });
15290 };
15291
15292 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15293     
15294     inputType: 'checkbox',
15295     inputValue: 1,
15296     valueOff: 0,
15297     boxLabel: false,
15298     checked: false,
15299     weight : false,
15300     
15301     getAutoCreate : function()
15302     {
15303         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15304         
15305         var id = Roo.id();
15306         
15307         var cfg = {};
15308         
15309         cfg.cls = 'form-group checkbox' //input-group
15310         
15311         
15312         
15313         
15314         var input =  {
15315             tag: 'input',
15316             id : id,
15317             type : this.inputType,
15318             value : (!this.checked) ? this.valueOff : this.inputValue,
15319             cls : 'roo-checkbox', //'form-box',
15320             placeholder : this.placeholder || ''
15321             
15322         };
15323         
15324         if (this.weight) { // Validity check?
15325             cfg.cls += " checkbox-" + this.weight;
15326         }
15327         
15328         if (this.disabled) {
15329             input.disabled=true;
15330         }
15331         
15332         if(this.checked){
15333             input.checked = this.checked;
15334         }
15335         
15336         if (this.name) {
15337             input.name = this.name;
15338         }
15339         
15340         if (this.size) {
15341             input.cls += ' input-' + this.size;
15342         }
15343         
15344         var settings=this;
15345         ['xs','sm','md','lg'].map(function(size){
15346             if (settings[size]) {
15347                 cfg.cls += ' col-' + size + '-' + settings[size];
15348             }
15349         });
15350         
15351        
15352         
15353         var inputblock = input;
15354         
15355         
15356         
15357         
15358         if (this.before || this.after) {
15359             
15360             inputblock = {
15361                 cls : 'input-group',
15362                 cn :  [] 
15363             };
15364             if (this.before) {
15365                 inputblock.cn.push({
15366                     tag :'span',
15367                     cls : 'input-group-addon',
15368                     html : this.before
15369                 });
15370             }
15371             inputblock.cn.push(input);
15372             if (this.after) {
15373                 inputblock.cn.push({
15374                     tag :'span',
15375                     cls : 'input-group-addon',
15376                     html : this.after
15377                 });
15378             }
15379             
15380         };
15381         
15382         if (align ==='left' && this.fieldLabel.length) {
15383                 Roo.log("left and has label");
15384                 cfg.cn = [
15385                     
15386                     {
15387                         tag: 'label',
15388                         'for' :  id,
15389                         cls : 'control-label col-md-' + this.labelWidth,
15390                         html : this.fieldLabel
15391                         
15392                     },
15393                     {
15394                         cls : "col-md-" + (12 - this.labelWidth), 
15395                         cn: [
15396                             inputblock
15397                         ]
15398                     }
15399                     
15400                 ];
15401         } else if ( this.fieldLabel.length) {
15402                 Roo.log(" label");
15403                 cfg.cn = [
15404                    
15405                     {
15406                         tag: this.boxLabel ? 'span' : 'label',
15407                         'for': id,
15408                         cls: 'control-label box-input-label',
15409                         //cls : 'input-group-addon',
15410                         html : this.fieldLabel
15411                         
15412                     },
15413                     
15414                     inputblock
15415                     
15416                 ];
15417
15418         } else {
15419             
15420                 Roo.log(" no label && no align");
15421                 cfg.cn = [  inputblock ] ;
15422                 
15423                 
15424         };
15425          if(this.boxLabel){
15426             cfg.cn.push( {
15427                 tag: 'label',
15428                 'for': id,
15429                 cls: 'box-label',
15430                 html: this.boxLabel
15431                 
15432             });
15433         }
15434         
15435         
15436        
15437         return cfg;
15438         
15439     },
15440     
15441     /**
15442      * return the real input element.
15443      */
15444     inputEl: function ()
15445     {
15446         return this.el.select('input.roo-checkbox',true).first();
15447     },
15448     
15449     label: function()
15450     {
15451         return this.el.select('label.control-label',true).first();
15452     },
15453     
15454     initEvents : function()
15455     {
15456 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15457         
15458         this.inputEl().on('click', this.onClick,  this);
15459         
15460     },
15461     
15462     onClick : function()
15463     {   
15464         this.setChecked(!this.checked);
15465     },
15466     
15467     setChecked : function(state,suppressEvent)
15468     {
15469         this.checked = state;
15470         
15471         this.inputEl().dom.checked = state;
15472         
15473         if(suppressEvent !== true){
15474             this.fireEvent('check', this, state);
15475         }
15476         
15477         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15478         
15479     },
15480     
15481     setValue : function(v,suppressEvent)
15482     {
15483         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15484     }
15485     
15486 });
15487
15488  
15489 /*
15490  * - LGPL
15491  *
15492  * Radio
15493  * 
15494  */
15495
15496 /**
15497  * @class Roo.bootstrap.Radio
15498  * @extends Roo.bootstrap.CheckBox
15499  * Bootstrap Radio class
15500
15501  * @constructor
15502  * Create a new Radio
15503  * @param {Object} config The config object
15504  */
15505
15506 Roo.bootstrap.Radio = function(config){
15507     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
15508    
15509 };
15510
15511 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
15512     
15513     inputType: 'radio',
15514     inputValue: '',
15515     valueOff: '',
15516     
15517     getAutoCreate : function()
15518     {
15519         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15520         
15521         var id = Roo.id();
15522         
15523         var cfg = {};
15524         
15525         cfg.cls = 'form-group radio' //input-group
15526         
15527         var input =  {
15528             tag: 'input',
15529             id : id,
15530             type : this.inputType,
15531             value : (!this.checked) ? this.valueOff : this.inputValue,
15532             cls : 'roo-radio',
15533             placeholder : this.placeholder || ''
15534             
15535         };
15536           if (this.weight) { // Validity check?
15537             cfg.cls += " radio-" + this.weight;
15538         }
15539         if (this.disabled) {
15540             input.disabled=true;
15541         }
15542         
15543         if(this.checked){
15544             input.checked = this.checked;
15545         }
15546         
15547         if (this.name) {
15548             input.name = this.name;
15549         }
15550         
15551         if (this.size) {
15552             input.cls += ' input-' + this.size;
15553         }
15554         
15555         var settings=this;
15556         ['xs','sm','md','lg'].map(function(size){
15557             if (settings[size]) {
15558                 cfg.cls += ' col-' + size + '-' + settings[size];
15559             }
15560         });
15561         
15562         var inputblock = input;
15563         
15564         if (this.before || this.after) {
15565             
15566             inputblock = {
15567                 cls : 'input-group',
15568                 cn :  [] 
15569             };
15570             if (this.before) {
15571                 inputblock.cn.push({
15572                     tag :'span',
15573                     cls : 'input-group-addon',
15574                     html : this.before
15575                 });
15576             }
15577             inputblock.cn.push(input);
15578             if (this.after) {
15579                 inputblock.cn.push({
15580                     tag :'span',
15581                     cls : 'input-group-addon',
15582                     html : this.after
15583                 });
15584             }
15585             
15586         };
15587         
15588         if (align ==='left' && this.fieldLabel.length) {
15589                 Roo.log("left and has label");
15590                 cfg.cn = [
15591                     
15592                     {
15593                         tag: 'label',
15594                         'for' :  id,
15595                         cls : 'control-label col-md-' + this.labelWidth,
15596                         html : this.fieldLabel
15597                         
15598                     },
15599                     {
15600                         cls : "col-md-" + (12 - this.labelWidth), 
15601                         cn: [
15602                             inputblock
15603                         ]
15604                     }
15605                     
15606                 ];
15607         } else if ( this.fieldLabel.length) {
15608                 Roo.log(" label");
15609                  cfg.cn = [
15610                    
15611                     {
15612                         tag: 'label',
15613                         'for': id,
15614                         cls: 'control-label box-input-label',
15615                         //cls : 'input-group-addon',
15616                         html : this.fieldLabel
15617                         
15618                     },
15619                     
15620                     inputblock
15621                     
15622                 ];
15623
15624         } else {
15625             
15626                    Roo.log(" no label && no align");
15627                 cfg.cn = [
15628                     
15629                         inputblock
15630                     
15631                 ];
15632                 
15633                 
15634         };
15635         
15636         if(this.boxLabel){
15637             cfg.cn.push({
15638                 tag: 'label',
15639                 'for': id,
15640                 cls: 'box-label',
15641                 html: this.boxLabel
15642             })
15643         }
15644         
15645         return cfg;
15646         
15647     },
15648     inputEl: function ()
15649     {
15650         return this.el.select('input.roo-radio',true).first();
15651     },
15652     onClick : function()
15653     {   
15654         this.setChecked(true);
15655     },
15656     
15657     setChecked : function(state,suppressEvent)
15658     {
15659         if(state){
15660             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
15661                 v.dom.checked = false;
15662             });
15663         }
15664         
15665         this.checked = state;
15666         this.inputEl().dom.checked = state;
15667         
15668         if(suppressEvent !== true){
15669             this.fireEvent('check', this, state);
15670         }
15671         
15672         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15673         
15674     },
15675     
15676     getGroupValue : function()
15677     {
15678         var value = ''
15679         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
15680             if(v.dom.checked == true){
15681                 value = v.dom.value;
15682             }
15683         });
15684         
15685         return value;
15686     },
15687     
15688     /**
15689      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
15690      * @return {Mixed} value The field value
15691      */
15692     getValue : function(){
15693         return this.getGroupValue();
15694     }
15695     
15696 });
15697
15698  
15699 //<script type="text/javascript">
15700
15701 /*
15702  * Based  Ext JS Library 1.1.1
15703  * Copyright(c) 2006-2007, Ext JS, LLC.
15704  * LGPL
15705  *
15706  */
15707  
15708 /**
15709  * @class Roo.HtmlEditorCore
15710  * @extends Roo.Component
15711  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
15712  *
15713  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
15714  */
15715
15716 Roo.HtmlEditorCore = function(config){
15717     
15718     
15719     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
15720     this.addEvents({
15721         /**
15722          * @event initialize
15723          * Fires when the editor is fully initialized (including the iframe)
15724          * @param {Roo.HtmlEditorCore} this
15725          */
15726         initialize: true,
15727         /**
15728          * @event activate
15729          * Fires when the editor is first receives the focus. Any insertion must wait
15730          * until after this event.
15731          * @param {Roo.HtmlEditorCore} this
15732          */
15733         activate: true,
15734          /**
15735          * @event beforesync
15736          * Fires before the textarea is updated with content from the editor iframe. Return false
15737          * to cancel the sync.
15738          * @param {Roo.HtmlEditorCore} this
15739          * @param {String} html
15740          */
15741         beforesync: true,
15742          /**
15743          * @event beforepush
15744          * Fires before the iframe editor is updated with content from the textarea. Return false
15745          * to cancel the push.
15746          * @param {Roo.HtmlEditorCore} this
15747          * @param {String} html
15748          */
15749         beforepush: true,
15750          /**
15751          * @event sync
15752          * Fires when the textarea is updated with content from the editor iframe.
15753          * @param {Roo.HtmlEditorCore} this
15754          * @param {String} html
15755          */
15756         sync: true,
15757          /**
15758          * @event push
15759          * Fires when the iframe editor is updated with content from the textarea.
15760          * @param {Roo.HtmlEditorCore} this
15761          * @param {String} html
15762          */
15763         push: true,
15764         
15765         /**
15766          * @event editorevent
15767          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15768          * @param {Roo.HtmlEditorCore} this
15769          */
15770         editorevent: true
15771     });
15772      
15773 };
15774
15775
15776 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
15777
15778
15779      /**
15780      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
15781      */
15782     
15783     owner : false,
15784     
15785      /**
15786      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15787      *                        Roo.resizable.
15788      */
15789     resizable : false,
15790      /**
15791      * @cfg {Number} height (in pixels)
15792      */   
15793     height: 300,
15794    /**
15795      * @cfg {Number} width (in pixels)
15796      */   
15797     width: 500,
15798     
15799     /**
15800      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15801      * 
15802      */
15803     stylesheets: false,
15804     
15805     // id of frame..
15806     frameId: false,
15807     
15808     // private properties
15809     validationEvent : false,
15810     deferHeight: true,
15811     initialized : false,
15812     activated : false,
15813     sourceEditMode : false,
15814     onFocus : Roo.emptyFn,
15815     iframePad:3,
15816     hideMode:'offsets',
15817     
15818     clearUp: true,
15819     
15820      
15821     
15822
15823     /**
15824      * Protected method that will not generally be called directly. It
15825      * is called when the editor initializes the iframe with HTML contents. Override this method if you
15826      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
15827      */
15828     getDocMarkup : function(){
15829         // body styles..
15830         var st = '';
15831         Roo.log(this.stylesheets);
15832         
15833         // inherit styels from page...?? 
15834         if (this.stylesheets === false) {
15835             
15836             Roo.get(document.head).select('style').each(function(node) {
15837                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
15838             });
15839             
15840             Roo.get(document.head).select('link').each(function(node) { 
15841                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
15842             });
15843             
15844         } else if (!this.stylesheets.length) {
15845                 // simple..
15846                 st = '<style type="text/css">' +
15847                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
15848                    '</style>';
15849         } else {
15850             Roo.each(this.stylesheets, function(s) {
15851                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
15852             });
15853             
15854         }
15855         
15856         st +=  '<style type="text/css">' +
15857             'IMG { cursor: pointer } ' +
15858         '</style>';
15859
15860         
15861         return '<html><head>' + st  +
15862             //<style type="text/css">' +
15863             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
15864             //'</style>' +
15865             ' </head><body class="roo-htmleditor-body"></body></html>';
15866     },
15867
15868     // private
15869     onRender : function(ct, position)
15870     {
15871         var _t = this;
15872         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
15873         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
15874         
15875         
15876         this.el.dom.style.border = '0 none';
15877         this.el.dom.setAttribute('tabIndex', -1);
15878         this.el.addClass('x-hidden hide');
15879         
15880         
15881         
15882         if(Roo.isIE){ // fix IE 1px bogus margin
15883             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
15884         }
15885        
15886         
15887         this.frameId = Roo.id();
15888         
15889          
15890         
15891         var iframe = this.owner.wrap.createChild({
15892             tag: 'iframe',
15893             cls: 'form-control', // bootstrap..
15894             id: this.frameId,
15895             name: this.frameId,
15896             frameBorder : 'no',
15897             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
15898         }, this.el
15899         );
15900         
15901         
15902         this.iframe = iframe.dom;
15903
15904          this.assignDocWin();
15905         
15906         this.doc.designMode = 'on';
15907        
15908         this.doc.open();
15909         this.doc.write(this.getDocMarkup());
15910         this.doc.close();
15911
15912         
15913         var task = { // must defer to wait for browser to be ready
15914             run : function(){
15915                 //console.log("run task?" + this.doc.readyState);
15916                 this.assignDocWin();
15917                 if(this.doc.body || this.doc.readyState == 'complete'){
15918                     try {
15919                         this.doc.designMode="on";
15920                     } catch (e) {
15921                         return;
15922                     }
15923                     Roo.TaskMgr.stop(task);
15924                     this.initEditor.defer(10, this);
15925                 }
15926             },
15927             interval : 10,
15928             duration: 10000,
15929             scope: this
15930         };
15931         Roo.TaskMgr.start(task);
15932
15933         
15934          
15935     },
15936
15937     // private
15938     onResize : function(w, h)
15939     {
15940          Roo.log('resize: ' +w + ',' + h );
15941         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
15942         if(!this.iframe){
15943             return;
15944         }
15945         if(typeof w == 'number'){
15946             
15947             this.iframe.style.width = w + 'px';
15948         }
15949         if(typeof h == 'number'){
15950             
15951             this.iframe.style.height = h + 'px';
15952             if(this.doc){
15953                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
15954             }
15955         }
15956         
15957     },
15958
15959     /**
15960      * Toggles the editor between standard and source edit mode.
15961      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15962      */
15963     toggleSourceEdit : function(sourceEditMode){
15964         
15965         this.sourceEditMode = sourceEditMode === true;
15966         
15967         if(this.sourceEditMode){
15968  
15969             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
15970             
15971         }else{
15972             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
15973             //this.iframe.className = '';
15974             this.deferFocus();
15975         }
15976         //this.setSize(this.owner.wrap.getSize());
15977         //this.fireEvent('editmodechange', this, this.sourceEditMode);
15978     },
15979
15980     
15981   
15982
15983     /**
15984      * Protected method that will not generally be called directly. If you need/want
15985      * custom HTML cleanup, this is the method you should override.
15986      * @param {String} html The HTML to be cleaned
15987      * return {String} The cleaned HTML
15988      */
15989     cleanHtml : function(html){
15990         html = String(html);
15991         if(html.length > 5){
15992             if(Roo.isSafari){ // strip safari nonsense
15993                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
15994             }
15995         }
15996         if(html == '&nbsp;'){
15997             html = '';
15998         }
15999         return html;
16000     },
16001
16002     /**
16003      * HTML Editor -> Textarea
16004      * Protected method that will not generally be called directly. Syncs the contents
16005      * of the editor iframe with the textarea.
16006      */
16007     syncValue : function(){
16008         if(this.initialized){
16009             var bd = (this.doc.body || this.doc.documentElement);
16010             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16011             var html = bd.innerHTML;
16012             if(Roo.isSafari){
16013                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16014                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16015                 if(m && m[1]){
16016                     html = '<div style="'+m[0]+'">' + html + '</div>';
16017                 }
16018             }
16019             html = this.cleanHtml(html);
16020             // fix up the special chars.. normaly like back quotes in word...
16021             // however we do not want to do this with chinese..
16022             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16023                 var cc = b.charCodeAt();
16024                 if (
16025                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16026                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16027                     (cc >= 0xf900 && cc < 0xfb00 )
16028                 ) {
16029                         return b;
16030                 }
16031                 return "&#"+cc+";" 
16032             });
16033             if(this.owner.fireEvent('beforesync', this, html) !== false){
16034                 this.el.dom.value = html;
16035                 this.owner.fireEvent('sync', this, html);
16036             }
16037         }
16038     },
16039
16040     /**
16041      * Protected method that will not generally be called directly. Pushes the value of the textarea
16042      * into the iframe editor.
16043      */
16044     pushValue : function(){
16045         if(this.initialized){
16046             var v = this.el.dom.value.trim();
16047             
16048 //            if(v.length < 1){
16049 //                v = '&#160;';
16050 //            }
16051             
16052             if(this.owner.fireEvent('beforepush', this, v) !== false){
16053                 var d = (this.doc.body || this.doc.documentElement);
16054                 d.innerHTML = v;
16055                 this.cleanUpPaste();
16056                 this.el.dom.value = d.innerHTML;
16057                 this.owner.fireEvent('push', this, v);
16058             }
16059         }
16060     },
16061
16062     // private
16063     deferFocus : function(){
16064         this.focus.defer(10, this);
16065     },
16066
16067     // doc'ed in Field
16068     focus : function(){
16069         if(this.win && !this.sourceEditMode){
16070             this.win.focus();
16071         }else{
16072             this.el.focus();
16073         }
16074     },
16075     
16076     assignDocWin: function()
16077     {
16078         var iframe = this.iframe;
16079         
16080          if(Roo.isIE){
16081             this.doc = iframe.contentWindow.document;
16082             this.win = iframe.contentWindow;
16083         } else {
16084             if (!Roo.get(this.frameId)) {
16085                 return;
16086             }
16087             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16088             this.win = Roo.get(this.frameId).dom.contentWindow;
16089         }
16090     },
16091     
16092     // private
16093     initEditor : function(){
16094         //console.log("INIT EDITOR");
16095         this.assignDocWin();
16096         
16097         
16098         
16099         this.doc.designMode="on";
16100         this.doc.open();
16101         this.doc.write(this.getDocMarkup());
16102         this.doc.close();
16103         
16104         var dbody = (this.doc.body || this.doc.documentElement);
16105         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16106         // this copies styles from the containing element into thsi one..
16107         // not sure why we need all of this..
16108         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16109         
16110         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16111         //ss['background-attachment'] = 'fixed'; // w3c
16112         dbody.bgProperties = 'fixed'; // ie
16113         //Roo.DomHelper.applyStyles(dbody, ss);
16114         Roo.EventManager.on(this.doc, {
16115             //'mousedown': this.onEditorEvent,
16116             'mouseup': this.onEditorEvent,
16117             'dblclick': this.onEditorEvent,
16118             'click': this.onEditorEvent,
16119             'keyup': this.onEditorEvent,
16120             buffer:100,
16121             scope: this
16122         });
16123         if(Roo.isGecko){
16124             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16125         }
16126         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16127             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16128         }
16129         this.initialized = true;
16130
16131         this.owner.fireEvent('initialize', this);
16132         this.pushValue();
16133     },
16134
16135     // private
16136     onDestroy : function(){
16137         
16138         
16139         
16140         if(this.rendered){
16141             
16142             //for (var i =0; i < this.toolbars.length;i++) {
16143             //    // fixme - ask toolbars for heights?
16144             //    this.toolbars[i].onDestroy();
16145            // }
16146             
16147             //this.wrap.dom.innerHTML = '';
16148             //this.wrap.remove();
16149         }
16150     },
16151
16152     // private
16153     onFirstFocus : function(){
16154         
16155         this.assignDocWin();
16156         
16157         
16158         this.activated = true;
16159          
16160     
16161         if(Roo.isGecko){ // prevent silly gecko errors
16162             this.win.focus();
16163             var s = this.win.getSelection();
16164             if(!s.focusNode || s.focusNode.nodeType != 3){
16165                 var r = s.getRangeAt(0);
16166                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16167                 r.collapse(true);
16168                 this.deferFocus();
16169             }
16170             try{
16171                 this.execCmd('useCSS', true);
16172                 this.execCmd('styleWithCSS', false);
16173             }catch(e){}
16174         }
16175         this.owner.fireEvent('activate', this);
16176     },
16177
16178     // private
16179     adjustFont: function(btn){
16180         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16181         //if(Roo.isSafari){ // safari
16182         //    adjust *= 2;
16183        // }
16184         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16185         if(Roo.isSafari){ // safari
16186             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16187             v =  (v < 10) ? 10 : v;
16188             v =  (v > 48) ? 48 : v;
16189             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16190             
16191         }
16192         
16193         
16194         v = Math.max(1, v+adjust);
16195         
16196         this.execCmd('FontSize', v  );
16197     },
16198
16199     onEditorEvent : function(e){
16200         this.owner.fireEvent('editorevent', this, e);
16201       //  this.updateToolbar();
16202         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16203     },
16204
16205     insertTag : function(tg)
16206     {
16207         // could be a bit smarter... -> wrap the current selected tRoo..
16208         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16209             
16210             range = this.createRange(this.getSelection());
16211             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16212             wrappingNode.appendChild(range.extractContents());
16213             range.insertNode(wrappingNode);
16214
16215             return;
16216             
16217             
16218             
16219         }
16220         this.execCmd("formatblock",   tg);
16221         
16222     },
16223     
16224     insertText : function(txt)
16225     {
16226         
16227         
16228         var range = this.createRange();
16229         range.deleteContents();
16230                //alert(Sender.getAttribute('label'));
16231                
16232         range.insertNode(this.doc.createTextNode(txt));
16233     } ,
16234     
16235      
16236
16237     /**
16238      * Executes a Midas editor command on the editor document and performs necessary focus and
16239      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16240      * @param {String} cmd The Midas command
16241      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16242      */
16243     relayCmd : function(cmd, value){
16244         this.win.focus();
16245         this.execCmd(cmd, value);
16246         this.owner.fireEvent('editorevent', this);
16247         //this.updateToolbar();
16248         this.owner.deferFocus();
16249     },
16250
16251     /**
16252      * Executes a Midas editor command directly on the editor document.
16253      * For visual commands, you should use {@link #relayCmd} instead.
16254      * <b>This should only be called after the editor is initialized.</b>
16255      * @param {String} cmd The Midas command
16256      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16257      */
16258     execCmd : function(cmd, value){
16259         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16260         this.syncValue();
16261     },
16262  
16263  
16264    
16265     /**
16266      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16267      * to insert tRoo.
16268      * @param {String} text | dom node.. 
16269      */
16270     insertAtCursor : function(text)
16271     {
16272         
16273         
16274         
16275         if(!this.activated){
16276             return;
16277         }
16278         /*
16279         if(Roo.isIE){
16280             this.win.focus();
16281             var r = this.doc.selection.createRange();
16282             if(r){
16283                 r.collapse(true);
16284                 r.pasteHTML(text);
16285                 this.syncValue();
16286                 this.deferFocus();
16287             
16288             }
16289             return;
16290         }
16291         */
16292         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16293             this.win.focus();
16294             
16295             
16296             // from jquery ui (MIT licenced)
16297             var range, node;
16298             var win = this.win;
16299             
16300             if (win.getSelection && win.getSelection().getRangeAt) {
16301                 range = win.getSelection().getRangeAt(0);
16302                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16303                 range.insertNode(node);
16304             } else if (win.document.selection && win.document.selection.createRange) {
16305                 // no firefox support
16306                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16307                 win.document.selection.createRange().pasteHTML(txt);
16308             } else {
16309                 // no firefox support
16310                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16311                 this.execCmd('InsertHTML', txt);
16312             } 
16313             
16314             this.syncValue();
16315             
16316             this.deferFocus();
16317         }
16318     },
16319  // private
16320     mozKeyPress : function(e){
16321         if(e.ctrlKey){
16322             var c = e.getCharCode(), cmd;
16323           
16324             if(c > 0){
16325                 c = String.fromCharCode(c).toLowerCase();
16326                 switch(c){
16327                     case 'b':
16328                         cmd = 'bold';
16329                         break;
16330                     case 'i':
16331                         cmd = 'italic';
16332                         break;
16333                     
16334                     case 'u':
16335                         cmd = 'underline';
16336                         break;
16337                     
16338                     case 'v':
16339                         this.cleanUpPaste.defer(100, this);
16340                         return;
16341                         
16342                 }
16343                 if(cmd){
16344                     this.win.focus();
16345                     this.execCmd(cmd);
16346                     this.deferFocus();
16347                     e.preventDefault();
16348                 }
16349                 
16350             }
16351         }
16352     },
16353
16354     // private
16355     fixKeys : function(){ // load time branching for fastest keydown performance
16356         if(Roo.isIE){
16357             return function(e){
16358                 var k = e.getKey(), r;
16359                 if(k == e.TAB){
16360                     e.stopEvent();
16361                     r = this.doc.selection.createRange();
16362                     if(r){
16363                         r.collapse(true);
16364                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16365                         this.deferFocus();
16366                     }
16367                     return;
16368                 }
16369                 
16370                 if(k == e.ENTER){
16371                     r = this.doc.selection.createRange();
16372                     if(r){
16373                         var target = r.parentElement();
16374                         if(!target || target.tagName.toLowerCase() != 'li'){
16375                             e.stopEvent();
16376                             r.pasteHTML('<br />');
16377                             r.collapse(false);
16378                             r.select();
16379                         }
16380                     }
16381                 }
16382                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16383                     this.cleanUpPaste.defer(100, this);
16384                     return;
16385                 }
16386                 
16387                 
16388             };
16389         }else if(Roo.isOpera){
16390             return function(e){
16391                 var k = e.getKey();
16392                 if(k == e.TAB){
16393                     e.stopEvent();
16394                     this.win.focus();
16395                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16396                     this.deferFocus();
16397                 }
16398                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16399                     this.cleanUpPaste.defer(100, this);
16400                     return;
16401                 }
16402                 
16403             };
16404         }else if(Roo.isSafari){
16405             return function(e){
16406                 var k = e.getKey();
16407                 
16408                 if(k == e.TAB){
16409                     e.stopEvent();
16410                     this.execCmd('InsertText','\t');
16411                     this.deferFocus();
16412                     return;
16413                 }
16414                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16415                     this.cleanUpPaste.defer(100, this);
16416                     return;
16417                 }
16418                 
16419              };
16420         }
16421     }(),
16422     
16423     getAllAncestors: function()
16424     {
16425         var p = this.getSelectedNode();
16426         var a = [];
16427         if (!p) {
16428             a.push(p); // push blank onto stack..
16429             p = this.getParentElement();
16430         }
16431         
16432         
16433         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16434             a.push(p);
16435             p = p.parentNode;
16436         }
16437         a.push(this.doc.body);
16438         return a;
16439     },
16440     lastSel : false,
16441     lastSelNode : false,
16442     
16443     
16444     getSelection : function() 
16445     {
16446         this.assignDocWin();
16447         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16448     },
16449     
16450     getSelectedNode: function() 
16451     {
16452         // this may only work on Gecko!!!
16453         
16454         // should we cache this!!!!
16455         
16456         
16457         
16458          
16459         var range = this.createRange(this.getSelection()).cloneRange();
16460         
16461         if (Roo.isIE) {
16462             var parent = range.parentElement();
16463             while (true) {
16464                 var testRange = range.duplicate();
16465                 testRange.moveToElementText(parent);
16466                 if (testRange.inRange(range)) {
16467                     break;
16468                 }
16469                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16470                     break;
16471                 }
16472                 parent = parent.parentElement;
16473             }
16474             return parent;
16475         }
16476         
16477         // is ancestor a text element.
16478         var ac =  range.commonAncestorContainer;
16479         if (ac.nodeType == 3) {
16480             ac = ac.parentNode;
16481         }
16482         
16483         var ar = ac.childNodes;
16484          
16485         var nodes = [];
16486         var other_nodes = [];
16487         var has_other_nodes = false;
16488         for (var i=0;i<ar.length;i++) {
16489             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16490                 continue;
16491             }
16492             // fullly contained node.
16493             
16494             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
16495                 nodes.push(ar[i]);
16496                 continue;
16497             }
16498             
16499             // probably selected..
16500             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
16501                 other_nodes.push(ar[i]);
16502                 continue;
16503             }
16504             // outer..
16505             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
16506                 continue;
16507             }
16508             
16509             
16510             has_other_nodes = true;
16511         }
16512         if (!nodes.length && other_nodes.length) {
16513             nodes= other_nodes;
16514         }
16515         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
16516             return false;
16517         }
16518         
16519         return nodes[0];
16520     },
16521     createRange: function(sel)
16522     {
16523         // this has strange effects when using with 
16524         // top toolbar - not sure if it's a great idea.
16525         //this.editor.contentWindow.focus();
16526         if (typeof sel != "undefined") {
16527             try {
16528                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
16529             } catch(e) {
16530                 return this.doc.createRange();
16531             }
16532         } else {
16533             return this.doc.createRange();
16534         }
16535     },
16536     getParentElement: function()
16537     {
16538         
16539         this.assignDocWin();
16540         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
16541         
16542         var range = this.createRange(sel);
16543          
16544         try {
16545             var p = range.commonAncestorContainer;
16546             while (p.nodeType == 3) { // text node
16547                 p = p.parentNode;
16548             }
16549             return p;
16550         } catch (e) {
16551             return null;
16552         }
16553     
16554     },
16555     /***
16556      *
16557      * Range intersection.. the hard stuff...
16558      *  '-1' = before
16559      *  '0' = hits..
16560      *  '1' = after.
16561      *         [ -- selected range --- ]
16562      *   [fail]                        [fail]
16563      *
16564      *    basically..
16565      *      if end is before start or  hits it. fail.
16566      *      if start is after end or hits it fail.
16567      *
16568      *   if either hits (but other is outside. - then it's not 
16569      *   
16570      *    
16571      **/
16572     
16573     
16574     // @see http://www.thismuchiknow.co.uk/?p=64.
16575     rangeIntersectsNode : function(range, node)
16576     {
16577         var nodeRange = node.ownerDocument.createRange();
16578         try {
16579             nodeRange.selectNode(node);
16580         } catch (e) {
16581             nodeRange.selectNodeContents(node);
16582         }
16583     
16584         var rangeStartRange = range.cloneRange();
16585         rangeStartRange.collapse(true);
16586     
16587         var rangeEndRange = range.cloneRange();
16588         rangeEndRange.collapse(false);
16589     
16590         var nodeStartRange = nodeRange.cloneRange();
16591         nodeStartRange.collapse(true);
16592     
16593         var nodeEndRange = nodeRange.cloneRange();
16594         nodeEndRange.collapse(false);
16595     
16596         return rangeStartRange.compareBoundaryPoints(
16597                  Range.START_TO_START, nodeEndRange) == -1 &&
16598                rangeEndRange.compareBoundaryPoints(
16599                  Range.START_TO_START, nodeStartRange) == 1;
16600         
16601          
16602     },
16603     rangeCompareNode : function(range, node)
16604     {
16605         var nodeRange = node.ownerDocument.createRange();
16606         try {
16607             nodeRange.selectNode(node);
16608         } catch (e) {
16609             nodeRange.selectNodeContents(node);
16610         }
16611         
16612         
16613         range.collapse(true);
16614     
16615         nodeRange.collapse(true);
16616      
16617         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
16618         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
16619          
16620         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
16621         
16622         var nodeIsBefore   =  ss == 1;
16623         var nodeIsAfter    = ee == -1;
16624         
16625         if (nodeIsBefore && nodeIsAfter)
16626             return 0; // outer
16627         if (!nodeIsBefore && nodeIsAfter)
16628             return 1; //right trailed.
16629         
16630         if (nodeIsBefore && !nodeIsAfter)
16631             return 2;  // left trailed.
16632         // fully contined.
16633         return 3;
16634     },
16635
16636     // private? - in a new class?
16637     cleanUpPaste :  function()
16638     {
16639         // cleans up the whole document..
16640         Roo.log('cleanuppaste');
16641         
16642         this.cleanUpChildren(this.doc.body);
16643         var clean = this.cleanWordChars(this.doc.body.innerHTML);
16644         if (clean != this.doc.body.innerHTML) {
16645             this.doc.body.innerHTML = clean;
16646         }
16647         
16648     },
16649     
16650     cleanWordChars : function(input) {// change the chars to hex code
16651         var he = Roo.HtmlEditorCore;
16652         
16653         var output = input;
16654         Roo.each(he.swapCodes, function(sw) { 
16655             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
16656             
16657             output = output.replace(swapper, sw[1]);
16658         });
16659         
16660         return output;
16661     },
16662     
16663     
16664     cleanUpChildren : function (n)
16665     {
16666         if (!n.childNodes.length) {
16667             return;
16668         }
16669         for (var i = n.childNodes.length-1; i > -1 ; i--) {
16670            this.cleanUpChild(n.childNodes[i]);
16671         }
16672     },
16673     
16674     
16675         
16676     
16677     cleanUpChild : function (node)
16678     {
16679         var ed = this;
16680         //console.log(node);
16681         if (node.nodeName == "#text") {
16682             // clean up silly Windows -- stuff?
16683             return; 
16684         }
16685         if (node.nodeName == "#comment") {
16686             node.parentNode.removeChild(node);
16687             // clean up silly Windows -- stuff?
16688             return; 
16689         }
16690         
16691         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
16692             // remove node.
16693             node.parentNode.removeChild(node);
16694             return;
16695             
16696         }
16697         
16698         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
16699         
16700         // remove <a name=....> as rendering on yahoo mailer is borked with this.
16701         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
16702         
16703         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
16704         //    remove_keep_children = true;
16705         //}
16706         
16707         if (remove_keep_children) {
16708             this.cleanUpChildren(node);
16709             // inserts everything just before this node...
16710             while (node.childNodes.length) {
16711                 var cn = node.childNodes[0];
16712                 node.removeChild(cn);
16713                 node.parentNode.insertBefore(cn, node);
16714             }
16715             node.parentNode.removeChild(node);
16716             return;
16717         }
16718         
16719         if (!node.attributes || !node.attributes.length) {
16720             this.cleanUpChildren(node);
16721             return;
16722         }
16723         
16724         function cleanAttr(n,v)
16725         {
16726             
16727             if (v.match(/^\./) || v.match(/^\//)) {
16728                 return;
16729             }
16730             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
16731                 return;
16732             }
16733             if (v.match(/^#/)) {
16734                 return;
16735             }
16736 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
16737             node.removeAttribute(n);
16738             
16739         }
16740         
16741         function cleanStyle(n,v)
16742         {
16743             if (v.match(/expression/)) { //XSS?? should we even bother..
16744                 node.removeAttribute(n);
16745                 return;
16746             }
16747             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
16748             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
16749             
16750             
16751             var parts = v.split(/;/);
16752             var clean = [];
16753             
16754             Roo.each(parts, function(p) {
16755                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
16756                 if (!p.length) {
16757                     return true;
16758                 }
16759                 var l = p.split(':').shift().replace(/\s+/g,'');
16760                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
16761                 
16762                 if ( cblack.indexOf(l) > -1) {
16763 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
16764                     //node.removeAttribute(n);
16765                     return true;
16766                 }
16767                 //Roo.log()
16768                 // only allow 'c whitelisted system attributes'
16769                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
16770 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
16771                     //node.removeAttribute(n);
16772                     return true;
16773                 }
16774                 
16775                 
16776                  
16777                 
16778                 clean.push(p);
16779                 return true;
16780             });
16781             if (clean.length) { 
16782                 node.setAttribute(n, clean.join(';'));
16783             } else {
16784                 node.removeAttribute(n);
16785             }
16786             
16787         }
16788         
16789         
16790         for (var i = node.attributes.length-1; i > -1 ; i--) {
16791             var a = node.attributes[i];
16792             //console.log(a);
16793             
16794             if (a.name.toLowerCase().substr(0,2)=='on')  {
16795                 node.removeAttribute(a.name);
16796                 continue;
16797             }
16798             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
16799                 node.removeAttribute(a.name);
16800                 continue;
16801             }
16802             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
16803                 cleanAttr(a.name,a.value); // fixme..
16804                 continue;
16805             }
16806             if (a.name == 'style') {
16807                 cleanStyle(a.name,a.value);
16808                 continue;
16809             }
16810             /// clean up MS crap..
16811             // tecnically this should be a list of valid class'es..
16812             
16813             
16814             if (a.name == 'class') {
16815                 if (a.value.match(/^Mso/)) {
16816                     node.className = '';
16817                 }
16818                 
16819                 if (a.value.match(/body/)) {
16820                     node.className = '';
16821                 }
16822                 continue;
16823             }
16824             
16825             // style cleanup!?
16826             // class cleanup?
16827             
16828         }
16829         
16830         
16831         this.cleanUpChildren(node);
16832         
16833         
16834     },
16835     /**
16836      * Clean up MS wordisms...
16837      */
16838     cleanWord : function(node)
16839     {
16840         var _t = this;
16841         var cleanWordChildren = function()
16842         {
16843             if (!node.childNodes.length) {
16844                 return;
16845             }
16846             for (var i = node.childNodes.length-1; i > -1 ; i--) {
16847                _t.cleanWord(node.childNodes[i]);
16848             }
16849         }
16850         
16851         
16852         if (!node) {
16853             this.cleanWord(this.doc.body);
16854             return;
16855         }
16856         if (node.nodeName == "#text") {
16857             // clean up silly Windows -- stuff?
16858             return; 
16859         }
16860         if (node.nodeName == "#comment") {
16861             node.parentNode.removeChild(node);
16862             // clean up silly Windows -- stuff?
16863             return; 
16864         }
16865         
16866         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
16867             node.parentNode.removeChild(node);
16868             return;
16869         }
16870         
16871         // remove - but keep children..
16872         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
16873             while (node.childNodes.length) {
16874                 var cn = node.childNodes[0];
16875                 node.removeChild(cn);
16876                 node.parentNode.insertBefore(cn, node);
16877             }
16878             node.parentNode.removeChild(node);
16879             cleanWordChildren();
16880             return;
16881         }
16882         // clean styles
16883         if (node.className.length) {
16884             
16885             var cn = node.className.split(/\W+/);
16886             var cna = [];
16887             Roo.each(cn, function(cls) {
16888                 if (cls.match(/Mso[a-zA-Z]+/)) {
16889                     return;
16890                 }
16891                 cna.push(cls);
16892             });
16893             node.className = cna.length ? cna.join(' ') : '';
16894             if (!cna.length) {
16895                 node.removeAttribute("class");
16896             }
16897         }
16898         
16899         if (node.hasAttribute("lang")) {
16900             node.removeAttribute("lang");
16901         }
16902         
16903         if (node.hasAttribute("style")) {
16904             
16905             var styles = node.getAttribute("style").split(";");
16906             var nstyle = [];
16907             Roo.each(styles, function(s) {
16908                 if (!s.match(/:/)) {
16909                     return;
16910                 }
16911                 var kv = s.split(":");
16912                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
16913                     return;
16914                 }
16915                 // what ever is left... we allow.
16916                 nstyle.push(s);
16917             });
16918             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
16919             if (!nstyle.length) {
16920                 node.removeAttribute('style');
16921             }
16922         }
16923         
16924         cleanWordChildren();
16925         
16926         
16927     },
16928     domToHTML : function(currentElement, depth, nopadtext) {
16929         
16930             depth = depth || 0;
16931             nopadtext = nopadtext || false;
16932         
16933             if (!currentElement) {
16934                 return this.domToHTML(this.doc.body);
16935             }
16936             
16937             //Roo.log(currentElement);
16938             var j;
16939             var allText = false;
16940             var nodeName = currentElement.nodeName;
16941             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
16942             
16943             if  (nodeName == '#text') {
16944                 return currentElement.nodeValue;
16945             }
16946             
16947             
16948             var ret = '';
16949             if (nodeName != 'BODY') {
16950                  
16951                 var i = 0;
16952                 // Prints the node tagName, such as <A>, <IMG>, etc
16953                 if (tagName) {
16954                     var attr = [];
16955                     for(i = 0; i < currentElement.attributes.length;i++) {
16956                         // quoting?
16957                         var aname = currentElement.attributes.item(i).name;
16958                         if (!currentElement.attributes.item(i).value.length) {
16959                             continue;
16960                         }
16961                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
16962                     }
16963                     
16964                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
16965                 } 
16966                 else {
16967                     
16968                     // eack
16969                 }
16970             } else {
16971                 tagName = false;
16972             }
16973             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
16974                 return ret;
16975             }
16976             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
16977                 nopadtext = true;
16978             }
16979             
16980             
16981             // Traverse the tree
16982             i = 0;
16983             var currentElementChild = currentElement.childNodes.item(i);
16984             var allText = true;
16985             var innerHTML  = '';
16986             lastnode = '';
16987             while (currentElementChild) {
16988                 // Formatting code (indent the tree so it looks nice on the screen)
16989                 var nopad = nopadtext;
16990                 if (lastnode == 'SPAN') {
16991                     nopad  = true;
16992                 }
16993                 // text
16994                 if  (currentElementChild.nodeName == '#text') {
16995                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
16996                     if (!nopad && toadd.length > 80) {
16997                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
16998                     }
16999                     innerHTML  += toadd;
17000                     
17001                     i++;
17002                     currentElementChild = currentElement.childNodes.item(i);
17003                     lastNode = '';
17004                     continue;
17005                 }
17006                 allText = false;
17007                 
17008                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17009                     
17010                 // Recursively traverse the tree structure of the child node
17011                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17012                 lastnode = currentElementChild.nodeName;
17013                 i++;
17014                 currentElementChild=currentElement.childNodes.item(i);
17015             }
17016             
17017             ret += innerHTML;
17018             
17019             if (!allText) {
17020                     // The remaining code is mostly for formatting the tree
17021                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17022             }
17023             
17024             
17025             if (tagName) {
17026                 ret+= "</"+tagName+">";
17027             }
17028             return ret;
17029             
17030         }
17031     
17032     // hide stuff that is not compatible
17033     /**
17034      * @event blur
17035      * @hide
17036      */
17037     /**
17038      * @event change
17039      * @hide
17040      */
17041     /**
17042      * @event focus
17043      * @hide
17044      */
17045     /**
17046      * @event specialkey
17047      * @hide
17048      */
17049     /**
17050      * @cfg {String} fieldClass @hide
17051      */
17052     /**
17053      * @cfg {String} focusClass @hide
17054      */
17055     /**
17056      * @cfg {String} autoCreate @hide
17057      */
17058     /**
17059      * @cfg {String} inputType @hide
17060      */
17061     /**
17062      * @cfg {String} invalidClass @hide
17063      */
17064     /**
17065      * @cfg {String} invalidText @hide
17066      */
17067     /**
17068      * @cfg {String} msgFx @hide
17069      */
17070     /**
17071      * @cfg {String} validateOnBlur @hide
17072      */
17073 });
17074
17075 Roo.HtmlEditorCore.white = [
17076         'area', 'br', 'img', 'input', 'hr', 'wbr',
17077         
17078        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17079        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17080        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17081        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17082        'table',   'ul',         'xmp', 
17083        
17084        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17085       'thead',   'tr', 
17086      
17087       'dir', 'menu', 'ol', 'ul', 'dl',
17088        
17089       'embed',  'object'
17090 ];
17091
17092
17093 Roo.HtmlEditorCore.black = [
17094     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17095         'applet', // 
17096         'base',   'basefont', 'bgsound', 'blink',  'body', 
17097         'frame',  'frameset', 'head',    'html',   'ilayer', 
17098         'iframe', 'layer',  'link',     'meta',    'object',   
17099         'script', 'style' ,'title',  'xml' // clean later..
17100 ];
17101 Roo.HtmlEditorCore.clean = [
17102     'script', 'style', 'title', 'xml'
17103 ];
17104 Roo.HtmlEditorCore.remove = [
17105     'font'
17106 ];
17107 // attributes..
17108
17109 Roo.HtmlEditorCore.ablack = [
17110     'on'
17111 ];
17112     
17113 Roo.HtmlEditorCore.aclean = [ 
17114     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17115 ];
17116
17117 // protocols..
17118 Roo.HtmlEditorCore.pwhite= [
17119         'http',  'https',  'mailto'
17120 ];
17121
17122 // white listed style attributes.
17123 Roo.HtmlEditorCore.cwhite= [
17124       //  'text-align', /// default is to allow most things..
17125       
17126          
17127 //        'font-size'//??
17128 ];
17129
17130 // black listed style attributes.
17131 Roo.HtmlEditorCore.cblack= [
17132       //  'font-size' -- this can be set by the project 
17133 ];
17134
17135
17136 Roo.HtmlEditorCore.swapCodes   =[ 
17137     [    8211, "--" ], 
17138     [    8212, "--" ], 
17139     [    8216,  "'" ],  
17140     [    8217, "'" ],  
17141     [    8220, '"' ],  
17142     [    8221, '"' ],  
17143     [    8226, "*" ],  
17144     [    8230, "..." ]
17145 ]; 
17146
17147     /*
17148  * - LGPL
17149  *
17150  * HtmlEditor
17151  * 
17152  */
17153
17154 /**
17155  * @class Roo.bootstrap.HtmlEditor
17156  * @extends Roo.bootstrap.TextArea
17157  * Bootstrap HtmlEditor class
17158
17159  * @constructor
17160  * Create a new HtmlEditor
17161  * @param {Object} config The config object
17162  */
17163
17164 Roo.bootstrap.HtmlEditor = function(config){
17165     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17166     if (!this.toolbars) {
17167         this.toolbars = [];
17168     }
17169     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17170     this.addEvents({
17171             /**
17172              * @event initialize
17173              * Fires when the editor is fully initialized (including the iframe)
17174              * @param {HtmlEditor} this
17175              */
17176             initialize: true,
17177             /**
17178              * @event activate
17179              * Fires when the editor is first receives the focus. Any insertion must wait
17180              * until after this event.
17181              * @param {HtmlEditor} this
17182              */
17183             activate: true,
17184              /**
17185              * @event beforesync
17186              * Fires before the textarea is updated with content from the editor iframe. Return false
17187              * to cancel the sync.
17188              * @param {HtmlEditor} this
17189              * @param {String} html
17190              */
17191             beforesync: true,
17192              /**
17193              * @event beforepush
17194              * Fires before the iframe editor is updated with content from the textarea. Return false
17195              * to cancel the push.
17196              * @param {HtmlEditor} this
17197              * @param {String} html
17198              */
17199             beforepush: true,
17200              /**
17201              * @event sync
17202              * Fires when the textarea is updated with content from the editor iframe.
17203              * @param {HtmlEditor} this
17204              * @param {String} html
17205              */
17206             sync: true,
17207              /**
17208              * @event push
17209              * Fires when the iframe editor is updated with content from the textarea.
17210              * @param {HtmlEditor} this
17211              * @param {String} html
17212              */
17213             push: true,
17214              /**
17215              * @event editmodechange
17216              * Fires when the editor switches edit modes
17217              * @param {HtmlEditor} this
17218              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17219              */
17220             editmodechange: true,
17221             /**
17222              * @event editorevent
17223              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17224              * @param {HtmlEditor} this
17225              */
17226             editorevent: true,
17227             /**
17228              * @event firstfocus
17229              * Fires when on first focus - needed by toolbars..
17230              * @param {HtmlEditor} this
17231              */
17232             firstfocus: true,
17233             /**
17234              * @event autosave
17235              * Auto save the htmlEditor value as a file into Events
17236              * @param {HtmlEditor} this
17237              */
17238             autosave: true,
17239             /**
17240              * @event savedpreview
17241              * preview the saved version of htmlEditor
17242              * @param {HtmlEditor} this
17243              */
17244             savedpreview: true
17245         });
17246 };
17247
17248
17249 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17250     
17251     
17252       /**
17253      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17254      */
17255     toolbars : false,
17256    
17257      /**
17258      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17259      *                        Roo.resizable.
17260      */
17261     resizable : false,
17262      /**
17263      * @cfg {Number} height (in pixels)
17264      */   
17265     height: 300,
17266    /**
17267      * @cfg {Number} width (in pixels)
17268      */   
17269     width: false,
17270     
17271     /**
17272      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17273      * 
17274      */
17275     stylesheets: false,
17276     
17277     // id of frame..
17278     frameId: false,
17279     
17280     // private properties
17281     validationEvent : false,
17282     deferHeight: true,
17283     initialized : false,
17284     activated : false,
17285     
17286     onFocus : Roo.emptyFn,
17287     iframePad:3,
17288     hideMode:'offsets',
17289     
17290     
17291     tbContainer : false,
17292     
17293     toolbarContainer :function() {
17294         return this.wrap.select('.x-html-editor-tb',true).first();
17295     },
17296
17297     /**
17298      * Protected method that will not generally be called directly. It
17299      * is called when the editor creates its toolbar. Override this method if you need to
17300      * add custom toolbar buttons.
17301      * @param {HtmlEditor} editor
17302      */
17303     createToolbar : function(){
17304         
17305         Roo.log("create toolbars");
17306         
17307         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17308         this.toolbars[0].render(this.toolbarContainer());
17309         
17310         return;
17311         
17312 //        if (!editor.toolbars || !editor.toolbars.length) {
17313 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17314 //        }
17315 //        
17316 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17317 //            editor.toolbars[i] = Roo.factory(
17318 //                    typeof(editor.toolbars[i]) == 'string' ?
17319 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17320 //                Roo.bootstrap.HtmlEditor);
17321 //            editor.toolbars[i].init(editor);
17322 //        }
17323     },
17324
17325      
17326     // private
17327     onRender : function(ct, position)
17328     {
17329        // Roo.log("Call onRender: " + this.xtype);
17330         var _t = this;
17331         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17332       
17333         this.wrap = this.inputEl().wrap({
17334             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17335         });
17336         
17337         this.editorcore.onRender(ct, position);
17338          
17339         if (this.resizable) {
17340             this.resizeEl = new Roo.Resizable(this.wrap, {
17341                 pinned : true,
17342                 wrap: true,
17343                 dynamic : true,
17344                 minHeight : this.height,
17345                 height: this.height,
17346                 handles : this.resizable,
17347                 width: this.width,
17348                 listeners : {
17349                     resize : function(r, w, h) {
17350                         _t.onResize(w,h); // -something
17351                     }
17352                 }
17353             });
17354             
17355         }
17356         this.createToolbar(this);
17357        
17358         
17359         if(!this.width && this.resizable){
17360             this.setSize(this.wrap.getSize());
17361         }
17362         if (this.resizeEl) {
17363             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17364             // should trigger onReize..
17365         }
17366         
17367     },
17368
17369     // private
17370     onResize : function(w, h)
17371     {
17372         Roo.log('resize: ' +w + ',' + h );
17373         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17374         var ew = false;
17375         var eh = false;
17376         
17377         if(this.inputEl() ){
17378             if(typeof w == 'number'){
17379                 var aw = w - this.wrap.getFrameWidth('lr');
17380                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17381                 ew = aw;
17382             }
17383             if(typeof h == 'number'){
17384                  var tbh = -11;  // fixme it needs to tool bar size!
17385                 for (var i =0; i < this.toolbars.length;i++) {
17386                     // fixme - ask toolbars for heights?
17387                     tbh += this.toolbars[i].el.getHeight();
17388                     //if (this.toolbars[i].footer) {
17389                     //    tbh += this.toolbars[i].footer.el.getHeight();
17390                     //}
17391                 }
17392               
17393                 
17394                 
17395                 
17396                 
17397                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17398                 ah -= 5; // knock a few pixes off for look..
17399                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17400                 var eh = ah;
17401             }
17402         }
17403         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17404         this.editorcore.onResize(ew,eh);
17405         
17406     },
17407
17408     /**
17409      * Toggles the editor between standard and source edit mode.
17410      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17411      */
17412     toggleSourceEdit : function(sourceEditMode)
17413     {
17414         this.editorcore.toggleSourceEdit(sourceEditMode);
17415         
17416         if(this.editorcore.sourceEditMode){
17417             Roo.log('editor - showing textarea');
17418             
17419 //            Roo.log('in');
17420 //            Roo.log(this.syncValue());
17421             this.syncValue();
17422             this.inputEl().removeClass(['hide', 'x-hidden']);
17423             this.inputEl().dom.removeAttribute('tabIndex');
17424             this.inputEl().focus();
17425         }else{
17426             Roo.log('editor - hiding textarea');
17427 //            Roo.log('out')
17428 //            Roo.log(this.pushValue()); 
17429             this.pushValue();
17430             
17431             this.inputEl().addClass(['hide', 'x-hidden']);
17432             this.inputEl().dom.setAttribute('tabIndex', -1);
17433             //this.deferFocus();
17434         }
17435          
17436         if(this.resizable){
17437             this.setSize(this.wrap.getSize());
17438         }
17439         
17440         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17441     },
17442  
17443     // private (for BoxComponent)
17444     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17445
17446     // private (for BoxComponent)
17447     getResizeEl : function(){
17448         return this.wrap;
17449     },
17450
17451     // private (for BoxComponent)
17452     getPositionEl : function(){
17453         return this.wrap;
17454     },
17455
17456     // private
17457     initEvents : function(){
17458         this.originalValue = this.getValue();
17459     },
17460
17461 //    /**
17462 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17463 //     * @method
17464 //     */
17465 //    markInvalid : Roo.emptyFn,
17466 //    /**
17467 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17468 //     * @method
17469 //     */
17470 //    clearInvalid : Roo.emptyFn,
17471
17472     setValue : function(v){
17473         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17474         this.editorcore.pushValue();
17475     },
17476
17477      
17478     // private
17479     deferFocus : function(){
17480         this.focus.defer(10, this);
17481     },
17482
17483     // doc'ed in Field
17484     focus : function(){
17485         this.editorcore.focus();
17486         
17487     },
17488       
17489
17490     // private
17491     onDestroy : function(){
17492         
17493         
17494         
17495         if(this.rendered){
17496             
17497             for (var i =0; i < this.toolbars.length;i++) {
17498                 // fixme - ask toolbars for heights?
17499                 this.toolbars[i].onDestroy();
17500             }
17501             
17502             this.wrap.dom.innerHTML = '';
17503             this.wrap.remove();
17504         }
17505     },
17506
17507     // private
17508     onFirstFocus : function(){
17509         //Roo.log("onFirstFocus");
17510         this.editorcore.onFirstFocus();
17511          for (var i =0; i < this.toolbars.length;i++) {
17512             this.toolbars[i].onFirstFocus();
17513         }
17514         
17515     },
17516     
17517     // private
17518     syncValue : function()
17519     {   
17520         this.editorcore.syncValue();
17521     },
17522     
17523     pushValue : function()
17524     {   
17525         this.editorcore.pushValue();
17526     }
17527      
17528     
17529     // hide stuff that is not compatible
17530     /**
17531      * @event blur
17532      * @hide
17533      */
17534     /**
17535      * @event change
17536      * @hide
17537      */
17538     /**
17539      * @event focus
17540      * @hide
17541      */
17542     /**
17543      * @event specialkey
17544      * @hide
17545      */
17546     /**
17547      * @cfg {String} fieldClass @hide
17548      */
17549     /**
17550      * @cfg {String} focusClass @hide
17551      */
17552     /**
17553      * @cfg {String} autoCreate @hide
17554      */
17555     /**
17556      * @cfg {String} inputType @hide
17557      */
17558     /**
17559      * @cfg {String} invalidClass @hide
17560      */
17561     /**
17562      * @cfg {String} invalidText @hide
17563      */
17564     /**
17565      * @cfg {String} msgFx @hide
17566      */
17567     /**
17568      * @cfg {String} validateOnBlur @hide
17569      */
17570 });
17571  
17572     
17573    
17574    
17575    
17576       
17577 Roo.namespace('Roo.bootstrap.htmleditor');
17578 /**
17579  * @class Roo.bootstrap.HtmlEditorToolbar1
17580  * Basic Toolbar
17581  * 
17582  * Usage:
17583  *
17584  new Roo.bootstrap.HtmlEditor({
17585     ....
17586     toolbars : [
17587         new Roo.bootstrap.HtmlEditorToolbar1({
17588             disable : { fonts: 1 , format: 1, ..., ... , ...],
17589             btns : [ .... ]
17590         })
17591     }
17592      
17593  * 
17594  * @cfg {Object} disable List of elements to disable..
17595  * @cfg {Array} btns List of additional buttons.
17596  * 
17597  * 
17598  * NEEDS Extra CSS? 
17599  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
17600  */
17601  
17602 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
17603 {
17604     
17605     Roo.apply(this, config);
17606     
17607     // default disabled, based on 'good practice'..
17608     this.disable = this.disable || {};
17609     Roo.applyIf(this.disable, {
17610         fontSize : true,
17611         colors : true,
17612         specialElements : true
17613     });
17614     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
17615     
17616     this.editor = config.editor;
17617     this.editorcore = config.editor.editorcore;
17618     
17619     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
17620     
17621     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
17622     // dont call parent... till later.
17623 }
17624 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
17625      
17626     bar : true,
17627     
17628     editor : false,
17629     editorcore : false,
17630     
17631     
17632     formats : [
17633         "p" ,  
17634         "h1","h2","h3","h4","h5","h6", 
17635         "pre", "code", 
17636         "abbr", "acronym", "address", "cite", "samp", "var",
17637         'div','span'
17638     ],
17639     
17640     onRender : function(ct, position)
17641     {
17642        // Roo.log("Call onRender: " + this.xtype);
17643         
17644        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
17645        Roo.log(this.el);
17646        this.el.dom.style.marginBottom = '0';
17647        var _this = this;
17648        var editorcore = this.editorcore;
17649        var editor= this.editor;
17650        
17651        var children = [];
17652        var btn = function(id,cmd , toggle, handler){
17653        
17654             var  event = toggle ? 'toggle' : 'click';
17655        
17656             var a = {
17657                 size : 'sm',
17658                 xtype: 'Button',
17659                 xns: Roo.bootstrap,
17660                 glyphicon : id,
17661                 cmd : id || cmd,
17662                 enableToggle:toggle !== false,
17663                 //html : 'submit'
17664                 pressed : toggle ? false : null,
17665                 listeners : {}
17666             }
17667             a.listeners[toggle ? 'toggle' : 'click'] = function() {
17668                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
17669             }
17670             children.push(a);
17671             return a;
17672        }
17673         
17674         var style = {
17675                 xtype: 'Button',
17676                 size : 'sm',
17677                 xns: Roo.bootstrap,
17678                 glyphicon : 'font',
17679                 //html : 'submit'
17680                 menu : {
17681                     xtype: 'Menu',
17682                     xns: Roo.bootstrap,
17683                     items:  []
17684                 }
17685         };
17686         Roo.each(this.formats, function(f) {
17687             style.menu.items.push({
17688                 xtype :'MenuItem',
17689                 xns: Roo.bootstrap,
17690                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
17691                 tagname : f,
17692                 listeners : {
17693                     click : function()
17694                     {
17695                         editorcore.insertTag(this.tagname);
17696                         editor.focus();
17697                     }
17698                 }
17699                 
17700             });
17701         });
17702          children.push(style);   
17703             
17704             
17705         btn('bold',false,true);
17706         btn('italic',false,true);
17707         btn('align-left', 'justifyleft',true);
17708         btn('align-center', 'justifycenter',true);
17709         btn('align-right' , 'justifyright',true);
17710         btn('link', false, false, function(btn) {
17711             //Roo.log("create link?");
17712             var url = prompt(this.createLinkText, this.defaultLinkValue);
17713             if(url && url != 'http:/'+'/'){
17714                 this.editorcore.relayCmd('createlink', url);
17715             }
17716         }),
17717         btn('list','insertunorderedlist',true);
17718         btn('pencil', false,true, function(btn){
17719                 Roo.log(this);
17720                 
17721                 this.toggleSourceEdit(btn.pressed);
17722         });
17723         /*
17724         var cog = {
17725                 xtype: 'Button',
17726                 size : 'sm',
17727                 xns: Roo.bootstrap,
17728                 glyphicon : 'cog',
17729                 //html : 'submit'
17730                 menu : {
17731                     xtype: 'Menu',
17732                     xns: Roo.bootstrap,
17733                     items:  []
17734                 }
17735         };
17736         
17737         cog.menu.items.push({
17738             xtype :'MenuItem',
17739             xns: Roo.bootstrap,
17740             html : Clean styles,
17741             tagname : f,
17742             listeners : {
17743                 click : function()
17744                 {
17745                     editorcore.insertTag(this.tagname);
17746                     editor.focus();
17747                 }
17748             }
17749             
17750         });
17751        */
17752         
17753          
17754        this.xtype = 'NavSimplebar';
17755         
17756         for(var i=0;i< children.length;i++) {
17757             
17758             this.buttons.add(this.addxtypeChild(children[i]));
17759             
17760         }
17761         
17762         editor.on('editorevent', this.updateToolbar, this);
17763     },
17764     onBtnClick : function(id)
17765     {
17766        this.editorcore.relayCmd(id);
17767        this.editorcore.focus();
17768     },
17769     
17770     /**
17771      * Protected method that will not generally be called directly. It triggers
17772      * a toolbar update by reading the markup state of the current selection in the editor.
17773      */
17774     updateToolbar: function(){
17775
17776         if(!this.editorcore.activated){
17777             this.editor.onFirstFocus(); // is this neeed?
17778             return;
17779         }
17780
17781         var btns = this.buttons; 
17782         var doc = this.editorcore.doc;
17783         btns.get('bold').setActive(doc.queryCommandState('bold'));
17784         btns.get('italic').setActive(doc.queryCommandState('italic'));
17785         //btns.get('underline').setActive(doc.queryCommandState('underline'));
17786         
17787         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
17788         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
17789         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
17790         
17791         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
17792         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
17793          /*
17794         
17795         var ans = this.editorcore.getAllAncestors();
17796         if (this.formatCombo) {
17797             
17798             
17799             var store = this.formatCombo.store;
17800             this.formatCombo.setValue("");
17801             for (var i =0; i < ans.length;i++) {
17802                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
17803                     // select it..
17804                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
17805                     break;
17806                 }
17807             }
17808         }
17809         
17810         
17811         
17812         // hides menus... - so this cant be on a menu...
17813         Roo.bootstrap.MenuMgr.hideAll();
17814         */
17815         Roo.bootstrap.MenuMgr.hideAll();
17816         //this.editorsyncValue();
17817     },
17818     onFirstFocus: function() {
17819         this.buttons.each(function(item){
17820            item.enable();
17821         });
17822     },
17823     toggleSourceEdit : function(sourceEditMode){
17824         
17825           
17826         if(sourceEditMode){
17827             Roo.log("disabling buttons");
17828            this.buttons.each( function(item){
17829                 if(item.cmd != 'pencil'){
17830                     item.disable();
17831                 }
17832             });
17833           
17834         }else{
17835             Roo.log("enabling buttons");
17836             if(this.editorcore.initialized){
17837                 this.buttons.each( function(item){
17838                     item.enable();
17839                 });
17840             }
17841             
17842         }
17843         Roo.log("calling toggole on editor");
17844         // tell the editor that it's been pressed..
17845         this.editor.toggleSourceEdit(sourceEditMode);
17846        
17847     }
17848 });
17849
17850
17851
17852
17853
17854 /**
17855  * @class Roo.bootstrap.Table.AbstractSelectionModel
17856  * @extends Roo.util.Observable
17857  * Abstract base class for grid SelectionModels.  It provides the interface that should be
17858  * implemented by descendant classes.  This class should not be directly instantiated.
17859  * @constructor
17860  */
17861 Roo.bootstrap.Table.AbstractSelectionModel = function(){
17862     this.locked = false;
17863     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
17864 };
17865
17866
17867 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
17868     /** @ignore Called by the grid automatically. Do not call directly. */
17869     init : function(grid){
17870         this.grid = grid;
17871         this.initEvents();
17872     },
17873
17874     /**
17875      * Locks the selections.
17876      */
17877     lock : function(){
17878         this.locked = true;
17879     },
17880
17881     /**
17882      * Unlocks the selections.
17883      */
17884     unlock : function(){
17885         this.locked = false;
17886     },
17887
17888     /**
17889      * Returns true if the selections are locked.
17890      * @return {Boolean}
17891      */
17892     isLocked : function(){
17893         return this.locked;
17894     }
17895 });
17896 /**
17897  * @extends Roo.bootstrap.Table.AbstractSelectionModel
17898  * @class Roo.bootstrap.Table.RowSelectionModel
17899  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17900  * It supports multiple selections and keyboard selection/navigation. 
17901  * @constructor
17902  * @param {Object} config
17903  */
17904
17905 Roo.bootstrap.Table.RowSelectionModel = function(config){
17906     Roo.apply(this, config);
17907     this.selections = new Roo.util.MixedCollection(false, function(o){
17908         return o.id;
17909     });
17910
17911     this.last = false;
17912     this.lastActive = false;
17913
17914     this.addEvents({
17915         /**
17916              * @event selectionchange
17917              * Fires when the selection changes
17918              * @param {SelectionModel} this
17919              */
17920             "selectionchange" : true,
17921         /**
17922              * @event afterselectionchange
17923              * Fires after the selection changes (eg. by key press or clicking)
17924              * @param {SelectionModel} this
17925              */
17926             "afterselectionchange" : true,
17927         /**
17928              * @event beforerowselect
17929              * Fires when a row is selected being selected, return false to cancel.
17930              * @param {SelectionModel} this
17931              * @param {Number} rowIndex The selected index
17932              * @param {Boolean} keepExisting False if other selections will be cleared
17933              */
17934             "beforerowselect" : true,
17935         /**
17936              * @event rowselect
17937              * Fires when a row is selected.
17938              * @param {SelectionModel} this
17939              * @param {Number} rowIndex The selected index
17940              * @param {Roo.data.Record} r The record
17941              */
17942             "rowselect" : true,
17943         /**
17944              * @event rowdeselect
17945              * Fires when a row is deselected.
17946              * @param {SelectionModel} this
17947              * @param {Number} rowIndex The selected index
17948              */
17949         "rowdeselect" : true
17950     });
17951     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17952     this.locked = false;
17953 };
17954
17955 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
17956     /**
17957      * @cfg {Boolean} singleSelect
17958      * True to allow selection of only one row at a time (defaults to false)
17959      */
17960     singleSelect : false,
17961
17962     // private
17963     initEvents : function(){
17964
17965         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17966             this.grid.on("mousedown", this.handleMouseDown, this);
17967         }else{ // allow click to work like normal
17968             this.grid.on("rowclick", this.handleDragableRowClick, this);
17969         }
17970
17971         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17972             "up" : function(e){
17973                 if(!e.shiftKey){
17974                     this.selectPrevious(e.shiftKey);
17975                 }else if(this.last !== false && this.lastActive !== false){
17976                     var last = this.last;
17977                     this.selectRange(this.last,  this.lastActive-1);
17978                     this.grid.getView().focusRow(this.lastActive);
17979                     if(last !== false){
17980                         this.last = last;
17981                     }
17982                 }else{
17983                     this.selectFirstRow();
17984                 }
17985                 this.fireEvent("afterselectionchange", this);
17986             },
17987             "down" : function(e){
17988                 if(!e.shiftKey){
17989                     this.selectNext(e.shiftKey);
17990                 }else if(this.last !== false && this.lastActive !== false){
17991                     var last = this.last;
17992                     this.selectRange(this.last,  this.lastActive+1);
17993                     this.grid.getView().focusRow(this.lastActive);
17994                     if(last !== false){
17995                         this.last = last;
17996                     }
17997                 }else{
17998                     this.selectFirstRow();
17999                 }
18000                 this.fireEvent("afterselectionchange", this);
18001             },
18002             scope: this
18003         });
18004
18005         var view = this.grid.view;
18006         view.on("refresh", this.onRefresh, this);
18007         view.on("rowupdated", this.onRowUpdated, this);
18008         view.on("rowremoved", this.onRemove, this);
18009     },
18010
18011     // private
18012     onRefresh : function(){
18013         var ds = this.grid.dataSource, i, v = this.grid.view;
18014         var s = this.selections;
18015         s.each(function(r){
18016             if((i = ds.indexOfId(r.id)) != -1){
18017                 v.onRowSelect(i);
18018             }else{
18019                 s.remove(r);
18020             }
18021         });
18022     },
18023
18024     // private
18025     onRemove : function(v, index, r){
18026         this.selections.remove(r);
18027     },
18028
18029     // private
18030     onRowUpdated : function(v, index, r){
18031         if(this.isSelected(r)){
18032             v.onRowSelect(index);
18033         }
18034     },
18035
18036     /**
18037      * Select records.
18038      * @param {Array} records The records to select
18039      * @param {Boolean} keepExisting (optional) True to keep existing selections
18040      */
18041     selectRecords : function(records, keepExisting){
18042         if(!keepExisting){
18043             this.clearSelections();
18044         }
18045         var ds = this.grid.dataSource;
18046         for(var i = 0, len = records.length; i < len; i++){
18047             this.selectRow(ds.indexOf(records[i]), true);
18048         }
18049     },
18050
18051     /**
18052      * Gets the number of selected rows.
18053      * @return {Number}
18054      */
18055     getCount : function(){
18056         return this.selections.length;
18057     },
18058
18059     /**
18060      * Selects the first row in the grid.
18061      */
18062     selectFirstRow : function(){
18063         this.selectRow(0);
18064     },
18065
18066     /**
18067      * Select the last row.
18068      * @param {Boolean} keepExisting (optional) True to keep existing selections
18069      */
18070     selectLastRow : function(keepExisting){
18071         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18072     },
18073
18074     /**
18075      * Selects the row immediately following the last selected row.
18076      * @param {Boolean} keepExisting (optional) True to keep existing selections
18077      */
18078     selectNext : function(keepExisting){
18079         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18080             this.selectRow(this.last+1, keepExisting);
18081             this.grid.getView().focusRow(this.last);
18082         }
18083     },
18084
18085     /**
18086      * Selects the row that precedes the last selected row.
18087      * @param {Boolean} keepExisting (optional) True to keep existing selections
18088      */
18089     selectPrevious : function(keepExisting){
18090         if(this.last){
18091             this.selectRow(this.last-1, keepExisting);
18092             this.grid.getView().focusRow(this.last);
18093         }
18094     },
18095
18096     /**
18097      * Returns the selected records
18098      * @return {Array} Array of selected records
18099      */
18100     getSelections : function(){
18101         return [].concat(this.selections.items);
18102     },
18103
18104     /**
18105      * Returns the first selected record.
18106      * @return {Record}
18107      */
18108     getSelected : function(){
18109         return this.selections.itemAt(0);
18110     },
18111
18112
18113     /**
18114      * Clears all selections.
18115      */
18116     clearSelections : function(fast){
18117         if(this.locked) return;
18118         if(fast !== true){
18119             var ds = this.grid.dataSource;
18120             var s = this.selections;
18121             s.each(function(r){
18122                 this.deselectRow(ds.indexOfId(r.id));
18123             }, this);
18124             s.clear();
18125         }else{
18126             this.selections.clear();
18127         }
18128         this.last = false;
18129     },
18130
18131
18132     /**
18133      * Selects all rows.
18134      */
18135     selectAll : function(){
18136         if(this.locked) return;
18137         this.selections.clear();
18138         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18139             this.selectRow(i, true);
18140         }
18141     },
18142
18143     /**
18144      * Returns True if there is a selection.
18145      * @return {Boolean}
18146      */
18147     hasSelection : function(){
18148         return this.selections.length > 0;
18149     },
18150
18151     /**
18152      * Returns True if the specified row is selected.
18153      * @param {Number/Record} record The record or index of the record to check
18154      * @return {Boolean}
18155      */
18156     isSelected : function(index){
18157         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18158         return (r && this.selections.key(r.id) ? true : false);
18159     },
18160
18161     /**
18162      * Returns True if the specified record id is selected.
18163      * @param {String} id The id of record to check
18164      * @return {Boolean}
18165      */
18166     isIdSelected : function(id){
18167         return (this.selections.key(id) ? true : false);
18168     },
18169
18170     // private
18171     handleMouseDown : function(e, t){
18172         var view = this.grid.getView(), rowIndex;
18173         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18174             return;
18175         };
18176         if(e.shiftKey && this.last !== false){
18177             var last = this.last;
18178             this.selectRange(last, rowIndex, e.ctrlKey);
18179             this.last = last; // reset the last
18180             view.focusRow(rowIndex);
18181         }else{
18182             var isSelected = this.isSelected(rowIndex);
18183             if(e.button !== 0 && isSelected){
18184                 view.focusRow(rowIndex);
18185             }else if(e.ctrlKey && isSelected){
18186                 this.deselectRow(rowIndex);
18187             }else if(!isSelected){
18188                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18189                 view.focusRow(rowIndex);
18190             }
18191         }
18192         this.fireEvent("afterselectionchange", this);
18193     },
18194     // private
18195     handleDragableRowClick :  function(grid, rowIndex, e) 
18196     {
18197         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18198             this.selectRow(rowIndex, false);
18199             grid.view.focusRow(rowIndex);
18200              this.fireEvent("afterselectionchange", this);
18201         }
18202     },
18203     
18204     /**
18205      * Selects multiple rows.
18206      * @param {Array} rows Array of the indexes of the row to select
18207      * @param {Boolean} keepExisting (optional) True to keep existing selections
18208      */
18209     selectRows : function(rows, keepExisting){
18210         if(!keepExisting){
18211             this.clearSelections();
18212         }
18213         for(var i = 0, len = rows.length; i < len; i++){
18214             this.selectRow(rows[i], true);
18215         }
18216     },
18217
18218     /**
18219      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18220      * @param {Number} startRow The index of the first row in the range
18221      * @param {Number} endRow The index of the last row in the range
18222      * @param {Boolean} keepExisting (optional) True to retain existing selections
18223      */
18224     selectRange : function(startRow, endRow, keepExisting){
18225         if(this.locked) return;
18226         if(!keepExisting){
18227             this.clearSelections();
18228         }
18229         if(startRow <= endRow){
18230             for(var i = startRow; i <= endRow; i++){
18231                 this.selectRow(i, true);
18232             }
18233         }else{
18234             for(var i = startRow; i >= endRow; i--){
18235                 this.selectRow(i, true);
18236             }
18237         }
18238     },
18239
18240     /**
18241      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18242      * @param {Number} startRow The index of the first row in the range
18243      * @param {Number} endRow The index of the last row in the range
18244      */
18245     deselectRange : function(startRow, endRow, preventViewNotify){
18246         if(this.locked) return;
18247         for(var i = startRow; i <= endRow; i++){
18248             this.deselectRow(i, preventViewNotify);
18249         }
18250     },
18251
18252     /**
18253      * Selects a row.
18254      * @param {Number} row The index of the row to select
18255      * @param {Boolean} keepExisting (optional) True to keep existing selections
18256      */
18257     selectRow : function(index, keepExisting, preventViewNotify){
18258         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18259         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18260             if(!keepExisting || this.singleSelect){
18261                 this.clearSelections();
18262             }
18263             var r = this.grid.dataSource.getAt(index);
18264             this.selections.add(r);
18265             this.last = this.lastActive = index;
18266             if(!preventViewNotify){
18267                 this.grid.getView().onRowSelect(index);
18268             }
18269             this.fireEvent("rowselect", this, index, r);
18270             this.fireEvent("selectionchange", this);
18271         }
18272     },
18273
18274     /**
18275      * Deselects a row.
18276      * @param {Number} row The index of the row to deselect
18277      */
18278     deselectRow : function(index, preventViewNotify){
18279         if(this.locked) return;
18280         if(this.last == index){
18281             this.last = false;
18282         }
18283         if(this.lastActive == index){
18284             this.lastActive = false;
18285         }
18286         var r = this.grid.dataSource.getAt(index);
18287         this.selections.remove(r);
18288         if(!preventViewNotify){
18289             this.grid.getView().onRowDeselect(index);
18290         }
18291         this.fireEvent("rowdeselect", this, index);
18292         this.fireEvent("selectionchange", this);
18293     },
18294
18295     // private
18296     restoreLast : function(){
18297         if(this._last){
18298             this.last = this._last;
18299         }
18300     },
18301
18302     // private
18303     acceptsNav : function(row, col, cm){
18304         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18305     },
18306
18307     // private
18308     onEditorKey : function(field, e){
18309         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18310         if(k == e.TAB){
18311             e.stopEvent();
18312             ed.completeEdit();
18313             if(e.shiftKey){
18314                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18315             }else{
18316                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18317             }
18318         }else if(k == e.ENTER && !e.ctrlKey){
18319             e.stopEvent();
18320             ed.completeEdit();
18321             if(e.shiftKey){
18322                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18323             }else{
18324                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18325             }
18326         }else if(k == e.ESC){
18327             ed.cancelEdit();
18328         }
18329         if(newCell){
18330             g.startEditing(newCell[0], newCell[1]);
18331         }
18332     }
18333 });/*
18334  * Based on:
18335  * Ext JS Library 1.1.1
18336  * Copyright(c) 2006-2007, Ext JS, LLC.
18337  *
18338  * Originally Released Under LGPL - original licence link has changed is not relivant.
18339  *
18340  * Fork - LGPL
18341  * <script type="text/javascript">
18342  */
18343  
18344 /**
18345  * @class Roo.bootstrap.PagingToolbar
18346  * @extends Roo.Row
18347  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18348  * @constructor
18349  * Create a new PagingToolbar
18350  * @param {Object} config The config object
18351  */
18352 Roo.bootstrap.PagingToolbar = function(config)
18353 {
18354     // old args format still supported... - xtype is prefered..
18355         // created from xtype...
18356     var ds = config.dataSource;
18357     this.toolbarItems = [];
18358     if (config.items) {
18359         this.toolbarItems = config.items;
18360 //        config.items = [];
18361     }
18362     
18363     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18364     this.ds = ds;
18365     this.cursor = 0;
18366     if (ds) { 
18367         this.bind(ds);
18368     }
18369     
18370     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18371     
18372 };
18373
18374 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18375     /**
18376      * @cfg {Roo.data.Store} dataSource
18377      * The underlying data store providing the paged data
18378      */
18379     /**
18380      * @cfg {String/HTMLElement/Element} container
18381      * container The id or element that will contain the toolbar
18382      */
18383     /**
18384      * @cfg {Boolean} displayInfo
18385      * True to display the displayMsg (defaults to false)
18386      */
18387     /**
18388      * @cfg {Number} pageSize
18389      * The number of records to display per page (defaults to 20)
18390      */
18391     pageSize: 20,
18392     /**
18393      * @cfg {String} displayMsg
18394      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18395      */
18396     displayMsg : 'Displaying {0} - {1} of {2}',
18397     /**
18398      * @cfg {String} emptyMsg
18399      * The message to display when no records are found (defaults to "No data to display")
18400      */
18401     emptyMsg : 'No data to display',
18402     /**
18403      * Customizable piece of the default paging text (defaults to "Page")
18404      * @type String
18405      */
18406     beforePageText : "Page",
18407     /**
18408      * Customizable piece of the default paging text (defaults to "of %0")
18409      * @type String
18410      */
18411     afterPageText : "of {0}",
18412     /**
18413      * Customizable piece of the default paging text (defaults to "First Page")
18414      * @type String
18415      */
18416     firstText : "First Page",
18417     /**
18418      * Customizable piece of the default paging text (defaults to "Previous Page")
18419      * @type String
18420      */
18421     prevText : "Previous Page",
18422     /**
18423      * Customizable piece of the default paging text (defaults to "Next Page")
18424      * @type String
18425      */
18426     nextText : "Next Page",
18427     /**
18428      * Customizable piece of the default paging text (defaults to "Last Page")
18429      * @type String
18430      */
18431     lastText : "Last Page",
18432     /**
18433      * Customizable piece of the default paging text (defaults to "Refresh")
18434      * @type String
18435      */
18436     refreshText : "Refresh",
18437
18438     buttons : false,
18439     // private
18440     onRender : function(ct, position) 
18441     {
18442         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18443         this.navgroup.parentId = this.id;
18444         this.navgroup.onRender(this.el, null);
18445         // add the buttons to the navgroup
18446         
18447         if(this.displayInfo){
18448             Roo.log(this.el.select('ul.navbar-nav',true).first());
18449             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18450             this.displayEl = this.el.select('.x-paging-info', true).first();
18451 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18452 //            this.displayEl = navel.el.select('span',true).first();
18453         }
18454         
18455         var _this = this;
18456         
18457         if(this.buttons){
18458             Roo.each(_this.buttons, function(e){
18459                Roo.factory(e).onRender(_this.el, null);
18460             });
18461         }
18462             
18463         Roo.each(_this.toolbarItems, function(e) {
18464             _this.navgroup.addItem(e);
18465         });
18466         
18467         this.first = this.navgroup.addItem({
18468             tooltip: this.firstText,
18469             cls: "prev",
18470             icon : 'fa fa-backward',
18471             disabled: true,
18472             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18473         });
18474         
18475         this.prev =  this.navgroup.addItem({
18476             tooltip: this.prevText,
18477             cls: "prev",
18478             icon : 'fa fa-step-backward',
18479             disabled: true,
18480             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18481         });
18482     //this.addSeparator();
18483         
18484         
18485         var field = this.navgroup.addItem( {
18486             tagtype : 'span',
18487             cls : 'x-paging-position',
18488             
18489             html : this.beforePageText  +
18490                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
18491                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
18492          } ); //?? escaped?
18493         
18494         this.field = field.el.select('input', true).first();
18495         this.field.on("keydown", this.onPagingKeydown, this);
18496         this.field.on("focus", function(){this.dom.select();});
18497     
18498     
18499         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
18500         //this.field.setHeight(18);
18501         //this.addSeparator();
18502         this.next = this.navgroup.addItem({
18503             tooltip: this.nextText,
18504             cls: "next",
18505             html : ' <i class="fa fa-step-forward">',
18506             disabled: true,
18507             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
18508         });
18509         this.last = this.navgroup.addItem({
18510             tooltip: this.lastText,
18511             icon : 'fa fa-forward',
18512             cls: "next",
18513             disabled: true,
18514             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
18515         });
18516     //this.addSeparator();
18517         this.loading = this.navgroup.addItem({
18518             tooltip: this.refreshText,
18519             icon: 'fa fa-refresh',
18520             
18521             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
18522         });
18523
18524     },
18525
18526     // private
18527     updateInfo : function(){
18528         if(this.displayEl){
18529             var count = this.ds.getCount();
18530             var msg = count == 0 ?
18531                 this.emptyMsg :
18532                 String.format(
18533                     this.displayMsg,
18534                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
18535                 );
18536             this.displayEl.update(msg);
18537         }
18538     },
18539
18540     // private
18541     onLoad : function(ds, r, o){
18542        this.cursor = o.params ? o.params.start : 0;
18543        var d = this.getPageData(),
18544             ap = d.activePage,
18545             ps = d.pages;
18546         
18547        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
18548        this.field.dom.value = ap;
18549        this.first.setDisabled(ap == 1);
18550        this.prev.setDisabled(ap == 1);
18551        this.next.setDisabled(ap == ps);
18552        this.last.setDisabled(ap == ps);
18553        this.loading.enable();
18554        this.updateInfo();
18555     },
18556
18557     // private
18558     getPageData : function(){
18559         var total = this.ds.getTotalCount();
18560         return {
18561             total : total,
18562             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
18563             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
18564         };
18565     },
18566
18567     // private
18568     onLoadError : function(){
18569         this.loading.enable();
18570     },
18571
18572     // private
18573     onPagingKeydown : function(e){
18574         var k = e.getKey();
18575         var d = this.getPageData();
18576         if(k == e.RETURN){
18577             var v = this.field.dom.value, pageNum;
18578             if(!v || isNaN(pageNum = parseInt(v, 10))){
18579                 this.field.dom.value = d.activePage;
18580                 return;
18581             }
18582             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
18583             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18584             e.stopEvent();
18585         }
18586         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
18587         {
18588           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
18589           this.field.dom.value = pageNum;
18590           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
18591           e.stopEvent();
18592         }
18593         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18594         {
18595           var v = this.field.dom.value, pageNum; 
18596           var increment = (e.shiftKey) ? 10 : 1;
18597           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18598             increment *= -1;
18599           if(!v || isNaN(pageNum = parseInt(v, 10))) {
18600             this.field.dom.value = d.activePage;
18601             return;
18602           }
18603           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
18604           {
18605             this.field.dom.value = parseInt(v, 10) + increment;
18606             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
18607             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18608           }
18609           e.stopEvent();
18610         }
18611     },
18612
18613     // private
18614     beforeLoad : function(){
18615         if(this.loading){
18616             this.loading.disable();
18617         }
18618     },
18619
18620     // private
18621     onClick : function(which){
18622         var ds = this.ds;
18623         if (!ds) {
18624             return;
18625         }
18626         switch(which){
18627             case "first":
18628                 ds.load({params:{start: 0, limit: this.pageSize}});
18629             break;
18630             case "prev":
18631                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
18632             break;
18633             case "next":
18634                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
18635             break;
18636             case "last":
18637                 var total = ds.getTotalCount();
18638                 var extra = total % this.pageSize;
18639                 var lastStart = extra ? (total - extra) : total-this.pageSize;
18640                 ds.load({params:{start: lastStart, limit: this.pageSize}});
18641             break;
18642             case "refresh":
18643                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
18644             break;
18645         }
18646     },
18647
18648     /**
18649      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
18650      * @param {Roo.data.Store} store The data store to unbind
18651      */
18652     unbind : function(ds){
18653         ds.un("beforeload", this.beforeLoad, this);
18654         ds.un("load", this.onLoad, this);
18655         ds.un("loadexception", this.onLoadError, this);
18656         ds.un("remove", this.updateInfo, this);
18657         ds.un("add", this.updateInfo, this);
18658         this.ds = undefined;
18659     },
18660
18661     /**
18662      * Binds the paging toolbar to the specified {@link Roo.data.Store}
18663      * @param {Roo.data.Store} store The data store to bind
18664      */
18665     bind : function(ds){
18666         ds.on("beforeload", this.beforeLoad, this);
18667         ds.on("load", this.onLoad, this);
18668         ds.on("loadexception", this.onLoadError, this);
18669         ds.on("remove", this.updateInfo, this);
18670         ds.on("add", this.updateInfo, this);
18671         this.ds = ds;
18672     }
18673 });/*
18674  * - LGPL
18675  *
18676  * element
18677  * 
18678  */
18679
18680 /**
18681  * @class Roo.bootstrap.MessageBar
18682  * @extends Roo.bootstrap.Component
18683  * Bootstrap MessageBar class
18684  * @cfg {String} html contents of the MessageBar
18685  * @cfg {String} weight (info | success | warning | danger) default info
18686  * @cfg {String} beforeClass insert the bar before the given class
18687  * @cfg {Boolean} closable (true | false) default false
18688  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
18689  * 
18690  * @constructor
18691  * Create a new Element
18692  * @param {Object} config The config object
18693  */
18694
18695 Roo.bootstrap.MessageBar = function(config){
18696     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
18697 };
18698
18699 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
18700     
18701     html: '',
18702     weight: 'info',
18703     closable: false,
18704     fixed: false,
18705     beforeClass: 'bootstrap-sticky-wrap',
18706     
18707     getAutoCreate : function(){
18708         
18709         var cfg = {
18710             tag: 'div',
18711             cls: 'alert alert-dismissable alert-' + this.weight,
18712             cn: [
18713                 {
18714                     tag: 'span',
18715                     cls: 'message',
18716                     html: this.html || ''
18717                 }
18718             ]
18719         }
18720         
18721         if(this.fixed){
18722             cfg.cls += ' alert-messages-fixed';
18723         }
18724         
18725         if(this.closable){
18726             cfg.cn.push({
18727                 tag: 'button',
18728                 cls: 'close',
18729                 html: 'x'
18730             });
18731         }
18732         
18733         return cfg;
18734     },
18735     
18736     onRender : function(ct, position)
18737     {
18738         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18739         
18740         if(!this.el){
18741             var cfg = Roo.apply({},  this.getAutoCreate());
18742             cfg.id = Roo.id();
18743             
18744             if (this.cls) {
18745                 cfg.cls += ' ' + this.cls;
18746             }
18747             if (this.style) {
18748                 cfg.style = this.style;
18749             }
18750             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
18751             
18752             this.el.setVisibilityMode(Roo.Element.DISPLAY);
18753         }
18754         
18755         this.el.select('>button.close').on('click', this.hide, this);
18756         
18757     },
18758     
18759     show : function()
18760     {
18761         if (!this.rendered) {
18762             this.render();
18763         }
18764         
18765         this.el.show();
18766         
18767         this.fireEvent('show', this);
18768         
18769     },
18770     
18771     hide : function()
18772     {
18773         if (!this.rendered) {
18774             this.render();
18775         }
18776         
18777         this.el.hide();
18778         
18779         this.fireEvent('hide', this);
18780     },
18781     
18782     update : function()
18783     {
18784 //        var e = this.el.dom.firstChild;
18785 //        
18786 //        if(this.closable){
18787 //            e = e.nextSibling;
18788 //        }
18789 //        
18790 //        e.data = this.html || '';
18791
18792         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
18793     }
18794    
18795 });
18796
18797  
18798
18799      /*
18800  * - LGPL
18801  *
18802  * Graph
18803  * 
18804  */
18805
18806
18807 /**
18808  * @class Roo.bootstrap.Graph
18809  * @extends Roo.bootstrap.Component
18810  * Bootstrap Graph class
18811 > Prameters
18812  -sm {number} sm 4
18813  -md {number} md 5
18814  @cfg {String} graphtype  bar | vbar | pie
18815  @cfg {number} g_x coodinator | centre x (pie)
18816  @cfg {number} g_y coodinator | centre y (pie)
18817  @cfg {number} g_r radius (pie)
18818  @cfg {number} g_height height of the chart (respected by all elements in the set)
18819  @cfg {number} g_width width of the chart (respected by all elements in the set)
18820  @cfg {Object} title The title of the chart
18821     
18822  -{Array}  values
18823  -opts (object) options for the chart 
18824      o {
18825      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
18826      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
18827      o vgutter (number)
18828      o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
18829      o stacked (boolean) whether or not to tread values as in a stacked bar chart
18830      o to
18831      o stretch (boolean)
18832      o }
18833  -opts (object) options for the pie
18834      o{
18835      o cut
18836      o startAngle (number)
18837      o endAngle (number)
18838      } 
18839  *
18840  * @constructor
18841  * Create a new Input
18842  * @param {Object} config The config object
18843  */
18844
18845 Roo.bootstrap.Graph = function(config){
18846     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
18847     
18848     this.addEvents({
18849         // img events
18850         /**
18851          * @event click
18852          * The img click event for the img.
18853          * @param {Roo.EventObject} e
18854          */
18855         "click" : true
18856     });
18857 };
18858
18859 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
18860     
18861     sm: 4,
18862     md: 5,
18863     graphtype: 'bar',
18864     g_height: 250,
18865     g_width: 400,
18866     g_x: 50,
18867     g_y: 50,
18868     g_r: 30,
18869     opts:{
18870         //g_colors: this.colors,
18871         g_type: 'soft',
18872         g_gutter: '20%'
18873
18874     },
18875     title : false,
18876
18877     getAutoCreate : function(){
18878         
18879         var cfg = {
18880             tag: 'div',
18881             html : null
18882         }
18883         
18884         
18885         return  cfg;
18886     },
18887
18888     onRender : function(ct,position){
18889         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
18890         this.raphael = Raphael(this.el.dom);
18891         
18892                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18893                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18894                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18895                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
18896                 /*
18897                 r.text(160, 10, "Single Series Chart").attr(txtattr);
18898                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
18899                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
18900                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
18901                 
18902                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
18903                 r.barchart(330, 10, 300, 220, data1);
18904                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
18905                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
18906                 */
18907                 
18908                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
18909                 // r.barchart(30, 30, 560, 250,  xdata, {
18910                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
18911                 //     axis : "0 0 1 1",
18912                 //     axisxlabels :  xdata
18913                 //     //yvalues : cols,
18914                    
18915                 // });
18916 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
18917 //        
18918 //        this.load(null,xdata,{
18919 //                axis : "0 0 1 1",
18920 //                axisxlabels :  xdata
18921 //                });
18922
18923     },
18924
18925     load : function(graphtype,xdata,opts){
18926         this.raphael.clear();
18927         if(!graphtype) {
18928             graphtype = this.graphtype;
18929         }
18930         if(!opts){
18931             opts = this.opts;
18932         }
18933         var r = this.raphael,
18934             fin = function () {
18935                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
18936             },
18937             fout = function () {
18938                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
18939             },
18940             pfin = function() {
18941                 this.sector.stop();
18942                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
18943
18944                 if (this.label) {
18945                     this.label[0].stop();
18946                     this.label[0].attr({ r: 7.5 });
18947                     this.label[1].attr({ "font-weight": 800 });
18948                 }
18949             },
18950             pfout = function() {
18951                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
18952
18953                 if (this.label) {
18954                     this.label[0].animate({ r: 5 }, 500, "bounce");
18955                     this.label[1].attr({ "font-weight": 400 });
18956                 }
18957             };
18958
18959         switch(graphtype){
18960             case 'bar':
18961                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
18962                 break;
18963             case 'hbar':
18964                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
18965                 break;
18966             case 'pie':
18967 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
18968 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
18969 //            
18970                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
18971                 
18972                 break;
18973
18974         }
18975         
18976         if(this.title){
18977             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
18978         }
18979         
18980     },
18981     
18982     setTitle: function(o)
18983     {
18984         this.title = o;
18985     },
18986     
18987     initEvents: function() {
18988         
18989         if(!this.href){
18990             this.el.on('click', this.onClick, this);
18991         }
18992     },
18993     
18994     onClick : function(e)
18995     {
18996         Roo.log('img onclick');
18997         this.fireEvent('click', this, e);
18998     }
18999    
19000 });
19001
19002  
19003 /*
19004  * - LGPL
19005  *
19006  * numberBox
19007  * 
19008  */
19009 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19010
19011 /**
19012  * @class Roo.bootstrap.dash.NumberBox
19013  * @extends Roo.bootstrap.Component
19014  * Bootstrap NumberBox class
19015  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19016  * @cfg {String} headline Box headline
19017  * @cfg {String} content Box content
19018  * @cfg {String} icon Box icon
19019  * @cfg {String} footer Footer text
19020  * @cfg {String} fhref Footer href
19021  * 
19022  * @constructor
19023  * Create a new NumberBox
19024  * @param {Object} config The config object
19025  */
19026
19027
19028 Roo.bootstrap.dash.NumberBox = function(config){
19029     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19030     
19031 };
19032
19033 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19034     
19035     bgcolor : 'aqua',
19036     headline : '',
19037     content : '',
19038     icon : '',
19039     footer : '',
19040     fhref : '',
19041     ficon : '',
19042     
19043     getAutoCreate : function(){
19044         
19045         var cfg = {
19046             tag : 'div',
19047             cls : 'small-box bg-' + this.bgcolor,
19048             cn : [
19049                 {
19050                     tag : 'div',
19051                     cls : 'inner',
19052                     cn :[
19053                         {
19054                             tag : 'h3',
19055                             cls : 'roo-headline',
19056                             html : this.headline
19057                         },
19058                         {
19059                             tag : 'p',
19060                             cls : 'roo-content',
19061                             html : this.content
19062                         }
19063                     ]
19064                 }
19065             ]
19066         }
19067         
19068         if(this.icon){
19069             cfg.cn.push({
19070                 tag : 'div',
19071                 cls : 'icon',
19072                 cn :[
19073                     {
19074                         tag : 'i',
19075                         cls : 'ion ' + this.icon
19076                     }
19077                 ]
19078             });
19079         }
19080         
19081         if(this.footer){
19082             var footer = {
19083                 tag : 'a',
19084                 cls : 'small-box-footer',
19085                 href : this.fhref || '#',
19086                 html : this.footer
19087             };
19088             
19089             cfg.cn.push(footer);
19090             
19091         }
19092         
19093         return  cfg;
19094     },
19095
19096     onRender : function(ct,position){
19097         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19098
19099
19100        
19101                 
19102     },
19103
19104     setHeadline: function (value)
19105     {
19106         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19107     },
19108     
19109     setFooter: function (value, href)
19110     {
19111         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19112         
19113         if(href){
19114             this.el.select('a.small-box-footer',true).first().attr('href', href);
19115         }
19116         
19117     },
19118
19119     setContent: function (value)
19120     {
19121         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19122     },
19123
19124     initEvents: function() 
19125     {   
19126         
19127     }
19128     
19129 });
19130
19131  
19132 /*
19133  * - LGPL
19134  *
19135  * TabBox
19136  * 
19137  */
19138 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19139
19140 /**
19141  * @class Roo.bootstrap.dash.TabBox
19142  * @extends Roo.bootstrap.Component
19143  * Bootstrap TabBox class
19144  * @cfg {String} title Title of the TabBox
19145  * @cfg {String} icon Icon of the TabBox
19146  * 
19147  * @constructor
19148  * Create a new TabBox
19149  * @param {Object} config The config object
19150  */
19151
19152
19153 Roo.bootstrap.dash.TabBox = function(config){
19154     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19155     this.addEvents({
19156         // raw events
19157         /**
19158          * @event addpane
19159          * When a pane is added
19160          * @param {Roo.bootstrap.dash.TabPane} pane
19161          */
19162         "addpane" : true
19163          
19164     });
19165 };
19166
19167 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19168
19169     title : '',
19170     icon : false,
19171     
19172     getChildContainer : function()
19173     {
19174         return this.el.select('.tab-content', true).first();
19175     },
19176     
19177     getAutoCreate : function(){
19178         
19179         var header = {
19180             tag: 'li',
19181             cls: 'pull-left header',
19182             html: this.title,
19183             cn : []
19184         };
19185         
19186         if(this.icon){
19187             header.cn.push({
19188                 tag: 'i',
19189                 cls: 'fa ' + this.icon
19190             });
19191         }
19192         
19193         
19194         var cfg = {
19195             tag: 'div',
19196             cls: 'nav-tabs-custom',
19197             cn: [
19198                 {
19199                     tag: 'ul',
19200                     cls: 'nav nav-tabs pull-right',
19201                     cn: [
19202                         header
19203                     ]
19204                 },
19205                 {
19206                     tag: 'div',
19207                     cls: 'tab-content no-padding',
19208                     cn: []
19209                 }
19210             ]
19211         }
19212
19213         return  cfg;
19214     },
19215     initEvents : function()
19216     {
19217         //Roo.log('add add pane handler');
19218         this.on('addpane', this.onAddPane, this);
19219     },
19220      /**
19221      * Updates the box title
19222      * @param {String} html to set the title to.
19223      */
19224     setTitle : function(value)
19225     {
19226         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19227     },
19228     onAddPane : function(pane)
19229     {
19230         //Roo.log('addpane');
19231         //Roo.log(pane);
19232         // tabs are rendere left to right..
19233         var ctr = this.el.select('.nav-tabs', true).first();
19234          
19235          
19236         var existing = ctr.select('.nav-tab',true);
19237         var qty = existing.getCount();;
19238         
19239         
19240         var tab = ctr.createChild({
19241             tag : 'li',
19242             cls : 'nav-tab' + (qty ? '' : ' active'),
19243             cn : [
19244                 {
19245                     tag : 'a',
19246                     href:'#',
19247                     html : pane.title
19248                 }
19249             ]
19250         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19251         pane.tab = tab;
19252         
19253         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19254         if (!qty) {
19255             pane.el.addClass('active');
19256         }
19257         
19258                 
19259     },
19260     onTabClick : function(ev,un,ob,pane)
19261     {
19262         //Roo.log('tab - prev default');
19263         ev.preventDefault();
19264         
19265         
19266         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19267         pane.tab.addClass('active');
19268         //Roo.log(pane.title);
19269         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19270         // technically we should have a deactivate event.. but maybe add later.
19271         // and it should not de-activate the selected tab...
19272         
19273         pane.el.addClass('active');
19274         pane.fireEvent('activate');
19275         
19276         
19277     }
19278     
19279     
19280 });
19281
19282  
19283 /*
19284  * - LGPL
19285  *
19286  * Tab pane
19287  * 
19288  */
19289 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19290 /**
19291  * @class Roo.bootstrap.TabPane
19292  * @extends Roo.bootstrap.Component
19293  * Bootstrap TabPane class
19294  * @cfg {Boolean} active (false | true) Default false
19295  * @cfg {String} title title of panel
19296
19297  * 
19298  * @constructor
19299  * Create a new TabPane
19300  * @param {Object} config The config object
19301  */
19302
19303 Roo.bootstrap.dash.TabPane = function(config){
19304     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19305     
19306 };
19307
19308 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19309     
19310     active : false,
19311     title : '',
19312     
19313     // the tabBox that this is attached to.
19314     tab : false,
19315      
19316     getAutoCreate : function() 
19317     {
19318         var cfg = {
19319             tag: 'div',
19320             cls: 'tab-pane'
19321         }
19322         
19323         if(this.active){
19324             cfg.cls += ' active';
19325         }
19326         
19327         return cfg;
19328     },
19329     initEvents  : function()
19330     {
19331         //Roo.log('trigger add pane handler');
19332         this.parent().fireEvent('addpane', this)
19333     },
19334     
19335      /**
19336      * Updates the tab title 
19337      * @param {String} html to set the title to.
19338      */
19339     setTitle: function(str)
19340     {
19341         if (!this.tab) {
19342             return;
19343         }
19344         this.title = str;
19345         this.tab.select('a'.true).first().dom.innerHTML = str;
19346         
19347     }
19348     
19349     
19350     
19351 });
19352
19353  
19354
19355
19356  /*
19357  * - LGPL
19358  *
19359  * menu
19360  * 
19361  */
19362 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19363
19364 /**
19365  * @class Roo.bootstrap.menu.Menu
19366  * @extends Roo.bootstrap.Component
19367  * Bootstrap Menu class - container for Menu
19368  * @cfg {String} html Text of the menu
19369  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19370  * @cfg {String} icon Font awesome icon
19371  * @cfg {String} pos Menu align to (top | bottom) default bottom
19372  * 
19373  * 
19374  * @constructor
19375  * Create a new Menu
19376  * @param {Object} config The config object
19377  */
19378
19379
19380 Roo.bootstrap.menu.Menu = function(config){
19381     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19382     
19383     this.addEvents({
19384         /**
19385          * @event beforeshow
19386          * Fires before this menu is displayed
19387          * @param {Roo.bootstrap.menu.Menu} this
19388          */
19389         beforeshow : true,
19390         /**
19391          * @event beforehide
19392          * Fires before this menu is hidden
19393          * @param {Roo.bootstrap.menu.Menu} this
19394          */
19395         beforehide : true,
19396         /**
19397          * @event show
19398          * Fires after this menu is displayed
19399          * @param {Roo.bootstrap.menu.Menu} this
19400          */
19401         show : true,
19402         /**
19403          * @event hide
19404          * Fires after this menu is hidden
19405          * @param {Roo.bootstrap.menu.Menu} this
19406          */
19407         hide : true,
19408         /**
19409          * @event click
19410          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19411          * @param {Roo.bootstrap.menu.Menu} this
19412          * @param {Roo.EventObject} e
19413          */
19414         click : true
19415     });
19416     
19417 };
19418
19419 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19420     
19421     submenu : false,
19422     html : '',
19423     weight : 'default',
19424     icon : false,
19425     pos : 'bottom',
19426     
19427     
19428     getChildContainer : function() {
19429         if(this.isSubMenu){
19430             return this.el;
19431         }
19432         
19433         return this.el.select('ul.dropdown-menu', true).first();  
19434     },
19435     
19436     getAutoCreate : function()
19437     {
19438         var text = [
19439             {
19440                 tag : 'span',
19441                 cls : 'roo-menu-text',
19442                 html : this.html
19443             }
19444         ];
19445         
19446         if(this.icon){
19447             text.unshift({
19448                 tag : 'i',
19449                 cls : 'fa ' + this.icon
19450             })
19451         }
19452         
19453         
19454         var cfg = {
19455             tag : 'div',
19456             cls : 'btn-group',
19457             cn : [
19458                 {
19459                     tag : 'button',
19460                     cls : 'dropdown-button btn btn-' + this.weight,
19461                     cn : text
19462                 },
19463                 {
19464                     tag : 'button',
19465                     cls : 'dropdown-toggle btn btn-' + this.weight,
19466                     cn : [
19467                         {
19468                             tag : 'span',
19469                             cls : 'caret'
19470                         }
19471                     ]
19472                 },
19473                 {
19474                     tag : 'ul',
19475                     cls : 'dropdown-menu'
19476                 }
19477             ]
19478             
19479         };
19480         
19481         if(this.pos == 'top'){
19482             cfg.cls += ' dropup';
19483         }
19484         
19485         if(this.isSubMenu){
19486             cfg = {
19487                 tag : 'ul',
19488                 cls : 'dropdown-menu'
19489             }
19490         }
19491         
19492         return cfg;
19493     },
19494     
19495     onRender : function(ct, position)
19496     {
19497         this.isSubMenu = ct.hasClass('dropdown-submenu');
19498         
19499         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
19500     },
19501     
19502     initEvents : function() 
19503     {
19504         if(this.isSubMenu){
19505             return;
19506         }
19507         
19508         this.hidden = true;
19509         
19510         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
19511         this.triggerEl.on('click', this.onTriggerPress, this);
19512         
19513         this.buttonEl = this.el.select('button.dropdown-button', true).first();
19514         this.buttonEl.on('click', this.onClick, this);
19515         
19516     },
19517     
19518     list : function()
19519     {
19520         if(this.isSubMenu){
19521             return this.el;
19522         }
19523         
19524         return this.el.select('ul.dropdown-menu', true).first();
19525     },
19526     
19527     onClick : function(e)
19528     {
19529         this.fireEvent("click", this, e);
19530     },
19531     
19532     onTriggerPress  : function(e)
19533     {   
19534         if (this.isVisible()) {
19535             this.hide();
19536         } else {
19537             this.show();
19538         }
19539     },
19540     
19541     isVisible : function(){
19542         return !this.hidden;
19543     },
19544     
19545     show : function()
19546     {
19547         this.fireEvent("beforeshow", this);
19548         
19549         this.hidden = false;
19550         this.el.addClass('open');
19551         
19552         Roo.get(document).on("mouseup", this.onMouseUp, this);
19553         
19554         this.fireEvent("show", this);
19555         
19556         
19557     },
19558     
19559     hide : function()
19560     {
19561         this.fireEvent("beforehide", this);
19562         
19563         this.hidden = true;
19564         this.el.removeClass('open');
19565         
19566         Roo.get(document).un("mouseup", this.onMouseUp);
19567         
19568         this.fireEvent("hide", this);
19569     },
19570     
19571     onMouseUp : function()
19572     {
19573         this.hide();
19574     }
19575     
19576 });
19577
19578  
19579  /*
19580  * - LGPL
19581  *
19582  * menu item
19583  * 
19584  */
19585 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19586
19587 /**
19588  * @class Roo.bootstrap.menu.Item
19589  * @extends Roo.bootstrap.Component
19590  * Bootstrap MenuItem class
19591  * @cfg {Boolean} submenu (true | false) default false
19592  * @cfg {String} html text of the item
19593  * @cfg {String} href the link
19594  * @cfg {Boolean} disable (true | false) default false
19595  * @cfg {Boolean} preventDefault (true | false) default true
19596  * @cfg {String} icon Font awesome icon
19597  * @cfg {String} pos Submenu align to (left | right) default right 
19598  * 
19599  * 
19600  * @constructor
19601  * Create a new Item
19602  * @param {Object} config The config object
19603  */
19604
19605
19606 Roo.bootstrap.menu.Item = function(config){
19607     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
19608     this.addEvents({
19609         /**
19610          * @event mouseover
19611          * Fires when the mouse is hovering over this menu
19612          * @param {Roo.bootstrap.menu.Item} this
19613          * @param {Roo.EventObject} e
19614          */
19615         mouseover : true,
19616         /**
19617          * @event mouseout
19618          * Fires when the mouse exits this menu
19619          * @param {Roo.bootstrap.menu.Item} this
19620          * @param {Roo.EventObject} e
19621          */
19622         mouseout : true,
19623         // raw events
19624         /**
19625          * @event click
19626          * The raw click event for the entire grid.
19627          * @param {Roo.EventObject} e
19628          */
19629         click : true
19630     });
19631 };
19632
19633 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
19634     
19635     submenu : false,
19636     href : '',
19637     html : '',
19638     preventDefault: true,
19639     disable : false,
19640     icon : false,
19641     pos : 'right',
19642     
19643     getAutoCreate : function()
19644     {
19645         var text = [
19646             {
19647                 tag : 'span',
19648                 cls : 'roo-menu-item-text',
19649                 html : this.html
19650             }
19651         ];
19652         
19653         if(this.icon){
19654             text.unshift({
19655                 tag : 'i',
19656                 cls : 'fa ' + this.icon
19657             })
19658         }
19659         
19660         var cfg = {
19661             tag : 'li',
19662             cn : [
19663                 {
19664                     tag : 'a',
19665                     href : this.href || '#',
19666                     cn : text
19667                 }
19668             ]
19669         };
19670         
19671         if(this.disable){
19672             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
19673         }
19674         
19675         if(this.submenu){
19676             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
19677             
19678             if(this.pos == 'left'){
19679                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
19680             }
19681         }
19682         
19683         return cfg;
19684     },
19685     
19686     initEvents : function() 
19687     {
19688         this.el.on('mouseover', this.onMouseOver, this);
19689         this.el.on('mouseout', this.onMouseOut, this);
19690         
19691         this.el.select('a', true).first().on('click', this.onClick, this);
19692         
19693     },
19694     
19695     onClick : function(e)
19696     {
19697         if(this.preventDefault){
19698             e.preventDefault();
19699         }
19700         
19701         this.fireEvent("click", this, e);
19702     },
19703     
19704     onMouseOver : function(e)
19705     {
19706         if(this.submenu && this.pos == 'left'){
19707             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
19708         }
19709         
19710         this.fireEvent("mouseover", this, e);
19711     },
19712     
19713     onMouseOut : function(e)
19714     {
19715         this.fireEvent("mouseout", this, e);
19716     }
19717 });
19718
19719  
19720
19721  /*
19722  * - LGPL
19723  *
19724  * menu separator
19725  * 
19726  */
19727 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19728
19729 /**
19730  * @class Roo.bootstrap.menu.Separator
19731  * @extends Roo.bootstrap.Component
19732  * Bootstrap Separator class
19733  * 
19734  * @constructor
19735  * Create a new Separator
19736  * @param {Object} config The config object
19737  */
19738
19739
19740 Roo.bootstrap.menu.Separator = function(config){
19741     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
19742 };
19743
19744 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
19745     
19746     getAutoCreate : function(){
19747         var cfg = {
19748             tag : 'li',
19749             cls: 'divider'
19750         };
19751         
19752         return cfg;
19753     }
19754    
19755 });
19756
19757  
19758
19759