buildSDK/dependancy_bootstrap.txt
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr](true));
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
775  * @cfg {Number} md colspan out of 12 for computer-sized screens
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
777  * @cfg {String} html content of column.
778  * 
779  * @constructor
780  * Create a new Column
781  * @param {Object} config The config object
782  */
783
784 Roo.bootstrap.Column = function(config){
785     Roo.bootstrap.Column.superclass.constructor.call(this, config);
786 };
787
788 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
789     
790     xs: null,
791     sm: null,
792     md: null,
793     lg: null,
794     html: '',
795     offset: 0,
796     
797     getAutoCreate : function(){
798         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
799         
800         cfg = {
801             tag: 'div',
802             cls: 'column'
803         };
804         
805         var settings=this;
806         ['xs','sm','md','lg'].map(function(size){
807             if (settings[size]) {
808                 cfg.cls += ' col-' + size + '-' + settings[size];
809             }
810         });
811         if (this.html.length) {
812             cfg.html = this.html;
813         }
814         
815         return cfg;
816     }
817    
818 });
819
820  
821
822  /*
823  * - LGPL
824  *
825  * page container.
826  * 
827  */
828
829
830 /**
831  * @class Roo.bootstrap.Container
832  * @extends Roo.bootstrap.Component
833  * Bootstrap Container class
834  * @cfg {Boolean} jumbotron is it a jumbotron element
835  * @cfg {String} html content of element
836  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
837  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
838  * @cfg {String} header content of header (for panel)
839  * @cfg {String} footer content of footer (for panel)
840  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
841  * @cfg {String} tag (header|aside|section) type of HTML tag.
842
843  *     
844  * @constructor
845  * Create a new Container
846  * @param {Object} config The config object
847  */
848
849 Roo.bootstrap.Container = function(config){
850     Roo.bootstrap.Container.superclass.constructor.call(this, config);
851 };
852
853 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
854     
855     jumbotron : false,
856     well: '',
857     panel : '',
858     header: '',
859     footer : '',
860     sticky: '',
861     tag : false,
862   
863      
864     getChildContainer : function() {
865         
866         if(!this.el){
867             return false;
868         }
869         
870         if (this.panel.length) {
871             return this.el.select('.panel-body',true).first();
872         }
873         
874         return this.el;
875     },
876     
877     
878     getAutoCreate : function(){
879         
880         var cfg = {
881             tag : this.tag || 'div',
882             html : '',
883             cls : ''
884         };
885         if (this.jumbotron) {
886             cfg.cls = 'jumbotron';
887         }
888         // - this is applied by the parent..
889         //if (this.cls) {
890         //    cfg.cls = this.cls + '';
891         //}
892         
893         if (this.sticky.length) {
894             
895             var bd = Roo.get(document.body);
896             if (!bd.hasClass('bootstrap-sticky')) {
897                 bd.addClass('bootstrap-sticky');
898                 Roo.select('html',true).setStyle('height', '100%');
899             }
900              
901             cfg.cls += 'bootstrap-sticky-' + this.sticky;
902         }
903         
904         
905         if (this.well.length) {
906             switch (this.well) {
907                 case 'lg':
908                 case 'sm':
909                     cfg.cls +=' well well-' +this.well;
910                     break;
911                 default:
912                     cfg.cls +=' well';
913                     break;
914             }
915         }
916         
917         var body = cfg;
918         
919         if (this.panel.length) {
920             cfg.cls += ' panel panel-' + this.panel;
921             cfg.cn = [];
922             if (this.header.length) {
923                 cfg.cn.push({
924                     
925                     cls : 'panel-heading',
926                     cn : [{
927                         tag: 'h3',
928                         cls : 'panel-title',
929                         html : this.header
930                     }]
931                     
932                 });
933             }
934             body = false;
935             cfg.cn.push({
936                 cls : 'panel-body',
937                 html : this.html
938             });
939             
940             
941             if (this.footer.length) {
942                 cfg.cn.push({
943                     cls : 'panel-footer',
944                     html : this.footer
945                     
946                 });
947             }
948             
949         }
950         
951         if (body) {
952             body.html = this.html || cfg.html;
953         }
954         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
955             cfg.cls =  'container';
956         }
957         
958         return cfg;
959     }
960    
961 });
962
963  /*
964  * - LGPL
965  *
966  * image
967  * 
968  */
969
970
971 /**
972  * @class Roo.bootstrap.Img
973  * @extends Roo.bootstrap.Component
974  * Bootstrap Img class
975  * @cfg {Boolean} imgResponsive false | true
976  * @cfg {String} border rounded | circle | thumbnail
977  * @cfg {String} src image source
978  * @cfg {String} alt image alternative text
979  * @cfg {String} href a tag href
980  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
981  * 
982  * @constructor
983  * Create a new Input
984  * @param {Object} config The config object
985  */
986
987 Roo.bootstrap.Img = function(config){
988     Roo.bootstrap.Img.superclass.constructor.call(this, config);
989     
990     this.addEvents({
991         // img events
992         /**
993          * @event click
994          * The img click event for the img.
995          * @param {Roo.EventObject} e
996          */
997         "click" : true
998     });
999 };
1000
1001 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1002     
1003     imgResponsive: true,
1004     border: '',
1005     src: '',
1006     href: false,
1007     target: false,
1008
1009     getAutoCreate : function(){
1010         
1011         var cfg = {
1012             tag: 'img',
1013             cls: (this.imgResponsive) ? 'img-responsive' : '',
1014             html : null
1015         }
1016         
1017         cfg.html = this.html || cfg.html;
1018         
1019         cfg.src = this.src || cfg.src;
1020         
1021         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1022             cfg.cls += ' img-' + this.border;
1023         }
1024         
1025         if(this.alt){
1026             cfg.alt = this.alt;
1027         }
1028         
1029         if(this.href){
1030             var a = {
1031                 tag: 'a',
1032                 href: this.href,
1033                 cn: [
1034                     cfg
1035                 ]
1036             }
1037             
1038             if(this.target){
1039                 a.target = this.target;
1040             }
1041             
1042         }
1043         
1044         
1045         return (this.href) ? a : cfg;
1046     },
1047     
1048     initEvents: function() {
1049         
1050         if(!this.href){
1051             this.el.on('click', this.onClick, this);
1052         }
1053     },
1054     
1055     onClick : function(e)
1056     {
1057         Roo.log('img onclick');
1058         this.fireEvent('click', this, e);
1059     }
1060    
1061 });
1062
1063  /*
1064  * - LGPL
1065  *
1066  * image
1067  * 
1068  */
1069
1070
1071 /**
1072  * @class Roo.bootstrap.Link
1073  * @extends Roo.bootstrap.Component
1074  * Bootstrap Link Class
1075  * @cfg {String} alt image alternative text
1076  * @cfg {String} href a tag href
1077  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1078  * @cfg {String} html the content of the link.
1079
1080  * 
1081  * @constructor
1082  * Create a new Input
1083  * @param {Object} config The config object
1084  */
1085
1086 Roo.bootstrap.Link = function(config){
1087     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1088     
1089     this.addEvents({
1090         // img events
1091         /**
1092          * @event click
1093          * The img click event for the img.
1094          * @param {Roo.EventObject} e
1095          */
1096         "click" : true
1097     });
1098 };
1099
1100 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1101     
1102     href: false,
1103     target: false,
1104
1105     getAutoCreate : function(){
1106         
1107         var cfg = {
1108             tag: 'a',
1109             html : this.html || 'html-missing'
1110         }
1111         
1112         
1113         if(this.alt){
1114             cfg.alt = this.alt;
1115         }
1116         cfg.href = this.href || '#';
1117         if(this.target){
1118             cfg.target = this.target;
1119         }
1120         
1121         return cfg;
1122     },
1123     
1124     initEvents: function() {
1125         
1126         if(!this.href){
1127             this.el.on('click', this.onClick, this);
1128         }
1129     },
1130     
1131     onClick : function(e)
1132     {
1133         //Roo.log('img onclick');
1134         this.fireEvent('click', this, e);
1135     }
1136    
1137 });
1138
1139  /*
1140  * - LGPL
1141  *
1142  * header
1143  * 
1144  */
1145
1146 /**
1147  * @class Roo.bootstrap.Header
1148  * @extends Roo.bootstrap.Component
1149  * Bootstrap Header class
1150  * @cfg {String} html content of header
1151  * @cfg {Number} level (1|2|3|4|5|6) default 1
1152  * 
1153  * @constructor
1154  * Create a new Header
1155  * @param {Object} config The config object
1156  */
1157
1158
1159 Roo.bootstrap.Header  = function(config){
1160     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1161 };
1162
1163 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1164     
1165     //href : false,
1166     html : false,
1167     level : 1,
1168     
1169     
1170     
1171     getAutoCreate : function(){
1172         
1173         var cfg = {
1174             tag: 'h' + (1 *this.level),
1175             html: this.html || 'fill in html'
1176         } ;
1177         
1178         return cfg;
1179     }
1180    
1181 });
1182
1183  
1184
1185  /*
1186  * Based on:
1187  * Ext JS Library 1.1.1
1188  * Copyright(c) 2006-2007, Ext JS, LLC.
1189  *
1190  * Originally Released Under LGPL - original licence link has changed is not relivant.
1191  *
1192  * Fork - LGPL
1193  * <script type="text/javascript">
1194  */
1195  
1196 /**
1197  * @class Roo.bootstrap.MenuMgr
1198  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1199  * @singleton
1200  */
1201 Roo.bootstrap.MenuMgr = function(){
1202    var menus, active, groups = {}, attached = false, lastShow = new Date();
1203
1204    // private - called when first menu is created
1205    function init(){
1206        menus = {};
1207        active = new Roo.util.MixedCollection();
1208        Roo.get(document).addKeyListener(27, function(){
1209            if(active.length > 0){
1210                hideAll();
1211            }
1212        });
1213    }
1214
1215    // private
1216    function hideAll(){
1217        if(active && active.length > 0){
1218            var c = active.clone();
1219            c.each(function(m){
1220                m.hide();
1221            });
1222        }
1223    }
1224
1225    // private
1226    function onHide(m){
1227        active.remove(m);
1228        if(active.length < 1){
1229            Roo.get(document).un("mouseup", onMouseDown);
1230             
1231            attached = false;
1232        }
1233    }
1234
1235    // private
1236    function onShow(m){
1237        var last = active.last();
1238        lastShow = new Date();
1239        active.add(m);
1240        if(!attached){
1241           Roo.get(document).on("mouseup", onMouseDown);
1242            
1243            attached = true;
1244        }
1245        if(m.parentMenu){
1246           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1247           m.parentMenu.activeChild = m;
1248        }else if(last && last.isVisible()){
1249           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1250        }
1251    }
1252
1253    // private
1254    function onBeforeHide(m){
1255        if(m.activeChild){
1256            m.activeChild.hide();
1257        }
1258        if(m.autoHideTimer){
1259            clearTimeout(m.autoHideTimer);
1260            delete m.autoHideTimer;
1261        }
1262    }
1263
1264    // private
1265    function onBeforeShow(m){
1266        var pm = m.parentMenu;
1267        if(!pm && !m.allowOtherMenus){
1268            hideAll();
1269        }else if(pm && pm.activeChild && active != m){
1270            pm.activeChild.hide();
1271        }
1272    }
1273
1274    // private
1275    function onMouseDown(e){
1276         Roo.log("on MouseDown");
1277         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1278            hideAll();
1279         }
1280         
1281         
1282    }
1283
1284    // private
1285    function onBeforeCheck(mi, state){
1286        if(state){
1287            var g = groups[mi.group];
1288            for(var i = 0, l = g.length; i < l; i++){
1289                if(g[i] != mi){
1290                    g[i].setChecked(false);
1291                }
1292            }
1293        }
1294    }
1295
1296    return {
1297
1298        /**
1299         * Hides all menus that are currently visible
1300         */
1301        hideAll : function(){
1302             hideAll();  
1303        },
1304
1305        // private
1306        register : function(menu){
1307            if(!menus){
1308                init();
1309            }
1310            menus[menu.id] = menu;
1311            menu.on("beforehide", onBeforeHide);
1312            menu.on("hide", onHide);
1313            menu.on("beforeshow", onBeforeShow);
1314            menu.on("show", onShow);
1315            var g = menu.group;
1316            if(g && menu.events["checkchange"]){
1317                if(!groups[g]){
1318                    groups[g] = [];
1319                }
1320                groups[g].push(menu);
1321                menu.on("checkchange", onCheck);
1322            }
1323        },
1324
1325         /**
1326          * Returns a {@link Roo.menu.Menu} object
1327          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1328          * be used to generate and return a new Menu instance.
1329          */
1330        get : function(menu){
1331            if(typeof menu == "string"){ // menu id
1332                return menus[menu];
1333            }else if(menu.events){  // menu instance
1334                return menu;
1335            }
1336            /*else if(typeof menu.length == 'number'){ // array of menu items?
1337                return new Roo.bootstrap.Menu({items:menu});
1338            }else{ // otherwise, must be a config
1339                return new Roo.bootstrap.Menu(menu);
1340            }
1341            */
1342            return false;
1343        },
1344
1345        // private
1346        unregister : function(menu){
1347            delete menus[menu.id];
1348            menu.un("beforehide", onBeforeHide);
1349            menu.un("hide", onHide);
1350            menu.un("beforeshow", onBeforeShow);
1351            menu.un("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                groups[g].remove(menu);
1355                menu.un("checkchange", onCheck);
1356            }
1357        },
1358
1359        // private
1360        registerCheckable : function(menuItem){
1361            var g = menuItem.group;
1362            if(g){
1363                if(!groups[g]){
1364                    groups[g] = [];
1365                }
1366                groups[g].push(menuItem);
1367                menuItem.on("beforecheckchange", onBeforeCheck);
1368            }
1369        },
1370
1371        // private
1372        unregisterCheckable : function(menuItem){
1373            var g = menuItem.group;
1374            if(g){
1375                groups[g].remove(menuItem);
1376                menuItem.un("beforecheckchange", onBeforeCheck);
1377            }
1378        }
1379    };
1380 }();/*
1381  * - LGPL
1382  *
1383  * menu
1384  * 
1385  */
1386
1387 /**
1388  * @class Roo.bootstrap.Menu
1389  * @extends Roo.bootstrap.Component
1390  * Bootstrap Menu class - container for MenuItems
1391  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1392  * 
1393  * @constructor
1394  * Create a new Menu
1395  * @param {Object} config The config object
1396  */
1397
1398
1399 Roo.bootstrap.Menu = function(config){
1400     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1401     if (this.registerMenu) {
1402         Roo.bootstrap.MenuMgr.register(this);
1403     }
1404     this.addEvents({
1405         /**
1406          * @event beforeshow
1407          * Fires before this menu is displayed
1408          * @param {Roo.menu.Menu} this
1409          */
1410         beforeshow : true,
1411         /**
1412          * @event beforehide
1413          * Fires before this menu is hidden
1414          * @param {Roo.menu.Menu} this
1415          */
1416         beforehide : true,
1417         /**
1418          * @event show
1419          * Fires after this menu is displayed
1420          * @param {Roo.menu.Menu} this
1421          */
1422         show : true,
1423         /**
1424          * @event hide
1425          * Fires after this menu is hidden
1426          * @param {Roo.menu.Menu} this
1427          */
1428         hide : true,
1429         /**
1430          * @event click
1431          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1432          * @param {Roo.menu.Menu} this
1433          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1434          * @param {Roo.EventObject} e
1435          */
1436         click : true,
1437         /**
1438          * @event mouseover
1439          * Fires when the mouse is hovering over this menu
1440          * @param {Roo.menu.Menu} this
1441          * @param {Roo.EventObject} e
1442          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1443          */
1444         mouseover : true,
1445         /**
1446          * @event mouseout
1447          * Fires when the mouse exits this menu
1448          * @param {Roo.menu.Menu} this
1449          * @param {Roo.EventObject} e
1450          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1451          */
1452         mouseout : true,
1453         /**
1454          * @event itemclick
1455          * Fires when a menu item contained in this menu is clicked
1456          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1457          * @param {Roo.EventObject} e
1458          */
1459         itemclick: true
1460     });
1461     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1462 };
1463
1464 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1465     
1466    /// html : false,
1467     //align : '',
1468     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1469     type: false,
1470     /**
1471      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1472      */
1473     registerMenu : true,
1474     
1475     menuItems :false, // stores the menu items..
1476     
1477     hidden:true,
1478     
1479     parentMenu : false,
1480     
1481     getChildContainer : function() {
1482         return this.el;  
1483     },
1484     
1485     getAutoCreate : function(){
1486          
1487         //if (['right'].indexOf(this.align)!==-1) {
1488         //    cfg.cn[1].cls += ' pull-right'
1489         //}
1490         
1491         
1492         var cfg = {
1493             tag : 'ul',
1494             cls : 'dropdown-menu' ,
1495             style : 'z-index:1000'
1496             
1497         }
1498         
1499         if (this.type === 'submenu') {
1500             cfg.cls = 'submenu active';
1501         }
1502         if (this.type === 'treeview') {
1503             cfg.cls = 'treeview-menu';
1504         }
1505         
1506         return cfg;
1507     },
1508     initEvents : function() {
1509         
1510        // Roo.log("ADD event");
1511        // Roo.log(this.triggerEl.dom);
1512         this.triggerEl.on('click', this.onTriggerPress, this);
1513         this.triggerEl.addClass('dropdown-toggle');
1514         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1515
1516         this.el.on("mouseover", this.onMouseOver, this);
1517         this.el.on("mouseout", this.onMouseOut, this);
1518         
1519         
1520     },
1521     findTargetItem : function(e){
1522         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1523         if(!t){
1524             return false;
1525         }
1526         //Roo.log(t);         Roo.log(t.id);
1527         if(t && t.id){
1528             //Roo.log(this.menuitems);
1529             return this.menuitems.get(t.id);
1530             
1531             //return this.items.get(t.menuItemId);
1532         }
1533         
1534         return false;
1535     },
1536     onClick : function(e){
1537         Roo.log("menu.onClick");
1538         var t = this.findTargetItem(e);
1539         if(!t){
1540             return;
1541         }
1542         Roo.log(e);
1543         /*
1544         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1545             if(t == this.activeItem && t.shouldDeactivate(e)){
1546                 this.activeItem.deactivate();
1547                 delete this.activeItem;
1548                 return;
1549             }
1550             if(t.canActivate){
1551                 this.setActiveItem(t, true);
1552             }
1553             return;
1554             
1555             
1556         }
1557         */
1558         Roo.log('pass click event');
1559         
1560         t.onClick(e);
1561         
1562         this.fireEvent("click", this, t, e);
1563         
1564         this.hide();
1565     },
1566      onMouseOver : function(e){
1567         var t  = this.findTargetItem(e);
1568         //Roo.log(t);
1569         //if(t){
1570         //    if(t.canActivate && !t.disabled){
1571         //        this.setActiveItem(t, true);
1572         //    }
1573         //}
1574         
1575         this.fireEvent("mouseover", this, e, t);
1576     },
1577     isVisible : function(){
1578         return !this.hidden;
1579     },
1580      onMouseOut : function(e){
1581         var t  = this.findTargetItem(e);
1582         
1583         //if(t ){
1584         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1585         //        this.activeItem.deactivate();
1586         //        delete this.activeItem;
1587         //    }
1588         //}
1589         this.fireEvent("mouseout", this, e, t);
1590     },
1591     
1592     
1593     /**
1594      * Displays this menu relative to another element
1595      * @param {String/HTMLElement/Roo.Element} element The element to align to
1596      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1597      * the element (defaults to this.defaultAlign)
1598      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1599      */
1600     show : function(el, pos, parentMenu){
1601         this.parentMenu = parentMenu;
1602         if(!this.el){
1603             this.render();
1604         }
1605         this.fireEvent("beforeshow", this);
1606         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1607     },
1608      /**
1609      * Displays this menu at a specific xy position
1610      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1611      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1612      */
1613     showAt : function(xy, parentMenu, /* private: */_e){
1614         this.parentMenu = parentMenu;
1615         if(!this.el){
1616             this.render();
1617         }
1618         if(_e !== false){
1619             this.fireEvent("beforeshow", this);
1620             
1621             //xy = this.el.adjustForConstraints(xy);
1622         }
1623         //this.el.setXY(xy);
1624         //this.el.show();
1625         this.hideMenuItems();
1626         this.hidden = false;
1627         this.triggerEl.addClass('open');
1628         this.focus();
1629         this.fireEvent("show", this);
1630     },
1631     
1632     focus : function(){
1633         return;
1634         if(!this.hidden){
1635             this.doFocus.defer(50, this);
1636         }
1637     },
1638
1639     doFocus : function(){
1640         if(!this.hidden){
1641             this.focusEl.focus();
1642         }
1643     },
1644
1645     /**
1646      * Hides this menu and optionally all parent menus
1647      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1648      */
1649     hide : function(deep){
1650         
1651         this.hideMenuItems();
1652         if(this.el && this.isVisible()){
1653             this.fireEvent("beforehide", this);
1654             if(this.activeItem){
1655                 this.activeItem.deactivate();
1656                 this.activeItem = null;
1657             }
1658             this.triggerEl.removeClass('open');;
1659             this.hidden = true;
1660             this.fireEvent("hide", this);
1661         }
1662         if(deep === true && this.parentMenu){
1663             this.parentMenu.hide(true);
1664         }
1665     },
1666     
1667     onTriggerPress  : function(e)
1668     {
1669         
1670         Roo.log('trigger press');
1671         //Roo.log(e.getTarget());
1672        // Roo.log(this.triggerEl.dom);
1673         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1674             return;
1675         }
1676         if (this.isVisible()) {
1677             Roo.log('hide');
1678             this.hide();
1679         } else {
1680             this.show(this.triggerEl, false, false);
1681         }
1682         
1683         
1684     },
1685     
1686          
1687        
1688     
1689     hideMenuItems : function()
1690     {
1691         //$(backdrop).remove()
1692         Roo.select('.open',true).each(function(aa) {
1693             
1694             aa.removeClass('open');
1695           //var parent = getParent($(this))
1696           //var relatedTarget = { relatedTarget: this }
1697           
1698            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1699           //if (e.isDefaultPrevented()) return
1700            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1701         })
1702     },
1703     addxtypeChild : function (tree, cntr) {
1704         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1705           
1706         this.menuitems.add(comp);
1707         return comp;
1708
1709     },
1710     getEl : function()
1711     {
1712         Roo.log(this.el);
1713         return this.el;
1714     }
1715 });
1716
1717  
1718  /*
1719  * - LGPL
1720  *
1721  * menu item
1722  * 
1723  */
1724
1725
1726 /**
1727  * @class Roo.bootstrap.MenuItem
1728  * @extends Roo.bootstrap.Component
1729  * Bootstrap MenuItem class
1730  * @cfg {String} html the menu label
1731  * @cfg {String} href the link
1732  * @cfg {Boolean} preventDefault (true | false) default true
1733  * 
1734  * 
1735  * @constructor
1736  * Create a new MenuItem
1737  * @param {Object} config The config object
1738  */
1739
1740
1741 Roo.bootstrap.MenuItem = function(config){
1742     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1743     this.addEvents({
1744         // raw events
1745         /**
1746          * @event click
1747          * The raw click event for the entire grid.
1748          * @param {Roo.EventObject} e
1749          */
1750         "click" : true
1751     });
1752 };
1753
1754 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1755     
1756     href : false,
1757     html : false,
1758     preventDefault: true,
1759     
1760     getAutoCreate : function(){
1761         var cfg= {
1762             tag: 'li',
1763             cls: 'dropdown-menu-item',
1764             cn: [
1765                     {
1766                         tag : 'a',
1767                         href : '#',
1768                         html : 'Link'
1769                     }
1770                 ]
1771         };
1772         if (this.parent().type == 'treeview') {
1773             cfg.cls = 'treeview-menu';
1774         }
1775         
1776         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1777         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1778         return cfg;
1779     },
1780     
1781     initEvents: function() {
1782         
1783         //this.el.select('a').on('click', this.onClick, this);
1784         
1785     },
1786     onClick : function(e)
1787     {
1788         Roo.log('item on click ');
1789         //if(this.preventDefault){
1790         //    e.preventDefault();
1791         //}
1792         //this.parent().hideMenuItems();
1793         
1794         this.fireEvent('click', this, e);
1795     },
1796     getEl : function()
1797     {
1798         return this.el;
1799     }
1800 });
1801
1802  
1803
1804  /*
1805  * - LGPL
1806  *
1807  * menu separator
1808  * 
1809  */
1810
1811
1812 /**
1813  * @class Roo.bootstrap.MenuSeparator
1814  * @extends Roo.bootstrap.Component
1815  * Bootstrap MenuSeparator class
1816  * 
1817  * @constructor
1818  * Create a new MenuItem
1819  * @param {Object} config The config object
1820  */
1821
1822
1823 Roo.bootstrap.MenuSeparator = function(config){
1824     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1825 };
1826
1827 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1828     
1829     getAutoCreate : function(){
1830         var cfg = {
1831             cls: 'divider',
1832             tag : 'li'
1833         };
1834         
1835         return cfg;
1836     }
1837    
1838 });
1839
1840  
1841
1842  
1843 /*
1844 <div class="modal fade">
1845   <div class="modal-dialog">
1846     <div class="modal-content">
1847       <div class="modal-header">
1848         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1849         <h4 class="modal-title">Modal title</h4>
1850       </div>
1851       <div class="modal-body">
1852         <p>One fine body&hellip;</p>
1853       </div>
1854       <div class="modal-footer">
1855         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1856         <button type="button" class="btn btn-primary">Save changes</button>
1857       </div>
1858     </div><!-- /.modal-content -->
1859   </div><!-- /.modal-dialog -->
1860 </div><!-- /.modal -->
1861 */
1862 /*
1863  * - LGPL
1864  *
1865  * page contgainer.
1866  * 
1867  */
1868
1869 /**
1870  * @class Roo.bootstrap.Modal
1871  * @extends Roo.bootstrap.Component
1872  * Bootstrap Modal class
1873  * @cfg {String} title Title of dialog
1874  * @cfg {Boolean} specificTitle (true|false) default false
1875  * @cfg {Array} buttons Array of buttons or standard button set..
1876  * @cfg {String} buttonPosition (left|right|center) default right
1877  * 
1878  * @constructor
1879  * Create a new Modal Dialog
1880  * @param {Object} config The config object
1881  */
1882
1883 Roo.bootstrap.Modal = function(config){
1884     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1885     this.addEvents({
1886         // raw events
1887         /**
1888          * @event btnclick
1889          * The raw btnclick event for the button
1890          * @param {Roo.EventObject} e
1891          */
1892         "btnclick" : true
1893     });
1894     this.buttons = this.buttons || [];
1895 };
1896
1897 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1898     
1899     title : 'test dialog',
1900    
1901     buttons : false,
1902     
1903     // set on load...
1904     body:  false,
1905     
1906     specificTitle: false,
1907     
1908     buttonPosition: 'right',
1909     
1910     onRender : function(ct, position)
1911     {
1912         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1913      
1914         if(!this.el){
1915             var cfg = Roo.apply({},  this.getAutoCreate());
1916             cfg.id = Roo.id();
1917             //if(!cfg.name){
1918             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1919             //}
1920             //if (!cfg.name.length) {
1921             //    delete cfg.name;
1922            // }
1923             if (this.cls) {
1924                 cfg.cls += ' ' + this.cls;
1925             }
1926             if (this.style) {
1927                 cfg.style = this.style;
1928             }
1929             this.el = Roo.get(document.body).createChild(cfg, position);
1930         }
1931         //var type = this.el.dom.type;
1932         
1933         if(this.tabIndex !== undefined){
1934             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1935         }
1936         
1937         
1938         
1939         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1940         this.maskEl.enableDisplayMode("block");
1941         this.maskEl.hide();
1942         //this.el.addClass("x-dlg-modal");
1943     
1944         if (this.buttons.length) {
1945             Roo.each(this.buttons, function(bb) {
1946                 b = Roo.apply({}, bb);
1947                 b.xns = b.xns || Roo.bootstrap;
1948                 b.xtype = b.xtype || 'Button';
1949                 if (typeof(b.listeners) == 'undefined') {
1950                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1951                 }
1952                 
1953                 var btn = Roo.factory(b);
1954                 
1955                 btn.onRender(this.el.select('.modal-footer div').first());
1956                 
1957             },this);
1958         }
1959         // render the children.
1960         var nitems = [];
1961         
1962         if(typeof(this.items) != 'undefined'){
1963             var items = this.items;
1964             delete this.items;
1965
1966             for(var i =0;i < items.length;i++) {
1967                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1968             }
1969         }
1970         
1971         this.items = nitems;
1972         
1973         this.body = this.el.select('.modal-body',true).first();
1974         this.close = this.el.select('.modal-header .close', true).first();
1975         this.footer = this.el.select('.modal-footer',true).first();
1976         this.initEvents();
1977         //this.el.addClass([this.fieldClass, this.cls]);
1978         
1979     },
1980     getAutoCreate : function(){
1981         
1982         
1983         var bdy = {
1984                 cls : 'modal-body',
1985                 html : this.html || ''
1986         };
1987         
1988         var title = {
1989             tag: 'h4',
1990             cls : 'modal-title',
1991             html : this.title
1992         };
1993         
1994         if(this.specificTitle){
1995             title = this.title;
1996         };
1997         
1998         return modal = {
1999             cls: "modal fade",
2000             style : 'display: none',
2001             cn : [
2002                 {
2003                     cls: "modal-dialog",
2004                     cn : [
2005                         {
2006                             cls : "modal-content",
2007                             cn : [
2008                                 {
2009                                     cls : 'modal-header',
2010                                     cn : [
2011                                         {
2012                                             tag: 'button',
2013                                             cls : 'close',
2014                                             html : '&times'
2015                                         },
2016                                         title
2017                                     ]
2018                                 },
2019                                 bdy,
2020                                 {
2021                                     cls : 'modal-footer',
2022                                     cn : [
2023                                         {
2024                                             tag: 'div',
2025                                             cls: 'btn-' + this.buttonPosition
2026                                         }
2027                                     ]
2028                                     
2029                                 }
2030                                 
2031                                 
2032                             ]
2033                             
2034                         }
2035                     ]
2036                         
2037                 }
2038             ]
2039             
2040             
2041         };
2042           
2043     },
2044     getChildContainer : function() {
2045          
2046          return this.el.select('.modal-body',true).first();
2047         
2048     },
2049     getButtonContainer : function() {
2050          return this.el.select('.modal-footer div',true).first();
2051         
2052     },
2053     initEvents : function()
2054     {
2055         this.el.select('.modal-header .close').on('click', this.hide, this);
2056 //        
2057 //        this.addxtype(this);
2058     },
2059     show : function() {
2060         
2061         if (!this.rendered) {
2062             this.render();
2063         }
2064        
2065         this.el.addClass('on');
2066         this.el.removeClass('fade');
2067         this.el.setStyle('display', 'block');
2068         Roo.get(document.body).addClass("x-body-masked");
2069         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2070         this.maskEl.show();
2071         this.el.setStyle('zIndex', '10001');
2072         this.fireEvent('show', this);
2073         
2074         
2075     },
2076     hide : function()
2077     {
2078         Roo.log('Modal hide?!');
2079         this.maskEl.hide();
2080         Roo.get(document.body).removeClass("x-body-masked");
2081         this.el.removeClass('on');
2082         this.el.addClass('fade');
2083         this.el.setStyle('display', 'none');
2084         this.fireEvent('hide', this);
2085     },
2086     
2087     addButton : function(str, cb)
2088     {
2089          
2090         
2091         var b = Roo.apply({}, { html : str } );
2092         b.xns = b.xns || Roo.bootstrap;
2093         b.xtype = b.xtype || 'Button';
2094         if (typeof(b.listeners) == 'undefined') {
2095             b.listeners = { click : cb.createDelegate(this)  };
2096         }
2097         
2098         var btn = Roo.factory(b);
2099            
2100         btn.onRender(this.el.select('.modal-footer div').first());
2101         
2102         return btn;   
2103        
2104     },
2105     
2106     setDefaultButton : function(btn)
2107     {
2108         //this.el.select('.modal-footer').()
2109     },
2110     resizeTo: function(w,h)
2111     {
2112         // skip..
2113     },
2114     setContentSize  : function(w, h)
2115     {
2116         
2117     },
2118     onButtonClick: function(btn,e)
2119     {
2120         //Roo.log([a,b,c]);
2121         this.fireEvent('btnclick', btn.name, e);
2122     },
2123     setTitle: function(str) {
2124         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2125         
2126     }
2127 });
2128
2129
2130 Roo.apply(Roo.bootstrap.Modal,  {
2131     /**
2132          * Button config that displays a single OK button
2133          * @type Object
2134          */
2135         OK :  [{
2136             name : 'ok',
2137             weight : 'primary',
2138             html : 'OK'
2139         }], 
2140         /**
2141          * Button config that displays Yes and No buttons
2142          * @type Object
2143          */
2144         YESNO : [
2145             {
2146                 name  : 'no',
2147                 html : 'No'
2148             },
2149             {
2150                 name  :'yes',
2151                 weight : 'primary',
2152                 html : 'Yes'
2153             }
2154         ],
2155         
2156         /**
2157          * Button config that displays OK and Cancel buttons
2158          * @type Object
2159          */
2160         OKCANCEL : [
2161             {
2162                name : 'cancel',
2163                 html : 'Cancel'
2164             },
2165             {
2166                 name : 'ok',
2167                 weight : 'primary',
2168                 html : 'OK'
2169             }
2170         ],
2171         /**
2172          * Button config that displays Yes, No and Cancel buttons
2173          * @type Object
2174          */
2175         YESNOCANCEL : [
2176             {
2177                 name : 'yes',
2178                 weight : 'primary',
2179                 html : 'Yes'
2180             },
2181             {
2182                 name : 'no',
2183                 html : 'No'
2184             },
2185             {
2186                 name : 'cancel',
2187                 html : 'Cancel'
2188             }
2189         ]
2190 });
2191  /*
2192  * - LGPL
2193  *
2194  * messagebox - can be used as a replace
2195  * 
2196  */
2197 /**
2198  * @class Roo.MessageBox
2199  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2200  * Example usage:
2201  *<pre><code>
2202 // Basic alert:
2203 Roo.Msg.alert('Status', 'Changes saved successfully.');
2204
2205 // Prompt for user data:
2206 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2207     if (btn == 'ok'){
2208         // process text value...
2209     }
2210 });
2211
2212 // Show a dialog using config options:
2213 Roo.Msg.show({
2214    title:'Save Changes?',
2215    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2216    buttons: Roo.Msg.YESNOCANCEL,
2217    fn: processResult,
2218    animEl: 'elId'
2219 });
2220 </code></pre>
2221  * @singleton
2222  */
2223 Roo.bootstrap.MessageBox = function(){
2224     var dlg, opt, mask, waitTimer;
2225     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2226     var buttons, activeTextEl, bwidth;
2227
2228     
2229     // private
2230     var handleButton = function(button){
2231         dlg.hide();
2232         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2233     };
2234
2235     // private
2236     var handleHide = function(){
2237         if(opt && opt.cls){
2238             dlg.el.removeClass(opt.cls);
2239         }
2240         //if(waitTimer){
2241         //    Roo.TaskMgr.stop(waitTimer);
2242         //    waitTimer = null;
2243         //}
2244     };
2245
2246     // private
2247     var updateButtons = function(b){
2248         var width = 0;
2249         if(!b){
2250             buttons["ok"].hide();
2251             buttons["cancel"].hide();
2252             buttons["yes"].hide();
2253             buttons["no"].hide();
2254             //dlg.footer.dom.style.display = 'none';
2255             return width;
2256         }
2257         dlg.footer.dom.style.display = '';
2258         for(var k in buttons){
2259             if(typeof buttons[k] != "function"){
2260                 if(b[k]){
2261                     buttons[k].show();
2262                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2263                     width += buttons[k].el.getWidth()+15;
2264                 }else{
2265                     buttons[k].hide();
2266                 }
2267             }
2268         }
2269         return width;
2270     };
2271
2272     // private
2273     var handleEsc = function(d, k, e){
2274         if(opt && opt.closable !== false){
2275             dlg.hide();
2276         }
2277         if(e){
2278             e.stopEvent();
2279         }
2280     };
2281
2282     return {
2283         /**
2284          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2285          * @return {Roo.BasicDialog} The BasicDialog element
2286          */
2287         getDialog : function(){
2288            if(!dlg){
2289                 dlg = new Roo.bootstrap.Modal( {
2290                     //draggable: true,
2291                     //resizable:false,
2292                     //constraintoviewport:false,
2293                     //fixedcenter:true,
2294                     //collapsible : false,
2295                     //shim:true,
2296                     //modal: true,
2297                   //  width:400,
2298                   //  height:100,
2299                     //buttonAlign:"center",
2300                     closeClick : function(){
2301                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2302                             handleButton("no");
2303                         }else{
2304                             handleButton("cancel");
2305                         }
2306                     }
2307                 });
2308                 dlg.render();
2309                 dlg.on("hide", handleHide);
2310                 mask = dlg.mask;
2311                 //dlg.addKeyListener(27, handleEsc);
2312                 buttons = {};
2313                 this.buttons = buttons;
2314                 var bt = this.buttonText;
2315                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2316                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2317                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2318                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2319                 Roo.log(buttons)
2320                 bodyEl = dlg.body.createChild({
2321
2322                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2323                         '<textarea class="roo-mb-textarea"></textarea>' +
2324                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2325                 });
2326                 msgEl = bodyEl.dom.firstChild;
2327                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2328                 textboxEl.enableDisplayMode();
2329                 textboxEl.addKeyListener([10,13], function(){
2330                     if(dlg.isVisible() && opt && opt.buttons){
2331                         if(opt.buttons.ok){
2332                             handleButton("ok");
2333                         }else if(opt.buttons.yes){
2334                             handleButton("yes");
2335                         }
2336                     }
2337                 });
2338                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2339                 textareaEl.enableDisplayMode();
2340                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2341                 progressEl.enableDisplayMode();
2342                 var pf = progressEl.dom.firstChild;
2343                 if (pf) {
2344                     pp = Roo.get(pf.firstChild);
2345                     pp.setHeight(pf.offsetHeight);
2346                 }
2347                 
2348             }
2349             return dlg;
2350         },
2351
2352         /**
2353          * Updates the message box body text
2354          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2355          * the XHTML-compliant non-breaking space character '&amp;#160;')
2356          * @return {Roo.MessageBox} This message box
2357          */
2358         updateText : function(text){
2359             if(!dlg.isVisible() && !opt.width){
2360                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2361             }
2362             msgEl.innerHTML = text || '&#160;';
2363       
2364             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2365             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2366             var w = Math.max(
2367                     Math.min(opt.width || cw , this.maxWidth), 
2368                     Math.max(opt.minWidth || this.minWidth, bwidth)
2369             );
2370             if(opt.prompt){
2371                 activeTextEl.setWidth(w);
2372             }
2373             if(dlg.isVisible()){
2374                 dlg.fixedcenter = false;
2375             }
2376             // to big, make it scroll. = But as usual stupid IE does not support
2377             // !important..
2378             
2379             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2380                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2381                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2382             } else {
2383                 bodyEl.dom.style.height = '';
2384                 bodyEl.dom.style.overflowY = '';
2385             }
2386             if (cw > w) {
2387                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2388             } else {
2389                 bodyEl.dom.style.overflowX = '';
2390             }
2391             
2392             dlg.setContentSize(w, bodyEl.getHeight());
2393             if(dlg.isVisible()){
2394                 dlg.fixedcenter = true;
2395             }
2396             return this;
2397         },
2398
2399         /**
2400          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2401          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2402          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2403          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2404          * @return {Roo.MessageBox} This message box
2405          */
2406         updateProgress : function(value, text){
2407             if(text){
2408                 this.updateText(text);
2409             }
2410             if (pp) { // weird bug on my firefox - for some reason this is not defined
2411                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2412             }
2413             return this;
2414         },        
2415
2416         /**
2417          * Returns true if the message box is currently displayed
2418          * @return {Boolean} True if the message box is visible, else false
2419          */
2420         isVisible : function(){
2421             return dlg && dlg.isVisible();  
2422         },
2423
2424         /**
2425          * Hides the message box if it is displayed
2426          */
2427         hide : function(){
2428             if(this.isVisible()){
2429                 dlg.hide();
2430             }  
2431         },
2432
2433         /**
2434          * Displays a new message box, or reinitializes an existing message box, based on the config options
2435          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2436          * The following config object properties are supported:
2437          * <pre>
2438 Property    Type             Description
2439 ----------  ---------------  ------------------------------------------------------------------------------------
2440 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2441                                    closes (defaults to undefined)
2442 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2443                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2444 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2445                                    progress and wait dialogs will ignore this property and always hide the
2446                                    close button as they can only be closed programmatically.
2447 cls               String           A custom CSS class to apply to the message box element
2448 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2449                                    displayed (defaults to 75)
2450 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2451                                    function will be btn (the name of the button that was clicked, if applicable,
2452                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2453                                    Progress and wait dialogs will ignore this option since they do not respond to
2454                                    user actions and can only be closed programmatically, so any required function
2455                                    should be called by the same code after it closes the dialog.
2456 icon              String           A CSS class that provides a background image to be used as an icon for
2457                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2458 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2459 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2460 modal             Boolean          False to allow user interaction with the page while the message box is
2461                                    displayed (defaults to true)
2462 msg               String           A string that will replace the existing message box body text (defaults
2463                                    to the XHTML-compliant non-breaking space character '&#160;')
2464 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2465 progress          Boolean          True to display a progress bar (defaults to false)
2466 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2467 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2468 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2469 title             String           The title text
2470 value             String           The string value to set into the active textbox element if displayed
2471 wait              Boolean          True to display a progress bar (defaults to false)
2472 width             Number           The width of the dialog in pixels
2473 </pre>
2474          *
2475          * Example usage:
2476          * <pre><code>
2477 Roo.Msg.show({
2478    title: 'Address',
2479    msg: 'Please enter your address:',
2480    width: 300,
2481    buttons: Roo.MessageBox.OKCANCEL,
2482    multiline: true,
2483    fn: saveAddress,
2484    animEl: 'addAddressBtn'
2485 });
2486 </code></pre>
2487          * @param {Object} config Configuration options
2488          * @return {Roo.MessageBox} This message box
2489          */
2490         show : function(options)
2491         {
2492             
2493             // this causes nightmares if you show one dialog after another
2494             // especially on callbacks..
2495              
2496             if(this.isVisible()){
2497                 
2498                 this.hide();
2499                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2500                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2501                 Roo.log("New Dialog Message:" +  options.msg )
2502                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2503                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2504                 
2505             }
2506             var d = this.getDialog();
2507             opt = options;
2508             d.setTitle(opt.title || "&#160;");
2509             d.close.setDisplayed(opt.closable !== false);
2510             activeTextEl = textboxEl;
2511             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2512             if(opt.prompt){
2513                 if(opt.multiline){
2514                     textboxEl.hide();
2515                     textareaEl.show();
2516                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2517                         opt.multiline : this.defaultTextHeight);
2518                     activeTextEl = textareaEl;
2519                 }else{
2520                     textboxEl.show();
2521                     textareaEl.hide();
2522                 }
2523             }else{
2524                 textboxEl.hide();
2525                 textareaEl.hide();
2526             }
2527             progressEl.setDisplayed(opt.progress === true);
2528             this.updateProgress(0);
2529             activeTextEl.dom.value = opt.value || "";
2530             if(opt.prompt){
2531                 dlg.setDefaultButton(activeTextEl);
2532             }else{
2533                 var bs = opt.buttons;
2534                 var db = null;
2535                 if(bs && bs.ok){
2536                     db = buttons["ok"];
2537                 }else if(bs && bs.yes){
2538                     db = buttons["yes"];
2539                 }
2540                 dlg.setDefaultButton(db);
2541             }
2542             bwidth = updateButtons(opt.buttons);
2543             this.updateText(opt.msg);
2544             if(opt.cls){
2545                 d.el.addClass(opt.cls);
2546             }
2547             d.proxyDrag = opt.proxyDrag === true;
2548             d.modal = opt.modal !== false;
2549             d.mask = opt.modal !== false ? mask : false;
2550             if(!d.isVisible()){
2551                 // force it to the end of the z-index stack so it gets a cursor in FF
2552                 document.body.appendChild(dlg.el.dom);
2553                 d.animateTarget = null;
2554                 d.show(options.animEl);
2555             }
2556             return this;
2557         },
2558
2559         /**
2560          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2561          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2562          * and closing the message box when the process is complete.
2563          * @param {String} title The title bar text
2564          * @param {String} msg The message box body text
2565          * @return {Roo.MessageBox} This message box
2566          */
2567         progress : function(title, msg){
2568             this.show({
2569                 title : title,
2570                 msg : msg,
2571                 buttons: false,
2572                 progress:true,
2573                 closable:false,
2574                 minWidth: this.minProgressWidth,
2575                 modal : true
2576             });
2577             return this;
2578         },
2579
2580         /**
2581          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2582          * If a callback function is passed it will be called after the user clicks the button, and the
2583          * id of the button that was clicked will be passed as the only parameter to the callback
2584          * (could also be the top-right close button).
2585          * @param {String} title The title bar text
2586          * @param {String} msg The message box body text
2587          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2588          * @param {Object} scope (optional) The scope of the callback function
2589          * @return {Roo.MessageBox} This message box
2590          */
2591         alert : function(title, msg, fn, scope){
2592             this.show({
2593                 title : title,
2594                 msg : msg,
2595                 buttons: this.OK,
2596                 fn: fn,
2597                 scope : scope,
2598                 modal : true
2599             });
2600             return this;
2601         },
2602
2603         /**
2604          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2605          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2606          * You are responsible for closing the message box when the process is complete.
2607          * @param {String} msg The message box body text
2608          * @param {String} title (optional) The title bar text
2609          * @return {Roo.MessageBox} This message box
2610          */
2611         wait : function(msg, title){
2612             this.show({
2613                 title : title,
2614                 msg : msg,
2615                 buttons: false,
2616                 closable:false,
2617                 progress:true,
2618                 modal:true,
2619                 width:300,
2620                 wait:true
2621             });
2622             waitTimer = Roo.TaskMgr.start({
2623                 run: function(i){
2624                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2625                 },
2626                 interval: 1000
2627             });
2628             return this;
2629         },
2630
2631         /**
2632          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2633          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2634          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2635          * @param {String} title The title bar text
2636          * @param {String} msg The message box body text
2637          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2638          * @param {Object} scope (optional) The scope of the callback function
2639          * @return {Roo.MessageBox} This message box
2640          */
2641         confirm : function(title, msg, fn, scope){
2642             this.show({
2643                 title : title,
2644                 msg : msg,
2645                 buttons: this.YESNO,
2646                 fn: fn,
2647                 scope : scope,
2648                 modal : true
2649             });
2650             return this;
2651         },
2652
2653         /**
2654          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2655          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2656          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2657          * (could also be the top-right close button) and the text that was entered will be passed as the two
2658          * parameters to the callback.
2659          * @param {String} title The title bar text
2660          * @param {String} msg The message box body text
2661          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2662          * @param {Object} scope (optional) The scope of the callback function
2663          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2664          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2665          * @return {Roo.MessageBox} This message box
2666          */
2667         prompt : function(title, msg, fn, scope, multiline){
2668             this.show({
2669                 title : title,
2670                 msg : msg,
2671                 buttons: this.OKCANCEL,
2672                 fn: fn,
2673                 minWidth:250,
2674                 scope : scope,
2675                 prompt:true,
2676                 multiline: multiline,
2677                 modal : true
2678             });
2679             return this;
2680         },
2681
2682         /**
2683          * Button config that displays a single OK button
2684          * @type Object
2685          */
2686         OK : {ok:true},
2687         /**
2688          * Button config that displays Yes and No buttons
2689          * @type Object
2690          */
2691         YESNO : {yes:true, no:true},
2692         /**
2693          * Button config that displays OK and Cancel buttons
2694          * @type Object
2695          */
2696         OKCANCEL : {ok:true, cancel:true},
2697         /**
2698          * Button config that displays Yes, No and Cancel buttons
2699          * @type Object
2700          */
2701         YESNOCANCEL : {yes:true, no:true, cancel:true},
2702
2703         /**
2704          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2705          * @type Number
2706          */
2707         defaultTextHeight : 75,
2708         /**
2709          * The maximum width in pixels of the message box (defaults to 600)
2710          * @type Number
2711          */
2712         maxWidth : 600,
2713         /**
2714          * The minimum width in pixels of the message box (defaults to 100)
2715          * @type Number
2716          */
2717         minWidth : 100,
2718         /**
2719          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2720          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2721          * @type Number
2722          */
2723         minProgressWidth : 250,
2724         /**
2725          * An object containing the default button text strings that can be overriden for localized language support.
2726          * Supported properties are: ok, cancel, yes and no.
2727          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2728          * @type Object
2729          */
2730         buttonText : {
2731             ok : "OK",
2732             cancel : "Cancel",
2733             yes : "Yes",
2734             no : "No"
2735         }
2736     };
2737 }();
2738
2739 /**
2740  * Shorthand for {@link Roo.MessageBox}
2741  */
2742 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2743 Roo.Msg = Roo.Msg || Roo.MessageBox;
2744 /*
2745  * - LGPL
2746  *
2747  * navbar
2748  * 
2749  */
2750
2751 /**
2752  * @class Roo.bootstrap.Navbar
2753  * @extends Roo.bootstrap.Component
2754  * Bootstrap Navbar class
2755
2756  * @constructor
2757  * Create a new Navbar
2758  * @param {Object} config The config object
2759  */
2760
2761
2762 Roo.bootstrap.Navbar = function(config){
2763     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2764     
2765 };
2766
2767 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2768     
2769     
2770    
2771     // private
2772     navItems : false,
2773     loadMask : false,
2774     
2775     
2776     getAutoCreate : function(){
2777         
2778         
2779         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2780         
2781     },
2782     
2783     initEvents :function ()
2784     {
2785         //Roo.log(this.el.select('.navbar-toggle',true));
2786         this.el.select('.navbar-toggle',true).on('click', function() {
2787            // Roo.log('click');
2788             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2789         }, this);
2790         
2791         var mark = {
2792             tag: "div",
2793             cls:"x-dlg-mask"
2794         }
2795         
2796         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2797         
2798         var size = this.el.getSize();
2799         this.maskEl.setSize(size.width, size.height);
2800         this.maskEl.enableDisplayMode("block");
2801         this.maskEl.hide();
2802         
2803         if(this.loadMask){
2804             this.maskEl.show();
2805         }
2806     },
2807     
2808     
2809     getChildContainer : function()
2810     {
2811         if (this.el.select('.collapse').getCount()) {
2812             return this.el.select('.collapse',true).first();
2813         }
2814         
2815         return this.el;
2816     },
2817     
2818     mask : function()
2819     {
2820         this.maskEl.show();
2821     },
2822     
2823     unmask : function()
2824     {
2825         this.maskEl.hide();
2826     } 
2827     
2828     
2829     
2830     
2831 });
2832
2833
2834
2835  
2836
2837  /*
2838  * - LGPL
2839  *
2840  * navbar
2841  * 
2842  */
2843
2844 /**
2845  * @class Roo.bootstrap.NavSimplebar
2846  * @extends Roo.bootstrap.Navbar
2847  * Bootstrap Sidebar class
2848  *
2849  * @cfg {Boolean} inverse is inverted color
2850  * 
2851  * @cfg {String} type (nav | pills | tabs)
2852  * @cfg {Boolean} arrangement stacked | justified
2853  * @cfg {String} align (left | right) alignment
2854  * 
2855  * @cfg {Boolean} main (true|false) main nav bar? default false
2856  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2857  * 
2858  * @cfg {String} tag (header|footer|nav|div) default is nav 
2859
2860  * 
2861  * 
2862  * 
2863  * @constructor
2864  * Create a new Sidebar
2865  * @param {Object} config The config object
2866  */
2867
2868
2869 Roo.bootstrap.NavSimplebar = function(config){
2870     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2871 };
2872
2873 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2874     
2875     inverse: false,
2876     
2877     type: false,
2878     arrangement: '',
2879     align : false,
2880     
2881     
2882     
2883     main : false,
2884     
2885     
2886     tag : false,
2887     
2888     
2889     getAutoCreate : function(){
2890         
2891         
2892         var cfg = {
2893             tag : this.tag || 'div',
2894             cls : 'navbar'
2895         };
2896           
2897         
2898         cfg.cn = [
2899             {
2900                 cls: 'nav',
2901                 tag : 'ul'
2902             }
2903         ];
2904         
2905          
2906         this.type = this.type || 'nav';
2907         if (['tabs','pills'].indexOf(this.type)!==-1) {
2908             cfg.cn[0].cls += ' nav-' + this.type
2909         
2910         
2911         } else {
2912             if (this.type!=='nav') {
2913                 Roo.log('nav type must be nav/tabs/pills')
2914             }
2915             cfg.cn[0].cls += ' navbar-nav'
2916         }
2917         
2918         
2919         
2920         
2921         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2922             cfg.cn[0].cls += ' nav-' + this.arrangement;
2923         }
2924         
2925         
2926         if (this.align === 'right') {
2927             cfg.cn[0].cls += ' navbar-right';
2928         }
2929         
2930         if (this.inverse) {
2931             cfg.cls += ' navbar-inverse';
2932             
2933         }
2934         
2935         
2936         return cfg;
2937     
2938         
2939     }
2940     
2941     
2942     
2943 });
2944
2945
2946
2947  
2948
2949  
2950        /*
2951  * - LGPL
2952  *
2953  * navbar
2954  * 
2955  */
2956
2957 /**
2958  * @class Roo.bootstrap.NavHeaderbar
2959  * @extends Roo.bootstrap.NavSimplebar
2960  * Bootstrap Sidebar class
2961  *
2962  * @cfg {String} brand what is brand
2963  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2964  * @cfg {String} brand_href href of the brand
2965  * 
2966  * @constructor
2967  * Create a new Sidebar
2968  * @param {Object} config The config object
2969  */
2970
2971
2972 Roo.bootstrap.NavHeaderbar = function(config){
2973     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
2974 };
2975
2976 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
2977     
2978     position: '',
2979     brand: '',
2980     brand_href: false,
2981     
2982     
2983     getAutoCreate : function(){
2984         
2985         
2986         
2987         var   cfg = {
2988             tag: this.nav || 'nav',
2989             cls: 'navbar',
2990             role: 'navigation',
2991             cn: [
2992                 {
2993                     tag: 'div',
2994                     cls: 'navbar-header',
2995                     cn: [
2996                         {
2997                         tag: 'button',
2998                         type: 'button',
2999                         cls: 'navbar-toggle',
3000                         'data-toggle': 'collapse',
3001                         cn: [
3002                             {
3003                                 tag: 'span',
3004                                 cls: 'sr-only',
3005                                 html: 'Toggle navigation'
3006                             },
3007                             {
3008                                 tag: 'span',
3009                                 cls: 'icon-bar'
3010                             },
3011                             {
3012                                 tag: 'span',
3013                                 cls: 'icon-bar'
3014                             },
3015                             {
3016                                 tag: 'span',
3017                                 cls: 'icon-bar'
3018                             }
3019                         ]
3020                         }
3021                     ]
3022                 },
3023                 {
3024                 tag: 'div',
3025                 cls: 'collapse navbar-collapse'
3026                 }
3027             ]
3028         };
3029         
3030         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3031         
3032         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3033             cfg.cls += ' navbar-' + this.position;
3034             
3035             // tag can override this..
3036             
3037             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3038         }
3039         
3040         if (this.brand !== '') {
3041             cfg.cn[0].cn.push({
3042                 tag: 'a',
3043                 href: this.brand_href ? this.brand_href : '#',
3044                 cls: 'navbar-brand',
3045                 cn: [
3046                 this.brand
3047                 ]
3048             });
3049         }
3050         
3051         if(this.main){
3052             cfg.cls += ' main-nav';
3053         }
3054         
3055         
3056         return cfg;
3057
3058         
3059     }
3060     
3061     
3062     
3063 });
3064
3065
3066
3067  
3068
3069  /*
3070  * - LGPL
3071  *
3072  * navbar
3073  * 
3074  */
3075
3076 /**
3077  * @class Roo.bootstrap.NavSidebar
3078  * @extends Roo.bootstrap.Navbar
3079  * Bootstrap Sidebar class
3080  * 
3081  * @constructor
3082  * Create a new Sidebar
3083  * @param {Object} config The config object
3084  */
3085
3086
3087 Roo.bootstrap.NavSidebar = function(config){
3088     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3089 };
3090
3091 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3092     
3093     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3094     
3095     getAutoCreate : function(){
3096         
3097         
3098         return  {
3099             tag: 'div',
3100             cls: 'sidebar sidebar-nav'
3101         };
3102     
3103         
3104     }
3105     
3106     
3107     
3108 });
3109
3110
3111
3112  
3113
3114  /*
3115  * - LGPL
3116  *
3117  * nav group
3118  * 
3119  */
3120
3121 /**
3122  * @class Roo.bootstrap.NavGroup
3123  * @extends Roo.bootstrap.Component
3124  * Bootstrap NavGroup class
3125  * @cfg {String} align left | right
3126  * @cfg {Boolean} inverse false | true
3127  * @cfg {String} type (nav|pills|tab) default nav
3128  * @cfg {String} navId - reference Id for navbar.
3129
3130  * 
3131  * @constructor
3132  * Create a new nav group
3133  * @param {Object} config The config object
3134  */
3135
3136 Roo.bootstrap.NavGroup = function(config){
3137     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3138     this.navItems = [];
3139     Roo.bootstrap.NavGroup.register(this);
3140      this.addEvents({
3141         /**
3142              * @event changed
3143              * Fires when the active item changes
3144              * @param {Roo.bootstrap.NavGroup} this
3145              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3146              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3147          */
3148         'changed': true
3149      });
3150     
3151 };
3152
3153 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3154     
3155     align: '',
3156     inverse: false,
3157     form: false,
3158     type: 'nav',
3159     navId : '',
3160     // private
3161     
3162     navItems : false,
3163     
3164     getAutoCreate : function()
3165     {
3166         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3167         
3168         cfg = {
3169             tag : 'ul',
3170             cls: 'nav' 
3171         }
3172         
3173         if (['tabs','pills'].indexOf(this.type)!==-1) {
3174             cfg.cls += ' nav-' + this.type
3175         } else {
3176             if (this.type!=='nav') {
3177                 Roo.log('nav type must be nav/tabs/pills')
3178             }
3179             cfg.cls += ' navbar-nav'
3180         }
3181         
3182         if (this.parent().sidebar) {
3183             cfg = {
3184                 tag: 'ul',
3185                 cls: 'dashboard-menu sidebar-menu'
3186             }
3187             
3188             return cfg;
3189         }
3190         
3191         if (this.form === true) {
3192             cfg = {
3193                 tag: 'form',
3194                 cls: 'navbar-form'
3195             }
3196             
3197             if (this.align === 'right') {
3198                 cfg.cls += ' navbar-right';
3199             } else {
3200                 cfg.cls += ' navbar-left';
3201             }
3202         }
3203         
3204         if (this.align === 'right') {
3205             cfg.cls += ' navbar-right';
3206         }
3207         
3208         if (this.inverse) {
3209             cfg.cls += ' navbar-inverse';
3210             
3211         }
3212         
3213         
3214         return cfg;
3215     },
3216     
3217     setActiveItem : function(item)
3218     {
3219         var prev = false;
3220         Roo.each(this.navItems, function(v){
3221             if (v == item) {
3222                 return ;
3223             }
3224             if (v.isActive()) {
3225                 v.setActive(false, true);
3226                 prev = v;
3227                 
3228             }
3229             
3230         });
3231
3232         item.setActive(true, true);
3233         this.fireEvent('changed', this, item, prev);
3234         
3235         
3236     },
3237     
3238     addItem : function(cfg)
3239     {
3240         var cn = new Roo.bootstrap.NavItem(cfg);
3241         this.register(cn);
3242         cn.parentId = this.id;
3243         cn.onRender(this.el, null);
3244         return cn;
3245     },
3246     
3247     register : function(item)
3248     {
3249         this.navItems.push( item);
3250         item.navId = this.navId;
3251     
3252     },
3253     getNavItem: function(tabId)
3254     {
3255         var ret = false;
3256         Roo.each(this.navItems, function(e) {
3257             if (e.tabId == tabId) {
3258                ret =  e;
3259                return false;
3260             }
3261             return true;
3262             
3263         });
3264         return ret;
3265     }
3266     
3267     
3268     
3269     
3270 });
3271
3272  
3273 Roo.apply(Roo.bootstrap.NavGroup, {
3274     
3275     groups: {},
3276     
3277     register : function(navgrp)
3278     {
3279         this.groups[navgrp.navId] = navgrp;
3280         
3281     },
3282     get: function(navId) {
3283         return this.groups[navId];
3284     }
3285     
3286     
3287     
3288 });
3289
3290  /*
3291  * - LGPL
3292  *
3293  * row
3294  * 
3295  */
3296
3297 /**
3298  * @class Roo.bootstrap.NavItem
3299  * @extends Roo.bootstrap.Component
3300  * Bootstrap Navbar.NavItem class
3301  * @cfg {String} href  link to
3302  * @cfg {String} html content of button
3303  * @cfg {String} badge text inside badge
3304  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3305  * @cfg {String} glyphicon name of glyphicon
3306  * @cfg {String} icon name of font awesome icon
3307  * @cfg {Boolean} active Is item active
3308  * @cfg {Boolean} disabled Is item disabled
3309  
3310  * @cfg {Boolean} preventDefault (true | false) default false
3311  * @cfg {String} tabId the tab that this item activates.
3312  * @cfg {String} tagtype (a|span) render as a href or span?
3313   
3314  * @constructor
3315  * Create a new Navbar Item
3316  * @param {Object} config The config object
3317  */
3318 Roo.bootstrap.NavItem = function(config){
3319     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3320     this.addEvents({
3321         // raw events
3322         /**
3323          * @event click
3324          * The raw click event for the entire grid.
3325          * @param {Roo.EventObject} e
3326          */
3327         "click" : true,
3328          /**
3329             * @event changed
3330             * Fires when the active item active state changes
3331             * @param {Roo.bootstrap.NavItem} this
3332             * @param {boolean} state the new state
3333              
3334          */
3335         'changed': true
3336     });
3337    
3338 };
3339
3340 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3341     
3342     href: false,
3343     html: '',
3344     badge: '',
3345     icon: false,
3346     glyphicon: false,
3347     active: false,
3348     preventDefault : false,
3349     tabId : false,
3350     tagtype : 'a',
3351     disabled : false,
3352     
3353     getAutoCreate : function(){
3354          
3355         var cfg = {
3356             tag: 'li',
3357             cls: 'nav-item',
3358             cn : [
3359                 {
3360                     tag: this.tagtype,
3361                     href : this.href || "#",
3362                     html: this.html || ''
3363                 }
3364             ]
3365         }
3366             
3367         if (this.active) {
3368             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3369         }
3370             
3371         // glyphicon and icon go before content..
3372         if (this.glyphicon || this.icon) {
3373              if (this.icon) {
3374                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3375             } else {
3376                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3377             }
3378         }
3379         
3380         
3381         
3382         if (this.menu) {
3383             
3384             cfg.cn[0].html += " <span class='caret'></span>";
3385          
3386         }
3387         
3388         if (this.badge !== '') {
3389              
3390             cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3391         }
3392         if (this.disabled) {
3393             cfg.cls += ' disabled';
3394         }
3395         
3396         
3397         return cfg;
3398     },
3399     initEvents: function() {
3400        // Roo.log('init events?');
3401        // Roo.log(this.el.dom);
3402         if (typeof (this.menu) != 'undefined') {
3403             this.menu.parentType = this.xtype;
3404             this.menu.triggerEl = this.el;
3405             this.addxtype(Roo.apply({}, this.menu));
3406         }
3407
3408        
3409         this.el.select('a',true).on('click', this.onClick, this);
3410         // at this point parent should be available..
3411         this.parent().register(this);
3412     },
3413     
3414     onClick : function(e)
3415     {
3416          
3417         if(this.preventDefault){
3418             e.preventDefault();
3419         }
3420         if (this.disabled) {
3421             return;
3422         }
3423         Roo.log("fire event clicked");
3424         if(this.fireEvent('click', this, e) === false){
3425             return;
3426         };
3427         
3428         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3429              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3430                 this.parent().setActiveItem(this);
3431             }
3432             
3433             
3434             
3435         } 
3436     },
3437     
3438     isActive: function () {
3439         return this.active
3440     },
3441     setActive : function(state, fire)
3442     {
3443         this.active = state;
3444         if (!state ) {
3445             this.el.removeClass('active');
3446         } else if (!this.el.hasClass('active')) {
3447             this.el.addClass('active');
3448         }
3449         if (fire) {
3450             this.fireEvent('changed', this, state);
3451         }
3452         
3453         
3454     },
3455      // this should not be here...
3456     setDisabled : function(state)
3457     {
3458         this.disabled = state;
3459         if (!state ) {
3460             this.el.removeClass('disabled');
3461         } else if (!this.el.hasClass('disabled')) {
3462             this.el.addClass('disabled');
3463         }
3464         
3465     }
3466 });
3467  
3468
3469  /*
3470  * - LGPL
3471  *
3472  * sidebar item
3473  *
3474  *  li
3475  *    <span> icon </span>
3476  *    <span> text </span>
3477  *    <span>badge </span>
3478  */
3479
3480 /**
3481  * @class Roo.bootstrap.NavSidebarItem
3482  * @extends Roo.bootstrap.NavItem
3483  * Bootstrap Navbar.NavSidebarItem class
3484  * @constructor
3485  * Create a new Navbar Button
3486  * @param {Object} config The config object
3487  */
3488 Roo.bootstrap.NavSidebarItem = function(config){
3489     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3490     this.addEvents({
3491         // raw events
3492         /**
3493          * @event click
3494          * The raw click event for the entire grid.
3495          * @param {Roo.EventObject} e
3496          */
3497         "click" : true,
3498          /**
3499             * @event changed
3500             * Fires when the active item active state changes
3501             * @param {Roo.bootstrap.NavSidebarItem} this
3502             * @param {boolean} state the new state
3503              
3504          */
3505         'changed': true
3506     });
3507    
3508 };
3509
3510 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3511     
3512     
3513     getAutoCreate : function(){
3514         
3515         
3516         var a = {
3517                 tag: 'a',
3518                 href : this.href || '#',
3519                 cls: '',
3520                 html : '',
3521                 cn : []
3522         };
3523         var cfg = {
3524             tag: 'li',
3525             cls: '',
3526             cn: [ a ]
3527         }
3528         var span = {
3529             tag: 'span',
3530             html : this.html || ''
3531         }
3532         
3533         
3534         if (this.active) {
3535             cfg.cls += ' active';
3536         }
3537         
3538         // left icon..
3539         if (this.glyphicon || this.icon) {
3540             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3541             a.cn.push({ tag : 'i', cls : c }) ;
3542         }
3543         // html..
3544         a.cn.push(span);
3545         // then badge..
3546         if (this.badge !== '') {
3547             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3548         }
3549         // fi
3550         if (this.menu) {
3551             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3552             a.cls += 'dropdown-toggle treeview' ;
3553             
3554         }
3555         
3556         
3557         
3558         return cfg;
3559          
3560            
3561     }
3562    
3563      
3564  
3565 });
3566  
3567
3568  /*
3569  * - LGPL
3570  *
3571  * row
3572  * 
3573  */
3574
3575 /**
3576  * @class Roo.bootstrap.Row
3577  * @extends Roo.bootstrap.Component
3578  * Bootstrap Row class (contains columns...)
3579  * 
3580  * @constructor
3581  * Create a new Row
3582  * @param {Object} config The config object
3583  */
3584
3585 Roo.bootstrap.Row = function(config){
3586     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3587 };
3588
3589 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3590     
3591     getAutoCreate : function(){
3592        return {
3593             cls: 'row clearfix'
3594        };
3595     }
3596     
3597     
3598 });
3599
3600  
3601
3602  /*
3603  * - LGPL
3604  *
3605  * element
3606  * 
3607  */
3608
3609 /**
3610  * @class Roo.bootstrap.Element
3611  * @extends Roo.bootstrap.Component
3612  * Bootstrap Element class
3613  * @cfg {String} html contents of the element
3614  * @cfg {String} tag tag of the element
3615  * @cfg {String} cls class of the element
3616  * 
3617  * @constructor
3618  * Create a new Element
3619  * @param {Object} config The config object
3620  */
3621
3622 Roo.bootstrap.Element = function(config){
3623     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3624 };
3625
3626 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3627     
3628     tag: 'div',
3629     cls: '',
3630     html: '',
3631      
3632     
3633     getAutoCreate : function(){
3634         
3635         var cfg = {
3636             tag: this.tag,
3637             cls: this.cls,
3638             html: this.html
3639         }
3640         
3641         
3642         
3643         return cfg;
3644     }
3645    
3646 });
3647
3648  
3649
3650  /*
3651  * - LGPL
3652  *
3653  * pagination
3654  * 
3655  */
3656
3657 /**
3658  * @class Roo.bootstrap.Pagination
3659  * @extends Roo.bootstrap.Component
3660  * Bootstrap Pagination class
3661  * @cfg {String} size xs | sm | md | lg
3662  * @cfg {Boolean} inverse false | true
3663  * 
3664  * @constructor
3665  * Create a new Pagination
3666  * @param {Object} config The config object
3667  */
3668
3669 Roo.bootstrap.Pagination = function(config){
3670     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3671 };
3672
3673 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3674     
3675     cls: false,
3676     size: false,
3677     inverse: false,
3678     
3679     getAutoCreate : function(){
3680         var cfg = {
3681             tag: 'ul',
3682                 cls: 'pagination'
3683         };
3684         if (this.inverse) {
3685             cfg.cls += ' inverse';
3686         }
3687         if (this.html) {
3688             cfg.html=this.html;
3689         }
3690         if (this.cls) {
3691             cfg.cls += " " + this.cls;
3692         }
3693         return cfg;
3694     }
3695    
3696 });
3697
3698  
3699
3700  /*
3701  * - LGPL
3702  *
3703  * Pagination item
3704  * 
3705  */
3706
3707
3708 /**
3709  * @class Roo.bootstrap.PaginationItem
3710  * @extends Roo.bootstrap.Component
3711  * Bootstrap PaginationItem class
3712  * @cfg {String} html text
3713  * @cfg {String} href the link
3714  * @cfg {Boolean} preventDefault (true | false) default true
3715  * @cfg {Boolean} active (true | false) default false
3716  * 
3717  * 
3718  * @constructor
3719  * Create a new PaginationItem
3720  * @param {Object} config The config object
3721  */
3722
3723
3724 Roo.bootstrap.PaginationItem = function(config){
3725     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3726     this.addEvents({
3727         // raw events
3728         /**
3729          * @event click
3730          * The raw click event for the entire grid.
3731          * @param {Roo.EventObject} e
3732          */
3733         "click" : true
3734     });
3735 };
3736
3737 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3738     
3739     href : false,
3740     html : false,
3741     preventDefault: true,
3742     active : false,
3743     cls : false,
3744     
3745     getAutoCreate : function(){
3746         var cfg= {
3747             tag: 'li',
3748             cn: [
3749                 {
3750                     tag : 'a',
3751                     href : this.href ? this.href : '#',
3752                     html : this.html ? this.html : ''
3753                 }
3754             ]
3755         };
3756         
3757         if(this.cls){
3758             cfg.cls = this.cls;
3759         }
3760         
3761         if(this.active){
3762             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3763         }
3764         
3765         return cfg;
3766     },
3767     
3768     initEvents: function() {
3769         
3770         this.el.on('click', this.onClick, this);
3771         
3772     },
3773     onClick : function(e)
3774     {
3775         Roo.log('PaginationItem on click ');
3776         if(this.preventDefault){
3777             e.preventDefault();
3778         }
3779         
3780         this.fireEvent('click', this, e);
3781     }
3782    
3783 });
3784
3785  
3786
3787  /*
3788  * - LGPL
3789  *
3790  * slider
3791  * 
3792  */
3793
3794
3795 /**
3796  * @class Roo.bootstrap.Slider
3797  * @extends Roo.bootstrap.Component
3798  * Bootstrap Slider class
3799  *    
3800  * @constructor
3801  * Create a new Slider
3802  * @param {Object} config The config object
3803  */
3804
3805 Roo.bootstrap.Slider = function(config){
3806     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3807 };
3808
3809 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3810     
3811     getAutoCreate : function(){
3812         
3813         var cfg = {
3814             tag: 'div',
3815             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3816             cn: [
3817                 {
3818                     tag: 'a',
3819                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3820                 }
3821             ]
3822         }
3823         
3824         return cfg;
3825     }
3826    
3827 });
3828
3829  /*
3830  * - LGPL
3831  *
3832  * table
3833  * 
3834  */
3835
3836 /**
3837  * @class Roo.bootstrap.Table
3838  * @extends Roo.bootstrap.Component
3839  * Bootstrap Table class
3840  * @cfg {String} cls table class
3841  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
3842  * @cfg {String} bgcolor Specifies the background color for a table
3843  * @cfg {Number} border Specifies whether the table cells should have borders or not
3844  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
3845  * @cfg {Number} cellspacing Specifies the space between cells
3846  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
3847  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
3848  * @cfg {String} sortable Specifies that the table should be sortable
3849  * @cfg {String} summary Specifies a summary of the content of a table
3850  * @cfg {Number} width Specifies the width of a table
3851  * 
3852  * @cfg {boolean} striped Should the rows be alternative striped
3853  * @cfg {boolean} bordered Add borders to the table
3854  * @cfg {boolean} hover Add hover highlighting
3855  * @cfg {boolean} condensed Format condensed
3856  * @cfg {boolean} responsive Format condensed
3857  * @cfg {Boolean} loadMask (true|false) default false
3858  *
3859  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
3860  
3861  * 
3862  * @constructor
3863  * Create a new Table
3864  * @param {Object} config The config object
3865  */
3866
3867 Roo.bootstrap.Table = function(config){
3868     Roo.bootstrap.Table.superclass.constructor.call(this, config);
3869     
3870     if (this.sm) {
3871         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
3872         this.sm = this.selModel;
3873         this.sm.xmodule = this.xmodule || false;
3874     }
3875     if (this.cm && typeof(this.cm.config) == 'undefined') {
3876         this.colModel = new Roo.grid.ColumnModel(this.cm);
3877         this.cm = this.colModel;
3878         this.cm.xmodule = this.xmodule || false;
3879     }
3880     if (this.store) {
3881         this.store= Roo.factory(this.store, Roo.data);
3882         this.ds = this.store;
3883         this.ds.xmodule = this.xmodule || false;
3884          
3885     }
3886     if (this.footer && this.store) {
3887         this.footer.dataSource = this.ds;
3888         this.footer = Roo.factory(this.footer);
3889     }
3890 };
3891
3892 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
3893     
3894     cls: false,
3895     align: false,
3896     bgcolor: false,
3897     border: false,
3898     cellpadding: false,
3899     cellspacing: false,
3900     frame: false,
3901     rules: false,
3902     sortable: false,
3903     summary: false,
3904     width: false,
3905     striped : false,
3906     bordered: false,
3907     hover:  false,
3908     condensed : false,
3909     responsive : false,
3910     sm : false,
3911     cm : false,
3912     store : false,
3913     loadMask : false,
3914     
3915     getAutoCreate : function(){
3916         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
3917         
3918         cfg = {
3919             tag: 'table',
3920             cls : 'table',
3921             cn : []
3922         }
3923             
3924         if (this.striped) {
3925             cfg.cls += ' table-striped';
3926         }
3927         if (this.hover) {
3928             cfg.cls += ' table-hover';
3929         }
3930         if (this.bordered) {
3931             cfg.cls += ' table-bordered';
3932         }
3933         if (this.condensed) {
3934             cfg.cls += ' table-condensed';
3935         }
3936         if (this.responsive) {
3937             cfg.cls += ' table-responsive';
3938         }
3939         
3940           
3941         
3942         
3943         if (this.cls) {
3944             cfg.cls+=  ' ' +this.cls;
3945         }
3946         
3947         // this lot should be simplifed...
3948         
3949         if (this.align) {
3950             cfg.align=this.align;
3951         }
3952         if (this.bgcolor) {
3953             cfg.bgcolor=this.bgcolor;
3954         }
3955         if (this.border) {
3956             cfg.border=this.border;
3957         }
3958         if (this.cellpadding) {
3959             cfg.cellpadding=this.cellpadding;
3960         }
3961         if (this.cellspacing) {
3962             cfg.cellspacing=this.cellspacing;
3963         }
3964         if (this.frame) {
3965             cfg.frame=this.frame;
3966         }
3967         if (this.rules) {
3968             cfg.rules=this.rules;
3969         }
3970         if (this.sortable) {
3971             cfg.sortable=this.sortable;
3972         }
3973         if (this.summary) {
3974             cfg.summary=this.summary;
3975         }
3976         if (this.width) {
3977             cfg.width=this.width;
3978         }
3979         
3980         if(this.store || this.cm){
3981             cfg.cn.push(this.renderHeader());
3982             cfg.cn.push(this.renderBody());
3983             cfg.cn.push(this.renderFooter());
3984             
3985             cfg.cls+=  ' TableGrid';
3986         }
3987         
3988         return cfg;
3989     },
3990 //    
3991 //    initTableGrid : function()
3992 //    {
3993 //        var cfg = {};
3994 //        
3995 //        var header = {
3996 //            tag: 'thead',
3997 //            cn : []
3998 //        };
3999 //        
4000 //        var cm = this.cm;
4001 //        
4002 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4003 //            header.cn.push({
4004 //                tag: 'th',
4005 //                html: cm.getColumnHeader(i)
4006 //            })
4007 //        }
4008 //        
4009 //        cfg.push(header);
4010 //        
4011 //        return cfg;
4012 //        
4013 //        
4014 //    },
4015     
4016     initEvents : function()
4017     {   
4018         if(!this.store || !this.cm){
4019             return;
4020         }
4021         
4022         Roo.log('initEvents with ds!!!!');
4023         
4024         var _this = this;
4025         
4026         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4027             e.on('click', _this.sort, _this);
4028         });
4029 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
4030 //        this.maskEl.enableDisplayMode("block");
4031 //        this.maskEl.show();
4032         
4033         this.parent().el.setStyle('position', 'relative');
4034         if (this.footer) {
4035             this.footer.parentId = this.id;
4036             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4037         }
4038         
4039         
4040         // mask should be using Roo.bootstrap.Mask...
4041         
4042         var mark = {
4043             tag: "div",
4044             cls:"x-dlg-mask",
4045             style: "text-align:center",
4046             cn: [
4047                 {
4048                     tag: "div",
4049                     style: "background-color:white;width:50%;margin:100 auto; display:none",
4050                     cn: [
4051                         {
4052                             tag: "img",
4053                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
4054                         },
4055                         {
4056                             tag: "span",
4057                             html: "Loading"
4058                         }
4059                         
4060                     ]
4061                 }
4062             ]
4063         }
4064         this.maskEl = Roo.DomHelper.append(document.body, mark, true);
4065         
4066         var size = this.parent().el.getSize();
4067         
4068         this.maskEl.setSize(size.width, 300); // we will fix the height at the beginning...
4069         
4070         this.maskEl.enableDisplayMode("block");
4071         
4072         this.store.on('load', this.onLoad, this);
4073         this.store.on('beforeload', this.onBeforeLoad, this);
4074         
4075         // load should be trigger on render..
4076         //this.store.load();
4077         
4078         
4079         
4080     },
4081     
4082     sort : function(e,el)
4083     {
4084         var col = Roo.get(el)
4085         
4086         if(!col.hasClass('sortable')){
4087             return;
4088         }
4089         
4090         var sort = col.attr('sort');
4091         var dir = 'ASC';
4092         
4093         if(col.hasClass('glyphicon-arrow-up')){
4094             dir = 'DESC';
4095         }
4096         
4097         this.store.sortInfo = {field : sort, direction : dir};
4098         
4099         this.store.load();
4100     },
4101     
4102     renderHeader : function()
4103     {
4104         var header = {
4105             tag: 'thead',
4106             cn : []
4107         };
4108         
4109         var cm = this.cm;
4110         
4111         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4112             
4113             var config = cm.config[i];
4114             
4115             if(typeof(config.hidden) != 'undefined' && config.hidden){
4116                 continue;
4117             }
4118                     
4119             var c = {
4120                 tag: 'th',
4121                 html: cm.getColumnHeader(i)
4122             };
4123             
4124             if(typeof(config.dataIndex) != 'undefined'){
4125                 c.sort = config.dataIndex;
4126             }
4127             
4128             if(typeof(config.sortable) != 'undefined' && config.sortable){
4129                 c.cls = 'sortable';
4130             }
4131             
4132             if(typeof(config.width) != 'undefined'){
4133                 c.style = 'width:' + config.width + 'px';
4134             }
4135             
4136             header.cn.push(c)
4137         }
4138         
4139         return header;
4140     },
4141     
4142     renderBody : function()
4143     {
4144         var body = {
4145             tag: 'tbody',
4146             cn : []
4147         };
4148         
4149         return body;
4150     },
4151     
4152     renderFooter : function()
4153     {
4154         var footer = {
4155             tag: 'tfoot',
4156             cn : [
4157                 {
4158                     tag: 'tr',
4159                     cn : [
4160                         {
4161                             tag : 'td',
4162                             colspan : 1
4163                         }
4164                     ]
4165                 }
4166             ]
4167         };
4168         
4169         return footer;
4170     },
4171     
4172     onLoad : function()
4173     {
4174         Roo.log('ds onload');
4175         
4176         var _this = this;
4177         var cm = this.cm;
4178         
4179         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4180             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4181             
4182             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4183                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4184             }
4185             
4186             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4187                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4188             }
4189         });
4190         
4191         var tbody = this.el.select('tbody', true).first();
4192         
4193         var renders = [];
4194                     
4195         if(this.store.getCount() > 0){
4196             this.store.data.each(function(d){
4197                 var row = {
4198                     tag : 'tr',
4199                     cn : []
4200                 };
4201                 
4202                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4203                     var config = cm.config[i];
4204                     
4205                     if(typeof(config.hidden) != 'undefined' && config.hidden){
4206                         continue;
4207                     }
4208                     
4209                     var renderer = cm.getRenderer(i);
4210                     var value = '';
4211                     var id = Roo.id();
4212                     
4213                     if(typeof(renderer) !== 'undefined'){
4214                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
4215                     }
4216                     
4217                     if(typeof(value) === 'object'){
4218                         renders.push({
4219                             id : id,
4220                             cfg : value 
4221                         })
4222                     }
4223                     
4224                     var td = {
4225                         tag: 'td',
4226                         id: id,
4227                         html: (typeof(value) === 'object') ? '' : value
4228                     };
4229                     
4230                     if(typeof(config.width) != 'undefined'){
4231                         td.style = 'width:' +  config.width + 'px';
4232                     }
4233                     
4234                     row.cn.push(td);
4235                    
4236                 }
4237                 
4238                 tbody.createChild(row);
4239                 
4240             });
4241         }
4242         
4243         
4244         if(renders.length){
4245             var _this = this;
4246             Roo.each(renders, function(r){
4247                 _this.renderColumn(r);
4248             })
4249         }
4250
4251         if(this.loadMask){
4252             this.maskEl.hide();
4253         }
4254     },
4255     
4256     onBeforeLoad : function()
4257     {
4258         Roo.log('ds onBeforeLoad');
4259         
4260         this.clear();
4261         
4262         if(this.loadMask){
4263             this.maskEl.show();
4264         }
4265     },
4266     
4267     clear : function()
4268     {
4269         this.el.select('tbody', true).first().dom.innerHTML = '';
4270     },
4271     
4272     getSelectionModel : function(){
4273         if(!this.selModel){
4274             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
4275         }
4276         return this.selModel;
4277     },
4278     
4279     renderColumn : function(r)
4280     {
4281         var _this = this;
4282         r.cfg.render(Roo.get(r.id));
4283         
4284         if(r.cfg.cn){
4285             Roo.each(r.cfg.cn, function(c){
4286                 var child = {
4287                     id: r.id,
4288                     cfg: c
4289                 }
4290                 _this.renderColumn(child);
4291             })
4292         }
4293     }
4294    
4295 });
4296
4297  
4298
4299  /*
4300  * - LGPL
4301  *
4302  * table cell
4303  * 
4304  */
4305
4306 /**
4307  * @class Roo.bootstrap.TableCell
4308  * @extends Roo.bootstrap.Component
4309  * Bootstrap TableCell class
4310  * @cfg {String} html cell contain text
4311  * @cfg {String} cls cell class
4312  * @cfg {String} tag cell tag (td|th) default td
4313  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
4314  * @cfg {String} align Aligns the content in a cell
4315  * @cfg {String} axis Categorizes cells
4316  * @cfg {String} bgcolor Specifies the background color of a cell
4317  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4318  * @cfg {Number} colspan Specifies the number of columns a cell should span
4319  * @cfg {String} headers Specifies one or more header cells a cell is related to
4320  * @cfg {Number} height Sets the height of a cell
4321  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
4322  * @cfg {Number} rowspan Sets the number of rows a cell should span
4323  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
4324  * @cfg {String} valign Vertical aligns the content in a cell
4325  * @cfg {Number} width Specifies the width of a cell
4326  * 
4327  * @constructor
4328  * Create a new TableCell
4329  * @param {Object} config The config object
4330  */
4331
4332 Roo.bootstrap.TableCell = function(config){
4333     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
4334 };
4335
4336 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
4337     
4338     html: false,
4339     cls: false,
4340     tag: false,
4341     abbr: false,
4342     align: false,
4343     axis: false,
4344     bgcolor: false,
4345     charoff: false,
4346     colspan: false,
4347     headers: false,
4348     height: false,
4349     nowrap: false,
4350     rowspan: false,
4351     scope: false,
4352     valign: false,
4353     width: false,
4354     
4355     
4356     getAutoCreate : function(){
4357         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
4358         
4359         cfg = {
4360             tag: 'td'
4361         }
4362         
4363         if(this.tag){
4364             cfg.tag = this.tag;
4365         }
4366         
4367         if (this.html) {
4368             cfg.html=this.html
4369         }
4370         if (this.cls) {
4371             cfg.cls=this.cls
4372         }
4373         if (this.abbr) {
4374             cfg.abbr=this.abbr
4375         }
4376         if (this.align) {
4377             cfg.align=this.align
4378         }
4379         if (this.axis) {
4380             cfg.axis=this.axis
4381         }
4382         if (this.bgcolor) {
4383             cfg.bgcolor=this.bgcolor
4384         }
4385         if (this.charoff) {
4386             cfg.charoff=this.charoff
4387         }
4388         if (this.colspan) {
4389             cfg.colspan=this.colspan
4390         }
4391         if (this.headers) {
4392             cfg.headers=this.headers
4393         }
4394         if (this.height) {
4395             cfg.height=this.height
4396         }
4397         if (this.nowrap) {
4398             cfg.nowrap=this.nowrap
4399         }
4400         if (this.rowspan) {
4401             cfg.rowspan=this.rowspan
4402         }
4403         if (this.scope) {
4404             cfg.scope=this.scope
4405         }
4406         if (this.valign) {
4407             cfg.valign=this.valign
4408         }
4409         if (this.width) {
4410             cfg.width=this.width
4411         }
4412         
4413         
4414         return cfg;
4415     }
4416    
4417 });
4418
4419  
4420
4421  /*
4422  * - LGPL
4423  *
4424  * table row
4425  * 
4426  */
4427
4428 /**
4429  * @class Roo.bootstrap.TableRow
4430  * @extends Roo.bootstrap.Component
4431  * Bootstrap TableRow class
4432  * @cfg {String} cls row class
4433  * @cfg {String} align Aligns the content in a table row
4434  * @cfg {String} bgcolor Specifies a background color for a table row
4435  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4436  * @cfg {String} valign Vertical aligns the content in a table row
4437  * 
4438  * @constructor
4439  * Create a new TableRow
4440  * @param {Object} config The config object
4441  */
4442
4443 Roo.bootstrap.TableRow = function(config){
4444     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4445 };
4446
4447 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4448     
4449     cls: false,
4450     align: false,
4451     bgcolor: false,
4452     charoff: false,
4453     valign: false,
4454     
4455     getAutoCreate : function(){
4456         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4457         
4458         cfg = {
4459             tag: 'tr'
4460         }
4461             
4462         if(this.cls){
4463             cfg.cls = this.cls;
4464         }
4465         if(this.align){
4466             cfg.align = this.align;
4467         }
4468         if(this.bgcolor){
4469             cfg.bgcolor = this.bgcolor;
4470         }
4471         if(this.charoff){
4472             cfg.charoff = this.charoff;
4473         }
4474         if(this.valign){
4475             cfg.valign = this.valign;
4476         }
4477         
4478         return cfg;
4479     }
4480    
4481 });
4482
4483  
4484
4485  /*
4486  * - LGPL
4487  *
4488  * table body
4489  * 
4490  */
4491
4492 /**
4493  * @class Roo.bootstrap.TableBody
4494  * @extends Roo.bootstrap.Component
4495  * Bootstrap TableBody class
4496  * @cfg {String} cls element class
4497  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4498  * @cfg {String} align Aligns the content inside the element
4499  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4500  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4501  * 
4502  * @constructor
4503  * Create a new TableBody
4504  * @param {Object} config The config object
4505  */
4506
4507 Roo.bootstrap.TableBody = function(config){
4508     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4509 };
4510
4511 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4512     
4513     cls: false,
4514     tag: false,
4515     align: false,
4516     charoff: false,
4517     valign: false,
4518     
4519     getAutoCreate : function(){
4520         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4521         
4522         cfg = {
4523             tag: 'tbody'
4524         }
4525             
4526         if (this.cls) {
4527             cfg.cls=this.cls
4528         }
4529         if(this.tag){
4530             cfg.tag = this.tag;
4531         }
4532         
4533         if(this.align){
4534             cfg.align = this.align;
4535         }
4536         if(this.charoff){
4537             cfg.charoff = this.charoff;
4538         }
4539         if(this.valign){
4540             cfg.valign = this.valign;
4541         }
4542         
4543         return cfg;
4544     }
4545     
4546     
4547 //    initEvents : function()
4548 //    {
4549 //        
4550 //        if(!this.store){
4551 //            return;
4552 //        }
4553 //        
4554 //        this.store = Roo.factory(this.store, Roo.data);
4555 //        this.store.on('load', this.onLoad, this);
4556 //        
4557 //        this.store.load();
4558 //        
4559 //    },
4560 //    
4561 //    onLoad: function () 
4562 //    {   
4563 //        this.fireEvent('load', this);
4564 //    }
4565 //    
4566 //   
4567 });
4568
4569  
4570
4571  /*
4572  * Based on:
4573  * Ext JS Library 1.1.1
4574  * Copyright(c) 2006-2007, Ext JS, LLC.
4575  *
4576  * Originally Released Under LGPL - original licence link has changed is not relivant.
4577  *
4578  * Fork - LGPL
4579  * <script type="text/javascript">
4580  */
4581
4582 // as we use this in bootstrap.
4583 Roo.namespace('Roo.form');
4584  /**
4585  * @class Roo.form.Action
4586  * Internal Class used to handle form actions
4587  * @constructor
4588  * @param {Roo.form.BasicForm} el The form element or its id
4589  * @param {Object} config Configuration options
4590  */
4591
4592  
4593  
4594 // define the action interface
4595 Roo.form.Action = function(form, options){
4596     this.form = form;
4597     this.options = options || {};
4598 };
4599 /**
4600  * Client Validation Failed
4601  * @const 
4602  */
4603 Roo.form.Action.CLIENT_INVALID = 'client';
4604 /**
4605  * Server Validation Failed
4606  * @const 
4607  */
4608 Roo.form.Action.SERVER_INVALID = 'server';
4609  /**
4610  * Connect to Server Failed
4611  * @const 
4612  */
4613 Roo.form.Action.CONNECT_FAILURE = 'connect';
4614 /**
4615  * Reading Data from Server Failed
4616  * @const 
4617  */
4618 Roo.form.Action.LOAD_FAILURE = 'load';
4619
4620 Roo.form.Action.prototype = {
4621     type : 'default',
4622     failureType : undefined,
4623     response : undefined,
4624     result : undefined,
4625
4626     // interface method
4627     run : function(options){
4628
4629     },
4630
4631     // interface method
4632     success : function(response){
4633
4634     },
4635
4636     // interface method
4637     handleResponse : function(response){
4638
4639     },
4640
4641     // default connection failure
4642     failure : function(response){
4643         
4644         this.response = response;
4645         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4646         this.form.afterAction(this, false);
4647     },
4648
4649     processResponse : function(response){
4650         this.response = response;
4651         if(!response.responseText){
4652             return true;
4653         }
4654         this.result = this.handleResponse(response);
4655         return this.result;
4656     },
4657
4658     // utility functions used internally
4659     getUrl : function(appendParams){
4660         var url = this.options.url || this.form.url || this.form.el.dom.action;
4661         if(appendParams){
4662             var p = this.getParams();
4663             if(p){
4664                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4665             }
4666         }
4667         return url;
4668     },
4669
4670     getMethod : function(){
4671         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4672     },
4673
4674     getParams : function(){
4675         var bp = this.form.baseParams;
4676         var p = this.options.params;
4677         if(p){
4678             if(typeof p == "object"){
4679                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4680             }else if(typeof p == 'string' && bp){
4681                 p += '&' + Roo.urlEncode(bp);
4682             }
4683         }else if(bp){
4684             p = Roo.urlEncode(bp);
4685         }
4686         return p;
4687     },
4688
4689     createCallback : function(){
4690         return {
4691             success: this.success,
4692             failure: this.failure,
4693             scope: this,
4694             timeout: (this.form.timeout*1000),
4695             upload: this.form.fileUpload ? this.success : undefined
4696         };
4697     }
4698 };
4699
4700 Roo.form.Action.Submit = function(form, options){
4701     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4702 };
4703
4704 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4705     type : 'submit',
4706
4707     haveProgress : false,
4708     uploadComplete : false,
4709     
4710     // uploadProgress indicator.
4711     uploadProgress : function()
4712     {
4713         if (!this.form.progressUrl) {
4714             return;
4715         }
4716         
4717         if (!this.haveProgress) {
4718             Roo.MessageBox.progress("Uploading", "Uploading");
4719         }
4720         if (this.uploadComplete) {
4721            Roo.MessageBox.hide();
4722            return;
4723         }
4724         
4725         this.haveProgress = true;
4726    
4727         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4728         
4729         var c = new Roo.data.Connection();
4730         c.request({
4731             url : this.form.progressUrl,
4732             params: {
4733                 id : uid
4734             },
4735             method: 'GET',
4736             success : function(req){
4737                //console.log(data);
4738                 var rdata = false;
4739                 var edata;
4740                 try  {
4741                    rdata = Roo.decode(req.responseText)
4742                 } catch (e) {
4743                     Roo.log("Invalid data from server..");
4744                     Roo.log(edata);
4745                     return;
4746                 }
4747                 if (!rdata || !rdata.success) {
4748                     Roo.log(rdata);
4749                     Roo.MessageBox.alert(Roo.encode(rdata));
4750                     return;
4751                 }
4752                 var data = rdata.data;
4753                 
4754                 if (this.uploadComplete) {
4755                    Roo.MessageBox.hide();
4756                    return;
4757                 }
4758                    
4759                 if (data){
4760                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4761                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4762                     );
4763                 }
4764                 this.uploadProgress.defer(2000,this);
4765             },
4766        
4767             failure: function(data) {
4768                 Roo.log('progress url failed ');
4769                 Roo.log(data);
4770             },
4771             scope : this
4772         });
4773            
4774     },
4775     
4776     
4777     run : function()
4778     {
4779         // run get Values on the form, so it syncs any secondary forms.
4780         this.form.getValues();
4781         
4782         var o = this.options;
4783         var method = this.getMethod();
4784         var isPost = method == 'POST';
4785         if(o.clientValidation === false || this.form.isValid()){
4786             
4787             if (this.form.progressUrl) {
4788                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4789                     (new Date() * 1) + '' + Math.random());
4790                     
4791             } 
4792             
4793             
4794             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4795                 form:this.form.el.dom,
4796                 url:this.getUrl(!isPost),
4797                 method: method,
4798                 params:isPost ? this.getParams() : null,
4799                 isUpload: this.form.fileUpload
4800             }));
4801             
4802             this.uploadProgress();
4803
4804         }else if (o.clientValidation !== false){ // client validation failed
4805             this.failureType = Roo.form.Action.CLIENT_INVALID;
4806             this.form.afterAction(this, false);
4807         }
4808     },
4809
4810     success : function(response)
4811     {
4812         this.uploadComplete= true;
4813         if (this.haveProgress) {
4814             Roo.MessageBox.hide();
4815         }
4816         
4817         
4818         var result = this.processResponse(response);
4819         if(result === true || result.success){
4820             this.form.afterAction(this, true);
4821             return;
4822         }
4823         if(result.errors){
4824             this.form.markInvalid(result.errors);
4825             this.failureType = Roo.form.Action.SERVER_INVALID;
4826         }
4827         this.form.afterAction(this, false);
4828     },
4829     failure : function(response)
4830     {
4831         this.uploadComplete= true;
4832         if (this.haveProgress) {
4833             Roo.MessageBox.hide();
4834         }
4835         
4836         this.response = response;
4837         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4838         this.form.afterAction(this, false);
4839     },
4840     
4841     handleResponse : function(response){
4842         if(this.form.errorReader){
4843             var rs = this.form.errorReader.read(response);
4844             var errors = [];
4845             if(rs.records){
4846                 for(var i = 0, len = rs.records.length; i < len; i++) {
4847                     var r = rs.records[i];
4848                     errors[i] = r.data;
4849                 }
4850             }
4851             if(errors.length < 1){
4852                 errors = null;
4853             }
4854             return {
4855                 success : rs.success,
4856                 errors : errors
4857             };
4858         }
4859         var ret = false;
4860         try {
4861             ret = Roo.decode(response.responseText);
4862         } catch (e) {
4863             ret = {
4864                 success: false,
4865                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4866                 errors : []
4867             };
4868         }
4869         return ret;
4870         
4871     }
4872 });
4873
4874
4875 Roo.form.Action.Load = function(form, options){
4876     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4877     this.reader = this.form.reader;
4878 };
4879
4880 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4881     type : 'load',
4882
4883     run : function(){
4884         
4885         Roo.Ajax.request(Roo.apply(
4886                 this.createCallback(), {
4887                     method:this.getMethod(),
4888                     url:this.getUrl(false),
4889                     params:this.getParams()
4890         }));
4891     },
4892
4893     success : function(response){
4894         
4895         var result = this.processResponse(response);
4896         if(result === true || !result.success || !result.data){
4897             this.failureType = Roo.form.Action.LOAD_FAILURE;
4898             this.form.afterAction(this, false);
4899             return;
4900         }
4901         this.form.clearInvalid();
4902         this.form.setValues(result.data);
4903         this.form.afterAction(this, true);
4904     },
4905
4906     handleResponse : function(response){
4907         if(this.form.reader){
4908             var rs = this.form.reader.read(response);
4909             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
4910             return {
4911                 success : rs.success,
4912                 data : data
4913             };
4914         }
4915         return Roo.decode(response.responseText);
4916     }
4917 });
4918
4919 Roo.form.Action.ACTION_TYPES = {
4920     'load' : Roo.form.Action.Load,
4921     'submit' : Roo.form.Action.Submit
4922 };/*
4923  * - LGPL
4924  *
4925  * form
4926  * 
4927  */
4928
4929 /**
4930  * @class Roo.bootstrap.Form
4931  * @extends Roo.bootstrap.Component
4932  * Bootstrap Form class
4933  * @cfg {String} method  GET | POST (default POST)
4934  * @cfg {String} labelAlign top | left (default top)
4935   * @cfg {String} align left  | right - for navbars
4936
4937  * 
4938  * @constructor
4939  * Create a new Form
4940  * @param {Object} config The config object
4941  */
4942
4943
4944 Roo.bootstrap.Form = function(config){
4945     Roo.bootstrap.Form.superclass.constructor.call(this, config);
4946     this.addEvents({
4947         /**
4948          * @event clientvalidation
4949          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
4950          * @param {Form} this
4951          * @param {Boolean} valid true if the form has passed client-side validation
4952          */
4953         clientvalidation: true,
4954         /**
4955          * @event beforeaction
4956          * Fires before any action is performed. Return false to cancel the action.
4957          * @param {Form} this
4958          * @param {Action} action The action to be performed
4959          */
4960         beforeaction: true,
4961         /**
4962          * @event actionfailed
4963          * Fires when an action fails.
4964          * @param {Form} this
4965          * @param {Action} action The action that failed
4966          */
4967         actionfailed : true,
4968         /**
4969          * @event actioncomplete
4970          * Fires when an action is completed.
4971          * @param {Form} this
4972          * @param {Action} action The action that completed
4973          */
4974         actioncomplete : true
4975     });
4976     
4977 };
4978
4979 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
4980       
4981      /**
4982      * @cfg {String} method
4983      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
4984      */
4985     method : 'POST',
4986     /**
4987      * @cfg {String} url
4988      * The URL to use for form actions if one isn't supplied in the action options.
4989      */
4990     /**
4991      * @cfg {Boolean} fileUpload
4992      * Set to true if this form is a file upload.
4993      */
4994      
4995     /**
4996      * @cfg {Object} baseParams
4997      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
4998      */
4999       
5000     /**
5001      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5002      */
5003     timeout: 30,
5004     /**
5005      * @cfg {Sting} align (left|right) for navbar forms
5006      */
5007     align : 'left',
5008
5009     // private
5010     activeAction : null,
5011  
5012     /**
5013      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5014      * element by passing it or its id or mask the form itself by passing in true.
5015      * @type Mixed
5016      */
5017     waitMsgTarget : false,
5018     
5019      
5020     
5021     /**
5022      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5023      * element by passing it or its id or mask the form itself by passing in true.
5024      * @type Mixed
5025      */
5026     
5027     getAutoCreate : function(){
5028         
5029         var cfg = {
5030             tag: 'form',
5031             method : this.method || 'POST',
5032             id : this.id || Roo.id(),
5033             cls : ''
5034         }
5035         if (this.parent().xtype.match(/^Nav/)) {
5036             cfg.cls = 'navbar-form navbar-' + this.align;
5037             
5038         }
5039         
5040         if (this.labelAlign == 'left' ) {
5041             cfg.cls += ' form-horizontal';
5042         }
5043         
5044         
5045         return cfg;
5046     },
5047     initEvents : function()
5048     {
5049         this.el.on('submit', this.onSubmit, this);
5050         
5051         
5052     },
5053     // private
5054     onSubmit : function(e){
5055         e.stopEvent();
5056     },
5057     
5058      /**
5059      * Returns true if client-side validation on the form is successful.
5060      * @return Boolean
5061      */
5062     isValid : function(){
5063         var items = this.getItems();
5064         var valid = true;
5065         items.each(function(f){
5066            if(!f.validate()){
5067                valid = false;
5068                
5069            }
5070         });
5071         return valid;
5072     },
5073     /**
5074      * Returns true if any fields in this form have changed since their original load.
5075      * @return Boolean
5076      */
5077     isDirty : function(){
5078         var dirty = false;
5079         var items = this.getItems();
5080         items.each(function(f){
5081            if(f.isDirty()){
5082                dirty = true;
5083                return false;
5084            }
5085            return true;
5086         });
5087         return dirty;
5088     },
5089      /**
5090      * Performs a predefined action (submit or load) or custom actions you define on this form.
5091      * @param {String} actionName The name of the action type
5092      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
5093      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5094      * accept other config options):
5095      * <pre>
5096 Property          Type             Description
5097 ----------------  ---------------  ----------------------------------------------------------------------------------
5098 url               String           The url for the action (defaults to the form's url)
5099 method            String           The form method to use (defaults to the form's method, or POST if not defined)
5100 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
5101 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
5102                                    validate the form on the client (defaults to false)
5103      * </pre>
5104      * @return {BasicForm} this
5105      */
5106     doAction : function(action, options){
5107         if(typeof action == 'string'){
5108             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5109         }
5110         if(this.fireEvent('beforeaction', this, action) !== false){
5111             this.beforeAction(action);
5112             action.run.defer(100, action);
5113         }
5114         return this;
5115     },
5116     
5117     // private
5118     beforeAction : function(action){
5119         var o = action.options;
5120         
5121         // not really supported yet.. ??
5122         
5123         //if(this.waitMsgTarget === true){
5124             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
5125         //}else if(this.waitMsgTarget){
5126         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
5127         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
5128         //}else {
5129         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
5130        // }
5131          
5132     },
5133
5134     // private
5135     afterAction : function(action, success){
5136         this.activeAction = null;
5137         var o = action.options;
5138         
5139         //if(this.waitMsgTarget === true){
5140             this.el.unmask();
5141         //}else if(this.waitMsgTarget){
5142         //    this.waitMsgTarget.unmask();
5143         //}else{
5144         //    Roo.MessageBox.updateProgress(1);
5145         //    Roo.MessageBox.hide();
5146        // }
5147         // 
5148         if(success){
5149             if(o.reset){
5150                 this.reset();
5151             }
5152             Roo.callback(o.success, o.scope, [this, action]);
5153             this.fireEvent('actioncomplete', this, action);
5154             
5155         }else{
5156             
5157             // failure condition..
5158             // we have a scenario where updates need confirming.
5159             // eg. if a locking scenario exists..
5160             // we look for { errors : { needs_confirm : true }} in the response.
5161             if (
5162                 (typeof(action.result) != 'undefined')  &&
5163                 (typeof(action.result.errors) != 'undefined')  &&
5164                 (typeof(action.result.errors.needs_confirm) != 'undefined')
5165            ){
5166                 var _t = this;
5167                 Roo.log("not supported yet");
5168                  /*
5169                 
5170                 Roo.MessageBox.confirm(
5171                     "Change requires confirmation",
5172                     action.result.errorMsg,
5173                     function(r) {
5174                         if (r != 'yes') {
5175                             return;
5176                         }
5177                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
5178                     }
5179                     
5180                 );
5181                 */
5182                 
5183                 
5184                 return;
5185             }
5186             
5187             Roo.callback(o.failure, o.scope, [this, action]);
5188             // show an error message if no failed handler is set..
5189             if (!this.hasListener('actionfailed')) {
5190                 Roo.log("need to add dialog support");
5191                 /*
5192                 Roo.MessageBox.alert("Error",
5193                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5194                         action.result.errorMsg :
5195                         "Saving Failed, please check your entries or try again"
5196                 );
5197                 */
5198             }
5199             
5200             this.fireEvent('actionfailed', this, action);
5201         }
5202         
5203     },
5204     /**
5205      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5206      * @param {String} id The value to search for
5207      * @return Field
5208      */
5209     findField : function(id){
5210         var items = this.getItems();
5211         var field = items.get(id);
5212         if(!field){
5213              items.each(function(f){
5214                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5215                     field = f;
5216                     return false;
5217                 }
5218                 return true;
5219             });
5220         }
5221         return field || null;
5222     },
5223      /**
5224      * Mark fields in this form invalid in bulk.
5225      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5226      * @return {BasicForm} this
5227      */
5228     markInvalid : function(errors){
5229         if(errors instanceof Array){
5230             for(var i = 0, len = errors.length; i < len; i++){
5231                 var fieldError = errors[i];
5232                 var f = this.findField(fieldError.id);
5233                 if(f){
5234                     f.markInvalid(fieldError.msg);
5235                 }
5236             }
5237         }else{
5238             var field, id;
5239             for(id in errors){
5240                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
5241                     field.markInvalid(errors[id]);
5242                 }
5243             }
5244         }
5245         //Roo.each(this.childForms || [], function (f) {
5246         //    f.markInvalid(errors);
5247         //});
5248         
5249         return this;
5250     },
5251
5252     /**
5253      * Set values for fields in this form in bulk.
5254      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
5255      * @return {BasicForm} this
5256      */
5257     setValues : function(values){
5258         if(values instanceof Array){ // array of objects
5259             for(var i = 0, len = values.length; i < len; i++){
5260                 var v = values[i];
5261                 var f = this.findField(v.id);
5262                 if(f){
5263                     f.setValue(v.value);
5264                     if(this.trackResetOnLoad){
5265                         f.originalValue = f.getValue();
5266                     }
5267                 }
5268             }
5269         }else{ // object hash
5270             var field, id;
5271             for(id in values){
5272                 if(typeof values[id] != 'function' && (field = this.findField(id))){
5273                     
5274                     if (field.setFromData && 
5275                         field.valueField && 
5276                         field.displayField &&
5277                         // combos' with local stores can 
5278                         // be queried via setValue()
5279                         // to set their value..
5280                         (field.store && !field.store.isLocal)
5281                         ) {
5282                         // it's a combo
5283                         var sd = { };
5284                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
5285                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
5286                         field.setFromData(sd);
5287                         
5288                     } else {
5289                         field.setValue(values[id]);
5290                     }
5291                     
5292                     
5293                     if(this.trackResetOnLoad){
5294                         field.originalValue = field.getValue();
5295                     }
5296                 }
5297             }
5298         }
5299          
5300         //Roo.each(this.childForms || [], function (f) {
5301         //    f.setValues(values);
5302         //});
5303                 
5304         return this;
5305     },
5306
5307     /**
5308      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
5309      * they are returned as an array.
5310      * @param {Boolean} asString
5311      * @return {Object}
5312      */
5313     getValues : function(asString){
5314         //if (this.childForms) {
5315             // copy values from the child forms
5316         //    Roo.each(this.childForms, function (f) {
5317         //        this.setValues(f.getValues());
5318         //    }, this);
5319         //}
5320         
5321         
5322         
5323         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
5324         if(asString === true){
5325             return fs;
5326         }
5327         return Roo.urlDecode(fs);
5328     },
5329     
5330     /**
5331      * Returns the fields in this form as an object with key/value pairs. 
5332      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
5333      * @return {Object}
5334      */
5335     getFieldValues : function(with_hidden)
5336     {
5337         var items = this.getItems();
5338         var ret = {};
5339         items.each(function(f){
5340             if (!f.getName()) {
5341                 return;
5342             }
5343             var v = f.getValue();
5344             if (f.inputType =='radio') {
5345                 if (typeof(ret[f.getName()]) == 'undefined') {
5346                     ret[f.getName()] = ''; // empty..
5347                 }
5348                 
5349                 if (!f.el.dom.checked) {
5350                     return;
5351                     
5352                 }
5353                 v = f.el.dom.value;
5354                 
5355             }
5356             
5357             // not sure if this supported any more..
5358             if ((typeof(v) == 'object') && f.getRawValue) {
5359                 v = f.getRawValue() ; // dates..
5360             }
5361             // combo boxes where name != hiddenName...
5362             if (f.name != f.getName()) {
5363                 ret[f.name] = f.getRawValue();
5364             }
5365             ret[f.getName()] = v;
5366         });
5367         
5368         return ret;
5369     },
5370
5371     /**
5372      * Clears all invalid messages in this form.
5373      * @return {BasicForm} this
5374      */
5375     clearInvalid : function(){
5376         var items = this.getItems();
5377         
5378         items.each(function(f){
5379            f.clearInvalid();
5380         });
5381         
5382         
5383         
5384         return this;
5385     },
5386
5387     /**
5388      * Resets this form.
5389      * @return {BasicForm} this
5390      */
5391     reset : function(){
5392         var items = this.getItems();
5393         items.each(function(f){
5394             f.reset();
5395         });
5396         
5397         Roo.each(this.childForms || [], function (f) {
5398             f.reset();
5399         });
5400        
5401         
5402         return this;
5403     },
5404     getItems : function()
5405     {
5406         var r=new Roo.util.MixedCollection(false, function(o){
5407             return o.id || (o.id = Roo.id());
5408         });
5409         var iter = function(el) {
5410             if (el.inputEl) {
5411                 r.add(el);
5412             }
5413             if (!el.items) {
5414                 return;
5415             }
5416             Roo.each(el.items,function(e) {
5417                 iter(e);
5418             });
5419             
5420             
5421         };
5422         iter(this);
5423         return r;
5424         
5425         
5426         
5427         
5428     }
5429     
5430 });
5431
5432  
5433 /*
5434  * Based on:
5435  * Ext JS Library 1.1.1
5436  * Copyright(c) 2006-2007, Ext JS, LLC.
5437  *
5438  * Originally Released Under LGPL - original licence link has changed is not relivant.
5439  *
5440  * Fork - LGPL
5441  * <script type="text/javascript">
5442  */
5443 /**
5444  * @class Roo.form.VTypes
5445  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5446  * @singleton
5447  */
5448 Roo.form.VTypes = function(){
5449     // closure these in so they are only created once.
5450     var alpha = /^[a-zA-Z_]+$/;
5451     var alphanum = /^[a-zA-Z0-9_]+$/;
5452     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5453     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5454
5455     // All these messages and functions are configurable
5456     return {
5457         /**
5458          * The function used to validate email addresses
5459          * @param {String} value The email address
5460          */
5461         'email' : function(v){
5462             return email.test(v);
5463         },
5464         /**
5465          * The error text to display when the email validation function returns false
5466          * @type String
5467          */
5468         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5469         /**
5470          * The keystroke filter mask to be applied on email input
5471          * @type RegExp
5472          */
5473         'emailMask' : /[a-z0-9_\.\-@]/i,
5474
5475         /**
5476          * The function used to validate URLs
5477          * @param {String} value The URL
5478          */
5479         'url' : function(v){
5480             return url.test(v);
5481         },
5482         /**
5483          * The error text to display when the url validation function returns false
5484          * @type String
5485          */
5486         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5487         
5488         /**
5489          * The function used to validate alpha values
5490          * @param {String} value The value
5491          */
5492         'alpha' : function(v){
5493             return alpha.test(v);
5494         },
5495         /**
5496          * The error text to display when the alpha validation function returns false
5497          * @type String
5498          */
5499         'alphaText' : 'This field should only contain letters and _',
5500         /**
5501          * The keystroke filter mask to be applied on alpha input
5502          * @type RegExp
5503          */
5504         'alphaMask' : /[a-z_]/i,
5505
5506         /**
5507          * The function used to validate alphanumeric values
5508          * @param {String} value The value
5509          */
5510         'alphanum' : function(v){
5511             return alphanum.test(v);
5512         },
5513         /**
5514          * The error text to display when the alphanumeric validation function returns false
5515          * @type String
5516          */
5517         'alphanumText' : 'This field should only contain letters, numbers and _',
5518         /**
5519          * The keystroke filter mask to be applied on alphanumeric input
5520          * @type RegExp
5521          */
5522         'alphanumMask' : /[a-z0-9_]/i
5523     };
5524 }();/*
5525  * - LGPL
5526  *
5527  * Input
5528  * 
5529  */
5530
5531 /**
5532  * @class Roo.bootstrap.Input
5533  * @extends Roo.bootstrap.Component
5534  * Bootstrap Input class
5535  * @cfg {Boolean} disabled is it disabled
5536  * @cfg {String} fieldLabel - the label associated
5537  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5538  * @cfg {String} name name of the input
5539  * @cfg {string} fieldLabel - the label associated
5540  * @cfg {string}  inputType - input / file submit ...
5541  * @cfg {string} placeholder - placeholder to put in text.
5542  * @cfg {string}  before - input group add on before
5543  * @cfg {string} after - input group add on after
5544  * @cfg {string} size - (lg|sm) or leave empty..
5545  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5546  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5547  * @cfg {Number} md colspan out of 12 for computer-sized screens
5548  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5549  * @cfg {string} value default value of the input
5550  * @cfg {Number} labelWidth set the width of label (0-12)
5551  * @cfg {String} labelAlign (top|left)
5552  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5553  * 
5554  * 
5555  * @constructor
5556  * Create a new Input
5557  * @param {Object} config The config object
5558  */
5559
5560 Roo.bootstrap.Input = function(config){
5561     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5562    
5563         this.addEvents({
5564             /**
5565              * @event focus
5566              * Fires when this field receives input focus.
5567              * @param {Roo.form.Field} this
5568              */
5569             focus : true,
5570             /**
5571              * @event blur
5572              * Fires when this field loses input focus.
5573              * @param {Roo.form.Field} this
5574              */
5575             blur : true,
5576             /**
5577              * @event specialkey
5578              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5579              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5580              * @param {Roo.form.Field} this
5581              * @param {Roo.EventObject} e The event object
5582              */
5583             specialkey : true,
5584             /**
5585              * @event change
5586              * Fires just before the field blurs if the field value has changed.
5587              * @param {Roo.form.Field} this
5588              * @param {Mixed} newValue The new value
5589              * @param {Mixed} oldValue The original value
5590              */
5591             change : true,
5592             /**
5593              * @event invalid
5594              * Fires after the field has been marked as invalid.
5595              * @param {Roo.form.Field} this
5596              * @param {String} msg The validation message
5597              */
5598             invalid : true,
5599             /**
5600              * @event valid
5601              * Fires after the field has been validated with no errors.
5602              * @param {Roo.form.Field} this
5603              */
5604             valid : true,
5605              /**
5606              * @event keyup
5607              * Fires after the key up
5608              * @param {Roo.form.Field} this
5609              * @param {Roo.EventObject}  e The event Object
5610              */
5611             keyup : true
5612         });
5613 };
5614
5615 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5616      /**
5617      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5618       automatic validation (defaults to "keyup").
5619      */
5620     validationEvent : "keyup",
5621      /**
5622      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5623      */
5624     validateOnBlur : true,
5625     /**
5626      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5627      */
5628     validationDelay : 250,
5629      /**
5630      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5631      */
5632     focusClass : "x-form-focus",  // not needed???
5633     
5634        
5635     /**
5636      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5637      */
5638     invalidClass : "has-error",
5639     
5640     /**
5641      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5642      */
5643     selectOnFocus : false,
5644     
5645      /**
5646      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5647      */
5648     maskRe : null,
5649        /**
5650      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5651      */
5652     vtype : null,
5653     
5654       /**
5655      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5656      */
5657     disableKeyFilter : false,
5658     
5659        /**
5660      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5661      */
5662     disabled : false,
5663      /**
5664      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5665      */
5666     allowBlank : true,
5667     /**
5668      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5669      */
5670     blankText : "This field is required",
5671     
5672      /**
5673      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5674      */
5675     minLength : 0,
5676     /**
5677      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5678      */
5679     maxLength : Number.MAX_VALUE,
5680     /**
5681      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5682      */
5683     minLengthText : "The minimum length for this field is {0}",
5684     /**
5685      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5686      */
5687     maxLengthText : "The maximum length for this field is {0}",
5688   
5689     
5690     /**
5691      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5692      * If available, this function will be called only after the basic validators all return true, and will be passed the
5693      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5694      */
5695     validator : null,
5696     /**
5697      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5698      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5699      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5700      */
5701     regex : null,
5702     /**
5703      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5704      */
5705     regexText : "",
5706     
5707     
5708     
5709     fieldLabel : '',
5710     inputType : 'text',
5711     
5712     name : false,
5713     placeholder: false,
5714     before : false,
5715     after : false,
5716     size : false,
5717     // private
5718     hasFocus : false,
5719     preventMark: false,
5720     isFormField : true,
5721     value : '',
5722     labelWidth : 2,
5723     labelAlign : false,
5724     readOnly : false,
5725     
5726     parentLabelAlign : function()
5727     {
5728         var parent = this;
5729         while (parent.parent()) {
5730             parent = parent.parent();
5731             if (typeof(parent.labelAlign) !='undefined') {
5732                 return parent.labelAlign;
5733             }
5734         }
5735         return 'left';
5736         
5737     },
5738     
5739     getAutoCreate : function(){
5740         
5741         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5742         
5743         var id = Roo.id();
5744         
5745         var cfg = {};
5746         
5747         if(this.inputType != 'hidden'){
5748             cfg.cls = 'form-group' //input-group
5749         }
5750         
5751         var input =  {
5752             tag: 'input',
5753             id : id,
5754             type : this.inputType,
5755             value : this.value,
5756             cls : 'form-control',
5757             placeholder : this.placeholder || ''
5758             
5759         };
5760         
5761         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5762             input.maxLength = this.maxLength;
5763         }
5764         
5765         if (this.disabled) {
5766             input.disabled=true;
5767         }
5768         
5769         if (this.readOnly) {
5770             input.readonly=true;
5771         }
5772         
5773         if (this.name) {
5774             input.name = this.name;
5775         }
5776         if (this.size) {
5777             input.cls += ' input-' + this.size;
5778         }
5779         var settings=this;
5780         ['xs','sm','md','lg'].map(function(size){
5781             if (settings[size]) {
5782                 cfg.cls += ' col-' + size + '-' + settings[size];
5783             }
5784         });
5785         
5786         var inputblock = input;
5787         
5788         if (this.before || this.after) {
5789             
5790             inputblock = {
5791                 cls : 'input-group',
5792                 cn :  [] 
5793             };
5794             if (this.before && typeof(this.before) == 'string') {
5795                 
5796                 inputblock.cn.push({
5797                     tag :'span',
5798                     cls : 'roo-input-before input-group-addon',
5799                     html : this.before
5800                 });
5801             }
5802             if (this.before && typeof(this.before) == 'object') {
5803                 this.before = Roo.factory(this.before);
5804                 Roo.log(this.before);
5805                 inputblock.cn.push({
5806                     tag :'span',
5807                     cls : 'roo-input-before input-group-' +
5808                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5809                 });
5810             }
5811             
5812             inputblock.cn.push(input);
5813             
5814             if (this.after && typeof(this.after) == 'string') {
5815                 inputblock.cn.push({
5816                     tag :'span',
5817                     cls : 'roo-input-after input-group-addon',
5818                     html : this.after
5819                 });
5820             }
5821             if (this.after && typeof(this.after) == 'object') {
5822                 this.after = Roo.factory(this.after);
5823                 Roo.log(this.after);
5824                 inputblock.cn.push({
5825                     tag :'span',
5826                     cls : 'roo-input-after input-group-' +
5827                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5828                 });
5829             }
5830         };
5831         
5832         if (align ==='left' && this.fieldLabel.length) {
5833                 Roo.log("left and has label");
5834                 cfg.cn = [
5835                     
5836                     {
5837                         tag: 'label',
5838                         'for' :  id,
5839                         cls : 'control-label col-sm-' + this.labelWidth,
5840                         html : this.fieldLabel
5841                         
5842                     },
5843                     {
5844                         cls : "col-sm-" + (12 - this.labelWidth), 
5845                         cn: [
5846                             inputblock
5847                         ]
5848                     }
5849                     
5850                 ];
5851         } else if ( this.fieldLabel.length) {
5852                 Roo.log(" label");
5853                  cfg.cn = [
5854                    
5855                     {
5856                         tag: 'label',
5857                         //cls : 'input-group-addon',
5858                         html : this.fieldLabel
5859                         
5860                     },
5861                     
5862                     inputblock
5863                     
5864                 ];
5865
5866         } else {
5867             
5868                 Roo.log(" no label && no align");
5869                 cfg.cn = [
5870                     
5871                         inputblock
5872                     
5873                 ];
5874                 
5875                 
5876         };
5877         Roo.log('input-parentType: ' + this.parentType);
5878         
5879         if (this.parentType === 'Navbar' &&  this.parent().bar) {
5880            cfg.cls += ' navbar-form';
5881            Roo.log(cfg);
5882         }
5883         
5884         return cfg;
5885         
5886     },
5887     /**
5888      * return the real input element.
5889      */
5890     inputEl: function ()
5891     {
5892         return this.el.select('input.form-control',true).first();
5893     },
5894     setDisabled : function(v)
5895     {
5896         var i  = this.inputEl().dom;
5897         if (!v) {
5898             i.removeAttribute('disabled');
5899             return;
5900             
5901         }
5902         i.setAttribute('disabled','true');
5903     },
5904     initEvents : function()
5905     {
5906         
5907         this.inputEl().on("keydown" , this.fireKey,  this);
5908         this.inputEl().on("focus", this.onFocus,  this);
5909         this.inputEl().on("blur", this.onBlur,  this);
5910         
5911         this.inputEl().relayEvent('keyup', this);
5912
5913         // reference to original value for reset
5914         this.originalValue = this.getValue();
5915         //Roo.form.TextField.superclass.initEvents.call(this);
5916         if(this.validationEvent == 'keyup'){
5917             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
5918             this.inputEl().on('keyup', this.filterValidation, this);
5919         }
5920         else if(this.validationEvent !== false){
5921             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
5922         }
5923         
5924         if(this.selectOnFocus){
5925             this.on("focus", this.preFocus, this);
5926             
5927         }
5928         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
5929             this.inputEl().on("keypress", this.filterKeys, this);
5930         }
5931        /* if(this.grow){
5932             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
5933             this.el.on("click", this.autoSize,  this);
5934         }
5935         */
5936         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
5937             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
5938         }
5939         
5940         if (typeof(this.before) == 'object') {
5941             this.before.render(this.el.select('.roo-input-before',true).first());
5942         }
5943         if (typeof(this.after) == 'object') {
5944             this.after.render(this.el.select('.roo-input-after',true).first());
5945         }
5946         
5947         
5948     },
5949     filterValidation : function(e){
5950         if(!e.isNavKeyPress()){
5951             this.validationTask.delay(this.validationDelay);
5952         }
5953     },
5954      /**
5955      * Validates the field value
5956      * @return {Boolean} True if the value is valid, else false
5957      */
5958     validate : function(){
5959         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
5960         if(this.disabled || this.validateValue(this.getRawValue())){
5961             this.clearInvalid();
5962             return true;
5963         }
5964         return false;
5965     },
5966     
5967     
5968     /**
5969      * Validates a value according to the field's validation rules and marks the field as invalid
5970      * if the validation fails
5971      * @param {Mixed} value The value to validate
5972      * @return {Boolean} True if the value is valid, else false
5973      */
5974     validateValue : function(value){
5975         if(value.length < 1)  { // if it's blank
5976              if(this.allowBlank){
5977                 this.clearInvalid();
5978                 return true;
5979              }else{
5980                 this.markInvalid(this.blankText);
5981                 return false;
5982              }
5983         }
5984         if(value.length < this.minLength){
5985             this.markInvalid(String.format(this.minLengthText, this.minLength));
5986             return false;
5987         }
5988         if(value.length > this.maxLength){
5989             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
5990             return false;
5991         }
5992         if(this.vtype){
5993             var vt = Roo.form.VTypes;
5994             if(!vt[this.vtype](value, this)){
5995                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
5996                 return false;
5997             }
5998         }
5999         if(typeof this.validator == "function"){
6000             var msg = this.validator(value);
6001             if(msg !== true){
6002                 this.markInvalid(msg);
6003                 return false;
6004             }
6005         }
6006         if(this.regex && !this.regex.test(value)){
6007             this.markInvalid(this.regexText);
6008             return false;
6009         }
6010         return true;
6011     },
6012
6013     
6014     
6015      // private
6016     fireKey : function(e){
6017         //Roo.log('field ' + e.getKey());
6018         if(e.isNavKeyPress()){
6019             this.fireEvent("specialkey", this, e);
6020         }
6021     },
6022     focus : function (selectText){
6023         if(this.rendered){
6024             this.inputEl().focus();
6025             if(selectText === true){
6026                 this.inputEl().dom.select();
6027             }
6028         }
6029         return this;
6030     } ,
6031     
6032     onFocus : function(){
6033         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6034            // this.el.addClass(this.focusClass);
6035         }
6036         if(!this.hasFocus){
6037             this.hasFocus = true;
6038             this.startValue = this.getValue();
6039             this.fireEvent("focus", this);
6040         }
6041     },
6042     
6043     beforeBlur : Roo.emptyFn,
6044
6045     
6046     // private
6047     onBlur : function(){
6048         this.beforeBlur();
6049         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6050             //this.el.removeClass(this.focusClass);
6051         }
6052         this.hasFocus = false;
6053         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6054             this.validate();
6055         }
6056         var v = this.getValue();
6057         if(String(v) !== String(this.startValue)){
6058             this.fireEvent('change', this, v, this.startValue);
6059         }
6060         this.fireEvent("blur", this);
6061     },
6062     
6063     /**
6064      * Resets the current field value to the originally loaded value and clears any validation messages
6065      */
6066     reset : function(){
6067         this.setValue(this.originalValue);
6068         this.clearInvalid();
6069     },
6070      /**
6071      * Returns the name of the field
6072      * @return {Mixed} name The name field
6073      */
6074     getName: function(){
6075         return this.name;
6076     },
6077      /**
6078      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
6079      * @return {Mixed} value The field value
6080      */
6081     getValue : function(){
6082         return this.inputEl().getValue();
6083     },
6084     /**
6085      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
6086      * @return {Mixed} value The field value
6087      */
6088     getRawValue : function(){
6089         var v = this.inputEl().getValue();
6090         
6091         return v;
6092     },
6093     
6094     /**
6095      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
6096      * @param {Mixed} value The value to set
6097      */
6098     setRawValue : function(v){
6099         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6100     },
6101     
6102     selectText : function(start, end){
6103         var v = this.getRawValue();
6104         if(v.length > 0){
6105             start = start === undefined ? 0 : start;
6106             end = end === undefined ? v.length : end;
6107             var d = this.inputEl().dom;
6108             if(d.setSelectionRange){
6109                 d.setSelectionRange(start, end);
6110             }else if(d.createTextRange){
6111                 var range = d.createTextRange();
6112                 range.moveStart("character", start);
6113                 range.moveEnd("character", v.length-end);
6114                 range.select();
6115             }
6116         }
6117     },
6118     
6119     /**
6120      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
6121      * @param {Mixed} value The value to set
6122      */
6123     setValue : function(v){
6124         this.value = v;
6125         if(this.rendered){
6126             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6127             this.validate();
6128         }
6129     },
6130     
6131     /*
6132     processValue : function(value){
6133         if(this.stripCharsRe){
6134             var newValue = value.replace(this.stripCharsRe, '');
6135             if(newValue !== value){
6136                 this.setRawValue(newValue);
6137                 return newValue;
6138             }
6139         }
6140         return value;
6141     },
6142   */
6143     preFocus : function(){
6144         
6145         if(this.selectOnFocus){
6146             this.inputEl().dom.select();
6147         }
6148     },
6149     filterKeys : function(e){
6150         var k = e.getKey();
6151         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
6152             return;
6153         }
6154         var c = e.getCharCode(), cc = String.fromCharCode(c);
6155         if(Roo.isIE && (e.isSpecialKey() || !cc)){
6156             return;
6157         }
6158         if(!this.maskRe.test(cc)){
6159             e.stopEvent();
6160         }
6161     },
6162      /**
6163      * Clear any invalid styles/messages for this field
6164      */
6165     clearInvalid : function(){
6166         
6167         if(!this.el || this.preventMark){ // not rendered
6168             return;
6169         }
6170         this.el.removeClass(this.invalidClass);
6171         /*
6172         switch(this.msgTarget){
6173             case 'qtip':
6174                 this.el.dom.qtip = '';
6175                 break;
6176             case 'title':
6177                 this.el.dom.title = '';
6178                 break;
6179             case 'under':
6180                 if(this.errorEl){
6181                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
6182                 }
6183                 break;
6184             case 'side':
6185                 if(this.errorIcon){
6186                     this.errorIcon.dom.qtip = '';
6187                     this.errorIcon.hide();
6188                     this.un('resize', this.alignErrorIcon, this);
6189                 }
6190                 break;
6191             default:
6192                 var t = Roo.getDom(this.msgTarget);
6193                 t.innerHTML = '';
6194                 t.style.display = 'none';
6195                 break;
6196         }
6197         */
6198         this.fireEvent('valid', this);
6199     },
6200      /**
6201      * Mark this field as invalid
6202      * @param {String} msg The validation message
6203      */
6204     markInvalid : function(msg){
6205         if(!this.el  || this.preventMark){ // not rendered
6206             return;
6207         }
6208         this.el.addClass(this.invalidClass);
6209         /*
6210         msg = msg || this.invalidText;
6211         switch(this.msgTarget){
6212             case 'qtip':
6213                 this.el.dom.qtip = msg;
6214                 this.el.dom.qclass = 'x-form-invalid-tip';
6215                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6216                     Roo.QuickTips.enable();
6217                 }
6218                 break;
6219             case 'title':
6220                 this.el.dom.title = msg;
6221                 break;
6222             case 'under':
6223                 if(!this.errorEl){
6224                     var elp = this.el.findParent('.x-form-element', 5, true);
6225                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
6226                     this.errorEl.setWidth(elp.getWidth(true)-20);
6227                 }
6228                 this.errorEl.update(msg);
6229                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
6230                 break;
6231             case 'side':
6232                 if(!this.errorIcon){
6233                     var elp = this.el.findParent('.x-form-element', 5, true);
6234                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
6235                 }
6236                 this.alignErrorIcon();
6237                 this.errorIcon.dom.qtip = msg;
6238                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
6239                 this.errorIcon.show();
6240                 this.on('resize', this.alignErrorIcon, this);
6241                 break;
6242             default:
6243                 var t = Roo.getDom(this.msgTarget);
6244                 t.innerHTML = msg;
6245                 t.style.display = this.msgDisplay;
6246                 break;
6247         }
6248         */
6249         this.fireEvent('invalid', this, msg);
6250     },
6251     // private
6252     SafariOnKeyDown : function(event)
6253     {
6254         // this is a workaround for a password hang bug on chrome/ webkit.
6255         
6256         var isSelectAll = false;
6257         
6258         if(this.inputEl().dom.selectionEnd > 0){
6259             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
6260         }
6261         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
6262             event.preventDefault();
6263             this.setValue('');
6264             return;
6265         }
6266         
6267         if(isSelectAll){ // backspace and delete key
6268             
6269             event.preventDefault();
6270             // this is very hacky as keydown always get's upper case.
6271             //
6272             var cc = String.fromCharCode(event.getCharCode());
6273             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
6274             
6275         }
6276     },
6277     adjustWidth : function(tag, w){
6278         tag = tag.toLowerCase();
6279         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
6280             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
6281                 if(tag == 'input'){
6282                     return w + 2;
6283                 }
6284                 if(tag == 'textarea'){
6285                     return w-2;
6286                 }
6287             }else if(Roo.isOpera){
6288                 if(tag == 'input'){
6289                     return w + 2;
6290                 }
6291                 if(tag == 'textarea'){
6292                     return w-2;
6293                 }
6294             }
6295         }
6296         return w;
6297     }
6298     
6299 });
6300
6301  
6302 /*
6303  * - LGPL
6304  *
6305  * Input
6306  * 
6307  */
6308
6309 /**
6310  * @class Roo.bootstrap.TextArea
6311  * @extends Roo.bootstrap.Input
6312  * Bootstrap TextArea class
6313  * @cfg {Number} cols Specifies the visible width of a text area
6314  * @cfg {Number} rows Specifies the visible number of lines in a text area
6315  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
6316  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
6317  * @cfg {string} html text
6318  * 
6319  * @constructor
6320  * Create a new TextArea
6321  * @param {Object} config The config object
6322  */
6323
6324 Roo.bootstrap.TextArea = function(config){
6325     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
6326    
6327 };
6328
6329 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
6330      
6331     cols : false,
6332     rows : 5,
6333     readOnly : false,
6334     warp : 'soft',
6335     resize : false,
6336     value: false,
6337     html: false,
6338     
6339     getAutoCreate : function(){
6340         
6341         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6342         
6343         var id = Roo.id();
6344         
6345         var cfg = {};
6346         
6347         var input =  {
6348             tag: 'textarea',
6349             id : id,
6350             warp : this.warp,
6351             rows : this.rows,
6352             value : this.value || '',
6353             html: this.html || '',
6354             cls : 'form-control',
6355             placeholder : this.placeholder || '' 
6356             
6357         };
6358         
6359         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6360             input.maxLength = this.maxLength;
6361         }
6362         
6363         if(this.resize){
6364             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
6365         }
6366         
6367         if(this.cols){
6368             input.cols = this.cols;
6369         }
6370         
6371         if (this.readOnly) {
6372             input.readonly = true;
6373         }
6374         
6375         if (this.name) {
6376             input.name = this.name;
6377         }
6378         
6379         if (this.size) {
6380             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
6381         }
6382         
6383         var settings=this;
6384         ['xs','sm','md','lg'].map(function(size){
6385             if (settings[size]) {
6386                 cfg.cls += ' col-' + size + '-' + settings[size];
6387             }
6388         });
6389         
6390         var inputblock = input;
6391         
6392         if (this.before || this.after) {
6393             
6394             inputblock = {
6395                 cls : 'input-group',
6396                 cn :  [] 
6397             };
6398             if (this.before) {
6399                 inputblock.cn.push({
6400                     tag :'span',
6401                     cls : 'input-group-addon',
6402                     html : this.before
6403                 });
6404             }
6405             inputblock.cn.push(input);
6406             if (this.after) {
6407                 inputblock.cn.push({
6408                     tag :'span',
6409                     cls : 'input-group-addon',
6410                     html : this.after
6411                 });
6412             }
6413             
6414         }
6415         
6416         if (align ==='left' && this.fieldLabel.length) {
6417                 Roo.log("left and has label");
6418                 cfg.cn = [
6419                     
6420                     {
6421                         tag: 'label',
6422                         'for' :  id,
6423                         cls : 'control-label col-sm-' + this.labelWidth,
6424                         html : this.fieldLabel
6425                         
6426                     },
6427                     {
6428                         cls : "col-sm-" + (12 - this.labelWidth), 
6429                         cn: [
6430                             inputblock
6431                         ]
6432                     }
6433                     
6434                 ];
6435         } else if ( this.fieldLabel.length) {
6436                 Roo.log(" label");
6437                  cfg.cn = [
6438                    
6439                     {
6440                         tag: 'label',
6441                         //cls : 'input-group-addon',
6442                         html : this.fieldLabel
6443                         
6444                     },
6445                     
6446                     inputblock
6447                     
6448                 ];
6449
6450         } else {
6451             
6452                    Roo.log(" no label && no align");
6453                 cfg.cn = [
6454                     
6455                         inputblock
6456                     
6457                 ];
6458                 
6459                 
6460         }
6461         
6462         if (this.disabled) {
6463             input.disabled=true;
6464         }
6465         
6466         return cfg;
6467         
6468     },
6469     /**
6470      * return the real textarea element.
6471      */
6472     inputEl: function ()
6473     {
6474         return this.el.select('textarea.form-control',true).first();
6475     }
6476 });
6477
6478  
6479 /*
6480  * - LGPL
6481  *
6482  * trigger field - base class for combo..
6483  * 
6484  */
6485  
6486 /**
6487  * @class Roo.bootstrap.TriggerField
6488  * @extends Roo.bootstrap.Input
6489  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6490  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6491  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6492  * for which you can provide a custom implementation.  For example:
6493  * <pre><code>
6494 var trigger = new Roo.bootstrap.TriggerField();
6495 trigger.onTriggerClick = myTriggerFn;
6496 trigger.applyTo('my-field');
6497 </code></pre>
6498  *
6499  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6500  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6501  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6502  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6503  * @constructor
6504  * Create a new TriggerField.
6505  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6506  * to the base TextField)
6507  */
6508 Roo.bootstrap.TriggerField = function(config){
6509     this.mimicing = false;
6510     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6511 };
6512
6513 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6514     /**
6515      * @cfg {String} triggerClass A CSS class to apply to the trigger
6516      */
6517      /**
6518      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6519      */
6520     hideTrigger:false,
6521
6522     /** @cfg {Boolean} grow @hide */
6523     /** @cfg {Number} growMin @hide */
6524     /** @cfg {Number} growMax @hide */
6525
6526     /**
6527      * @hide 
6528      * @method
6529      */
6530     autoSize: Roo.emptyFn,
6531     // private
6532     monitorTab : true,
6533     // private
6534     deferHeight : true,
6535
6536     
6537     actionMode : 'wrap',
6538     
6539     
6540     
6541     getAutoCreate : function(){
6542        
6543         var parent = this.parent();
6544         
6545         var align = this.labelAlign || this.parentLabelAlign();
6546         
6547         var id = Roo.id();
6548         
6549         var cfg = {
6550             cls: 'form-group' //input-group
6551         };
6552         
6553         
6554         var input =  {
6555             tag: 'input',
6556             id : id,
6557             type : this.inputType,
6558             cls : 'form-control',
6559             autocomplete: 'off',
6560             placeholder : this.placeholder || '' 
6561             
6562         };
6563         if (this.name) {
6564             input.name = this.name;
6565         }
6566         if (this.size) {
6567             input.cls += ' input-' + this.size;
6568         }
6569         
6570         if (this.disabled) {
6571             input.disabled=true;
6572         }
6573         
6574         var inputblock = input;
6575         
6576         if (this.before || this.after) {
6577             
6578             inputblock = {
6579                 cls : 'input-group',
6580                 cn :  [] 
6581             };
6582             if (this.before) {
6583                 inputblock.cn.push({
6584                     tag :'span',
6585                     cls : 'input-group-addon',
6586                     html : this.before
6587                 });
6588             }
6589             inputblock.cn.push(input);
6590             if (this.after) {
6591                 inputblock.cn.push({
6592                     tag :'span',
6593                     cls : 'input-group-addon',
6594                     html : this.after
6595                 });
6596             }
6597             
6598         };
6599         
6600         var box = {
6601             tag: 'div',
6602             cn: [
6603                 {
6604                     tag: 'input',
6605                     type : 'hidden',
6606                     cls: 'form-hidden-field'
6607                 },
6608                 inputblock
6609             ]
6610             
6611         };
6612         
6613         if(this.multiple){
6614             Roo.log('multiple');
6615             
6616             box = {
6617                 tag: 'div',
6618                 cn: [
6619                     {
6620                         tag: 'input',
6621                         type : 'hidden',
6622                         cls: 'form-hidden-field'
6623                     },
6624                     {
6625                         tag: 'ul',
6626                         cls: 'select2-choices',
6627                         cn:[
6628                             {
6629                                 tag: 'li',
6630                                 cls: 'select2-search-field',
6631                                 cn: [
6632
6633                                     inputblock
6634                                 ]
6635                             }
6636                         ]
6637                     }
6638                 ]
6639             }
6640         };
6641         
6642         var combobox = {
6643             cls: 'select2-container input-group',
6644             cn: [
6645                 box,
6646                 {
6647                     tag: 'ul',
6648                     cls: 'typeahead typeahead-long dropdown-menu',
6649                     style: 'display:none'
6650                 }
6651             ]
6652         };
6653         
6654         if(!this.multiple){
6655             combobox.cn.push({
6656                 tag :'span',
6657                 cls : 'input-group-addon btn dropdown-toggle',
6658                 cn : [
6659                     {
6660                         tag: 'span',
6661                         cls: 'caret'
6662                     },
6663                     {
6664                         tag: 'span',
6665                         cls: 'combobox-clear',
6666                         cn  : [
6667                             {
6668                                 tag : 'i',
6669                                 cls: 'icon-remove'
6670                             }
6671                         ]
6672                     }
6673                 ]
6674
6675             })
6676         }
6677         
6678         if(this.multiple){
6679             combobox.cls += ' select2-container-multi';
6680         }
6681         
6682         if (align ==='left' && this.fieldLabel.length) {
6683             
6684                 Roo.log("left and has label");
6685                 cfg.cn = [
6686                     
6687                     {
6688                         tag: 'label',
6689                         'for' :  id,
6690                         cls : 'control-label col-sm-' + this.labelWidth,
6691                         html : this.fieldLabel
6692                         
6693                     },
6694                     {
6695                         cls : "col-sm-" + (12 - this.labelWidth), 
6696                         cn: [
6697                             combobox
6698                         ]
6699                     }
6700                     
6701                 ];
6702         } else if ( this.fieldLabel.length) {
6703                 Roo.log(" label");
6704                  cfg.cn = [
6705                    
6706                     {
6707                         tag: 'label',
6708                         //cls : 'input-group-addon',
6709                         html : this.fieldLabel
6710                         
6711                     },
6712                     
6713                     combobox
6714                     
6715                 ];
6716
6717         } else {
6718             
6719                 Roo.log(" no label && no align");
6720                 cfg = combobox
6721                      
6722                 
6723         }
6724          
6725         var settings=this;
6726         ['xs','sm','md','lg'].map(function(size){
6727             if (settings[size]) {
6728                 cfg.cls += ' col-' + size + '-' + settings[size];
6729             }
6730         });
6731         
6732         return cfg;
6733         
6734     },
6735     
6736     
6737     
6738     // private
6739     onResize : function(w, h){
6740 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6741 //        if(typeof w == 'number'){
6742 //            var x = w - this.trigger.getWidth();
6743 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6744 //            this.trigger.setStyle('left', x+'px');
6745 //        }
6746     },
6747
6748     // private
6749     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6750
6751     // private
6752     getResizeEl : function(){
6753         return this.inputEl();
6754     },
6755
6756     // private
6757     getPositionEl : function(){
6758         return this.inputEl();
6759     },
6760
6761     // private
6762     alignErrorIcon : function(){
6763         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6764     },
6765
6766     // private
6767     initEvents : function(){
6768         
6769         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6770         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6771         if(!this.multiple){
6772             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6773             if(this.hideTrigger){
6774                 this.trigger.setDisplayed(false);
6775             }
6776             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6777         }
6778         
6779         if(this.multiple){
6780             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6781         }
6782         
6783         //this.trigger.addClassOnOver('x-form-trigger-over');
6784         //this.trigger.addClassOnClick('x-form-trigger-click');
6785         
6786         //if(!this.width){
6787         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6788         //}
6789     },
6790
6791     // private
6792     initTrigger : function(){
6793        
6794     },
6795
6796     // private
6797     onDestroy : function(){
6798         if(this.trigger){
6799             this.trigger.removeAllListeners();
6800           //  this.trigger.remove();
6801         }
6802         //if(this.wrap){
6803         //    this.wrap.remove();
6804         //}
6805         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6806     },
6807
6808     // private
6809     onFocus : function(){
6810         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6811         /*
6812         if(!this.mimicing){
6813             this.wrap.addClass('x-trigger-wrap-focus');
6814             this.mimicing = true;
6815             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6816             if(this.monitorTab){
6817                 this.el.on("keydown", this.checkTab, this);
6818             }
6819         }
6820         */
6821     },
6822
6823     // private
6824     checkTab : function(e){
6825         if(e.getKey() == e.TAB){
6826             this.triggerBlur();
6827         }
6828     },
6829
6830     // private
6831     onBlur : function(){
6832         // do nothing
6833     },
6834
6835     // private
6836     mimicBlur : function(e, t){
6837         /*
6838         if(!this.wrap.contains(t) && this.validateBlur()){
6839             this.triggerBlur();
6840         }
6841         */
6842     },
6843
6844     // private
6845     triggerBlur : function(){
6846         this.mimicing = false;
6847         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6848         if(this.monitorTab){
6849             this.el.un("keydown", this.checkTab, this);
6850         }
6851         //this.wrap.removeClass('x-trigger-wrap-focus');
6852         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6853     },
6854
6855     // private
6856     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6857     validateBlur : function(e, t){
6858         return true;
6859     },
6860
6861     // private
6862     onDisable : function(){
6863         this.inputEl().dom.disabled = true;
6864         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6865         //if(this.wrap){
6866         //    this.wrap.addClass('x-item-disabled');
6867         //}
6868     },
6869
6870     // private
6871     onEnable : function(){
6872         this.inputEl().dom.disabled = false;
6873         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6874         //if(this.wrap){
6875         //    this.el.removeClass('x-item-disabled');
6876         //}
6877     },
6878
6879     // private
6880     onShow : function(){
6881         var ae = this.getActionEl();
6882         
6883         if(ae){
6884             ae.dom.style.display = '';
6885             ae.dom.style.visibility = 'visible';
6886         }
6887     },
6888
6889     // private
6890     
6891     onHide : function(){
6892         var ae = this.getActionEl();
6893         ae.dom.style.display = 'none';
6894     },
6895
6896     /**
6897      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
6898      * by an implementing function.
6899      * @method
6900      * @param {EventObject} e
6901      */
6902     onTriggerClick : Roo.emptyFn
6903 });
6904  /*
6905  * Based on:
6906  * Ext JS Library 1.1.1
6907  * Copyright(c) 2006-2007, Ext JS, LLC.
6908  *
6909  * Originally Released Under LGPL - original licence link has changed is not relivant.
6910  *
6911  * Fork - LGPL
6912  * <script type="text/javascript">
6913  */
6914
6915
6916 /**
6917  * @class Roo.data.SortTypes
6918  * @singleton
6919  * Defines the default sorting (casting?) comparison functions used when sorting data.
6920  */
6921 Roo.data.SortTypes = {
6922     /**
6923      * Default sort that does nothing
6924      * @param {Mixed} s The value being converted
6925      * @return {Mixed} The comparison value
6926      */
6927     none : function(s){
6928         return s;
6929     },
6930     
6931     /**
6932      * The regular expression used to strip tags
6933      * @type {RegExp}
6934      * @property
6935      */
6936     stripTagsRE : /<\/?[^>]+>/gi,
6937     
6938     /**
6939      * Strips all HTML tags to sort on text only
6940      * @param {Mixed} s The value being converted
6941      * @return {String} The comparison value
6942      */
6943     asText : function(s){
6944         return String(s).replace(this.stripTagsRE, "");
6945     },
6946     
6947     /**
6948      * Strips all HTML tags to sort on text only - Case insensitive
6949      * @param {Mixed} s The value being converted
6950      * @return {String} The comparison value
6951      */
6952     asUCText : function(s){
6953         return String(s).toUpperCase().replace(this.stripTagsRE, "");
6954     },
6955     
6956     /**
6957      * Case insensitive string
6958      * @param {Mixed} s The value being converted
6959      * @return {String} The comparison value
6960      */
6961     asUCString : function(s) {
6962         return String(s).toUpperCase();
6963     },
6964     
6965     /**
6966      * Date sorting
6967      * @param {Mixed} s The value being converted
6968      * @return {Number} The comparison value
6969      */
6970     asDate : function(s) {
6971         if(!s){
6972             return 0;
6973         }
6974         if(s instanceof Date){
6975             return s.getTime();
6976         }
6977         return Date.parse(String(s));
6978     },
6979     
6980     /**
6981      * Float sorting
6982      * @param {Mixed} s The value being converted
6983      * @return {Float} The comparison value
6984      */
6985     asFloat : function(s) {
6986         var val = parseFloat(String(s).replace(/,/g, ""));
6987         if(isNaN(val)) val = 0;
6988         return val;
6989     },
6990     
6991     /**
6992      * Integer sorting
6993      * @param {Mixed} s The value being converted
6994      * @return {Number} The comparison value
6995      */
6996     asInt : function(s) {
6997         var val = parseInt(String(s).replace(/,/g, ""));
6998         if(isNaN(val)) val = 0;
6999         return val;
7000     }
7001 };/*
7002  * Based on:
7003  * Ext JS Library 1.1.1
7004  * Copyright(c) 2006-2007, Ext JS, LLC.
7005  *
7006  * Originally Released Under LGPL - original licence link has changed is not relivant.
7007  *
7008  * Fork - LGPL
7009  * <script type="text/javascript">
7010  */
7011
7012 /**
7013 * @class Roo.data.Record
7014  * Instances of this class encapsulate both record <em>definition</em> information, and record
7015  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7016  * to access Records cached in an {@link Roo.data.Store} object.<br>
7017  * <p>
7018  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7019  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7020  * objects.<br>
7021  * <p>
7022  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7023  * @constructor
7024  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7025  * {@link #create}. The parameters are the same.
7026  * @param {Array} data An associative Array of data values keyed by the field name.
7027  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7028  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7029  * not specified an integer id is generated.
7030  */
7031 Roo.data.Record = function(data, id){
7032     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7033     this.data = data;
7034 };
7035
7036 /**
7037  * Generate a constructor for a specific record layout.
7038  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7039  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7040  * Each field definition object may contain the following properties: <ul>
7041  * <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,
7042  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7043  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7044  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7045  * is being used, then this is a string containing the javascript expression to reference the data relative to 
7046  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7047  * to the data item relative to the record element. If the mapping expression is the same as the field name,
7048  * this may be omitted.</p></li>
7049  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7050  * <ul><li>auto (Default, implies no conversion)</li>
7051  * <li>string</li>
7052  * <li>int</li>
7053  * <li>float</li>
7054  * <li>boolean</li>
7055  * <li>date</li></ul></p></li>
7056  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7057  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7058  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7059  * by the Reader into an object that will be stored in the Record. It is passed the
7060  * following parameters:<ul>
7061  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7062  * </ul></p></li>
7063  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7064  * </ul>
7065  * <br>usage:<br><pre><code>
7066 var TopicRecord = Roo.data.Record.create(
7067     {name: 'title', mapping: 'topic_title'},
7068     {name: 'author', mapping: 'username'},
7069     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7070     {name: 'lastPost', mapping: 'post_time', type: 'date'},
7071     {name: 'lastPoster', mapping: 'user2'},
7072     {name: 'excerpt', mapping: 'post_text'}
7073 );
7074
7075 var myNewRecord = new TopicRecord({
7076     title: 'Do my job please',
7077     author: 'noobie',
7078     totalPosts: 1,
7079     lastPost: new Date(),
7080     lastPoster: 'Animal',
7081     excerpt: 'No way dude!'
7082 });
7083 myStore.add(myNewRecord);
7084 </code></pre>
7085  * @method create
7086  * @static
7087  */
7088 Roo.data.Record.create = function(o){
7089     var f = function(){
7090         f.superclass.constructor.apply(this, arguments);
7091     };
7092     Roo.extend(f, Roo.data.Record);
7093     var p = f.prototype;
7094     p.fields = new Roo.util.MixedCollection(false, function(field){
7095         return field.name;
7096     });
7097     for(var i = 0, len = o.length; i < len; i++){
7098         p.fields.add(new Roo.data.Field(o[i]));
7099     }
7100     f.getField = function(name){
7101         return p.fields.get(name);  
7102     };
7103     return f;
7104 };
7105
7106 Roo.data.Record.AUTO_ID = 1000;
7107 Roo.data.Record.EDIT = 'edit';
7108 Roo.data.Record.REJECT = 'reject';
7109 Roo.data.Record.COMMIT = 'commit';
7110
7111 Roo.data.Record.prototype = {
7112     /**
7113      * Readonly flag - true if this record has been modified.
7114      * @type Boolean
7115      */
7116     dirty : false,
7117     editing : false,
7118     error: null,
7119     modified: null,
7120
7121     // private
7122     join : function(store){
7123         this.store = store;
7124     },
7125
7126     /**
7127      * Set the named field to the specified value.
7128      * @param {String} name The name of the field to set.
7129      * @param {Object} value The value to set the field to.
7130      */
7131     set : function(name, value){
7132         if(this.data[name] == value){
7133             return;
7134         }
7135         this.dirty = true;
7136         if(!this.modified){
7137             this.modified = {};
7138         }
7139         if(typeof this.modified[name] == 'undefined'){
7140             this.modified[name] = this.data[name];
7141         }
7142         this.data[name] = value;
7143         if(!this.editing && this.store){
7144             this.store.afterEdit(this);
7145         }       
7146     },
7147
7148     /**
7149      * Get the value of the named field.
7150      * @param {String} name The name of the field to get the value of.
7151      * @return {Object} The value of the field.
7152      */
7153     get : function(name){
7154         return this.data[name]; 
7155     },
7156
7157     // private
7158     beginEdit : function(){
7159         this.editing = true;
7160         this.modified = {}; 
7161     },
7162
7163     // private
7164     cancelEdit : function(){
7165         this.editing = false;
7166         delete this.modified;
7167     },
7168
7169     // private
7170     endEdit : function(){
7171         this.editing = false;
7172         if(this.dirty && this.store){
7173             this.store.afterEdit(this);
7174         }
7175     },
7176
7177     /**
7178      * Usually called by the {@link Roo.data.Store} which owns the Record.
7179      * Rejects all changes made to the Record since either creation, or the last commit operation.
7180      * Modified fields are reverted to their original values.
7181      * <p>
7182      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7183      * of reject operations.
7184      */
7185     reject : function(){
7186         var m = this.modified;
7187         for(var n in m){
7188             if(typeof m[n] != "function"){
7189                 this.data[n] = m[n];
7190             }
7191         }
7192         this.dirty = false;
7193         delete this.modified;
7194         this.editing = false;
7195         if(this.store){
7196             this.store.afterReject(this);
7197         }
7198     },
7199
7200     /**
7201      * Usually called by the {@link Roo.data.Store} which owns the Record.
7202      * Commits all changes made to the Record since either creation, or the last commit operation.
7203      * <p>
7204      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7205      * of commit operations.
7206      */
7207     commit : function(){
7208         this.dirty = false;
7209         delete this.modified;
7210         this.editing = false;
7211         if(this.store){
7212             this.store.afterCommit(this);
7213         }
7214     },
7215
7216     // private
7217     hasError : function(){
7218         return this.error != null;
7219     },
7220
7221     // private
7222     clearError : function(){
7223         this.error = null;
7224     },
7225
7226     /**
7227      * Creates a copy of this record.
7228      * @param {String} id (optional) A new record id if you don't want to use this record's id
7229      * @return {Record}
7230      */
7231     copy : function(newId) {
7232         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
7233     }
7234 };/*
7235  * Based on:
7236  * Ext JS Library 1.1.1
7237  * Copyright(c) 2006-2007, Ext JS, LLC.
7238  *
7239  * Originally Released Under LGPL - original licence link has changed is not relivant.
7240  *
7241  * Fork - LGPL
7242  * <script type="text/javascript">
7243  */
7244
7245
7246
7247 /**
7248  * @class Roo.data.Store
7249  * @extends Roo.util.Observable
7250  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
7251  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
7252  * <p>
7253  * 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
7254  * has no knowledge of the format of the data returned by the Proxy.<br>
7255  * <p>
7256  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
7257  * instances from the data object. These records are cached and made available through accessor functions.
7258  * @constructor
7259  * Creates a new Store.
7260  * @param {Object} config A config object containing the objects needed for the Store to access data,
7261  * and read the data into Records.
7262  */
7263 Roo.data.Store = function(config){
7264     this.data = new Roo.util.MixedCollection(false);
7265     this.data.getKey = function(o){
7266         return o.id;
7267     };
7268     this.baseParams = {};
7269     // private
7270     this.paramNames = {
7271         "start" : "start",
7272         "limit" : "limit",
7273         "sort" : "sort",
7274         "dir" : "dir",
7275         "multisort" : "_multisort"
7276     };
7277
7278     if(config && config.data){
7279         this.inlineData = config.data;
7280         delete config.data;
7281     }
7282
7283     Roo.apply(this, config);
7284     
7285     if(this.reader){ // reader passed
7286         this.reader = Roo.factory(this.reader, Roo.data);
7287         this.reader.xmodule = this.xmodule || false;
7288         if(!this.recordType){
7289             this.recordType = this.reader.recordType;
7290         }
7291         if(this.reader.onMetaChange){
7292             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
7293         }
7294     }
7295
7296     if(this.recordType){
7297         this.fields = this.recordType.prototype.fields;
7298     }
7299     this.modified = [];
7300
7301     this.addEvents({
7302         /**
7303          * @event datachanged
7304          * Fires when the data cache has changed, and a widget which is using this Store
7305          * as a Record cache should refresh its view.
7306          * @param {Store} this
7307          */
7308         datachanged : true,
7309         /**
7310          * @event metachange
7311          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
7312          * @param {Store} this
7313          * @param {Object} meta The JSON metadata
7314          */
7315         metachange : true,
7316         /**
7317          * @event add
7318          * Fires when Records have been added to the Store
7319          * @param {Store} this
7320          * @param {Roo.data.Record[]} records The array of Records added
7321          * @param {Number} index The index at which the record(s) were added
7322          */
7323         add : true,
7324         /**
7325          * @event remove
7326          * Fires when a Record has been removed from the Store
7327          * @param {Store} this
7328          * @param {Roo.data.Record} record The Record that was removed
7329          * @param {Number} index The index at which the record was removed
7330          */
7331         remove : true,
7332         /**
7333          * @event update
7334          * Fires when a Record has been updated
7335          * @param {Store} this
7336          * @param {Roo.data.Record} record The Record that was updated
7337          * @param {String} operation The update operation being performed.  Value may be one of:
7338          * <pre><code>
7339  Roo.data.Record.EDIT
7340  Roo.data.Record.REJECT
7341  Roo.data.Record.COMMIT
7342          * </code></pre>
7343          */
7344         update : true,
7345         /**
7346          * @event clear
7347          * Fires when the data cache has been cleared.
7348          * @param {Store} this
7349          */
7350         clear : true,
7351         /**
7352          * @event beforeload
7353          * Fires before a request is made for a new data object.  If the beforeload handler returns false
7354          * the load action will be canceled.
7355          * @param {Store} this
7356          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7357          */
7358         beforeload : true,
7359         /**
7360          * @event beforeloadadd
7361          * Fires after a new set of Records has been loaded.
7362          * @param {Store} this
7363          * @param {Roo.data.Record[]} records The Records that were loaded
7364          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7365          */
7366         beforeloadadd : true,
7367         /**
7368          * @event load
7369          * Fires after a new set of Records has been loaded, before they are added to the store.
7370          * @param {Store} this
7371          * @param {Roo.data.Record[]} records The Records that were loaded
7372          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7373          * @params {Object} return from reader
7374          */
7375         load : true,
7376         /**
7377          * @event loadexception
7378          * Fires if an exception occurs in the Proxy during loading.
7379          * Called with the signature of the Proxy's "loadexception" event.
7380          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
7381          * 
7382          * @param {Proxy} 
7383          * @param {Object} return from JsonData.reader() - success, totalRecords, records
7384          * @param {Object} load options 
7385          * @param {Object} jsonData from your request (normally this contains the Exception)
7386          */
7387         loadexception : true
7388     });
7389     
7390     if(this.proxy){
7391         this.proxy = Roo.factory(this.proxy, Roo.data);
7392         this.proxy.xmodule = this.xmodule || false;
7393         this.relayEvents(this.proxy,  ["loadexception"]);
7394     }
7395     this.sortToggle = {};
7396     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
7397
7398     Roo.data.Store.superclass.constructor.call(this);
7399
7400     if(this.inlineData){
7401         this.loadData(this.inlineData);
7402         delete this.inlineData;
7403     }
7404 };
7405
7406 Roo.extend(Roo.data.Store, Roo.util.Observable, {
7407      /**
7408     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
7409     * without a remote query - used by combo/forms at present.
7410     */
7411     
7412     /**
7413     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7414     */
7415     /**
7416     * @cfg {Array} data Inline data to be loaded when the store is initialized.
7417     */
7418     /**
7419     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7420     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7421     */
7422     /**
7423     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7424     * on any HTTP request
7425     */
7426     /**
7427     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7428     */
7429     /**
7430     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7431     */
7432     multiSort: false,
7433     /**
7434     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7435     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7436     */
7437     remoteSort : false,
7438
7439     /**
7440     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7441      * loaded or when a record is removed. (defaults to false).
7442     */
7443     pruneModifiedRecords : false,
7444
7445     // private
7446     lastOptions : null,
7447
7448     /**
7449      * Add Records to the Store and fires the add event.
7450      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7451      */
7452     add : function(records){
7453         records = [].concat(records);
7454         for(var i = 0, len = records.length; i < len; i++){
7455             records[i].join(this);
7456         }
7457         var index = this.data.length;
7458         this.data.addAll(records);
7459         this.fireEvent("add", this, records, index);
7460     },
7461
7462     /**
7463      * Remove a Record from the Store and fires the remove event.
7464      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7465      */
7466     remove : function(record){
7467         var index = this.data.indexOf(record);
7468         this.data.removeAt(index);
7469         if(this.pruneModifiedRecords){
7470             this.modified.remove(record);
7471         }
7472         this.fireEvent("remove", this, record, index);
7473     },
7474
7475     /**
7476      * Remove all Records from the Store and fires the clear event.
7477      */
7478     removeAll : function(){
7479         this.data.clear();
7480         if(this.pruneModifiedRecords){
7481             this.modified = [];
7482         }
7483         this.fireEvent("clear", this);
7484     },
7485
7486     /**
7487      * Inserts Records to the Store at the given index and fires the add event.
7488      * @param {Number} index The start index at which to insert the passed Records.
7489      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7490      */
7491     insert : function(index, records){
7492         records = [].concat(records);
7493         for(var i = 0, len = records.length; i < len; i++){
7494             this.data.insert(index, records[i]);
7495             records[i].join(this);
7496         }
7497         this.fireEvent("add", this, records, index);
7498     },
7499
7500     /**
7501      * Get the index within the cache of the passed Record.
7502      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7503      * @return {Number} The index of the passed Record. Returns -1 if not found.
7504      */
7505     indexOf : function(record){
7506         return this.data.indexOf(record);
7507     },
7508
7509     /**
7510      * Get the index within the cache of the Record with the passed id.
7511      * @param {String} id The id of the Record to find.
7512      * @return {Number} The index of the Record. Returns -1 if not found.
7513      */
7514     indexOfId : function(id){
7515         return this.data.indexOfKey(id);
7516     },
7517
7518     /**
7519      * Get the Record with the specified id.
7520      * @param {String} id The id of the Record to find.
7521      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7522      */
7523     getById : function(id){
7524         return this.data.key(id);
7525     },
7526
7527     /**
7528      * Get the Record at the specified index.
7529      * @param {Number} index The index of the Record to find.
7530      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7531      */
7532     getAt : function(index){
7533         return this.data.itemAt(index);
7534     },
7535
7536     /**
7537      * Returns a range of Records between specified indices.
7538      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7539      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7540      * @return {Roo.data.Record[]} An array of Records
7541      */
7542     getRange : function(start, end){
7543         return this.data.getRange(start, end);
7544     },
7545
7546     // private
7547     storeOptions : function(o){
7548         o = Roo.apply({}, o);
7549         delete o.callback;
7550         delete o.scope;
7551         this.lastOptions = o;
7552     },
7553
7554     /**
7555      * Loads the Record cache from the configured Proxy using the configured Reader.
7556      * <p>
7557      * If using remote paging, then the first load call must specify the <em>start</em>
7558      * and <em>limit</em> properties in the options.params property to establish the initial
7559      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7560      * <p>
7561      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7562      * and this call will return before the new data has been loaded. Perform any post-processing
7563      * in a callback function, or in a "load" event handler.</strong>
7564      * <p>
7565      * @param {Object} options An object containing properties which control loading options:<ul>
7566      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7567      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7568      * passed the following arguments:<ul>
7569      * <li>r : Roo.data.Record[]</li>
7570      * <li>options: Options object from the load call</li>
7571      * <li>success: Boolean success indicator</li></ul></li>
7572      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7573      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7574      * </ul>
7575      */
7576     load : function(options){
7577         options = options || {};
7578         if(this.fireEvent("beforeload", this, options) !== false){
7579             this.storeOptions(options);
7580             var p = Roo.apply(options.params || {}, this.baseParams);
7581             // if meta was not loaded from remote source.. try requesting it.
7582             if (!this.reader.metaFromRemote) {
7583                 p._requestMeta = 1;
7584             }
7585             if(this.sortInfo && this.remoteSort){
7586                 var pn = this.paramNames;
7587                 p[pn["sort"]] = this.sortInfo.field;
7588                 p[pn["dir"]] = this.sortInfo.direction;
7589             }
7590             if (this.multiSort) {
7591                 var pn = this.paramNames;
7592                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7593             }
7594             
7595             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7596         }
7597     },
7598
7599     /**
7600      * Reloads the Record cache from the configured Proxy using the configured Reader and
7601      * the options from the last load operation performed.
7602      * @param {Object} options (optional) An object containing properties which may override the options
7603      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7604      * the most recently used options are reused).
7605      */
7606     reload : function(options){
7607         this.load(Roo.applyIf(options||{}, this.lastOptions));
7608     },
7609
7610     // private
7611     // Called as a callback by the Reader during a load operation.
7612     loadRecords : function(o, options, success){
7613         if(!o || success === false){
7614             if(success !== false){
7615                 this.fireEvent("load", this, [], options, o);
7616             }
7617             if(options.callback){
7618                 options.callback.call(options.scope || this, [], options, false);
7619             }
7620             return;
7621         }
7622         // if data returned failure - throw an exception.
7623         if (o.success === false) {
7624             // show a message if no listener is registered.
7625             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7626                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7627             }
7628             // loadmask wil be hooked into this..
7629             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7630             return;
7631         }
7632         var r = o.records, t = o.totalRecords || r.length;
7633         
7634         this.fireEvent("beforeloadadd", this, r, options, o);
7635         
7636         if(!options || options.add !== true){
7637             if(this.pruneModifiedRecords){
7638                 this.modified = [];
7639             }
7640             for(var i = 0, len = r.length; i < len; i++){
7641                 r[i].join(this);
7642             }
7643             if(this.snapshot){
7644                 this.data = this.snapshot;
7645                 delete this.snapshot;
7646             }
7647             this.data.clear();
7648             this.data.addAll(r);
7649             this.totalLength = t;
7650             this.applySort();
7651             this.fireEvent("datachanged", this);
7652         }else{
7653             this.totalLength = Math.max(t, this.data.length+r.length);
7654             this.add(r);
7655         }
7656         this.fireEvent("load", this, r, options, o);
7657         if(options.callback){
7658             options.callback.call(options.scope || this, r, options, true);
7659         }
7660     },
7661
7662
7663     /**
7664      * Loads data from a passed data block. A Reader which understands the format of the data
7665      * must have been configured in the constructor.
7666      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7667      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7668      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7669      */
7670     loadData : function(o, append){
7671         var r = this.reader.readRecords(o);
7672         this.loadRecords(r, {add: append}, true);
7673     },
7674
7675     /**
7676      * Gets the number of cached records.
7677      * <p>
7678      * <em>If using paging, this may not be the total size of the dataset. If the data object
7679      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7680      * the data set size</em>
7681      */
7682     getCount : function(){
7683         return this.data.length || 0;
7684     },
7685
7686     /**
7687      * Gets the total number of records in the dataset as returned by the server.
7688      * <p>
7689      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7690      * the dataset size</em>
7691      */
7692     getTotalCount : function(){
7693         return this.totalLength || 0;
7694     },
7695
7696     /**
7697      * Returns the sort state of the Store as an object with two properties:
7698      * <pre><code>
7699  field {String} The name of the field by which the Records are sorted
7700  direction {String} The sort order, "ASC" or "DESC"
7701      * </code></pre>
7702      */
7703     getSortState : function(){
7704         return this.sortInfo;
7705     },
7706
7707     // private
7708     applySort : function(){
7709         if(this.sortInfo && !this.remoteSort){
7710             var s = this.sortInfo, f = s.field;
7711             var st = this.fields.get(f).sortType;
7712             var fn = function(r1, r2){
7713                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7714                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7715             };
7716             this.data.sort(s.direction, fn);
7717             if(this.snapshot && this.snapshot != this.data){
7718                 this.snapshot.sort(s.direction, fn);
7719             }
7720         }
7721     },
7722
7723     /**
7724      * Sets the default sort column and order to be used by the next load operation.
7725      * @param {String} fieldName The name of the field to sort by.
7726      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7727      */
7728     setDefaultSort : function(field, dir){
7729         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7730     },
7731
7732     /**
7733      * Sort the Records.
7734      * If remote sorting is used, the sort is performed on the server, and the cache is
7735      * reloaded. If local sorting is used, the cache is sorted internally.
7736      * @param {String} fieldName The name of the field to sort by.
7737      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7738      */
7739     sort : function(fieldName, dir){
7740         var f = this.fields.get(fieldName);
7741         if(!dir){
7742             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7743             
7744             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7745                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7746             }else{
7747                 dir = f.sortDir;
7748             }
7749         }
7750         this.sortToggle[f.name] = dir;
7751         this.sortInfo = {field: f.name, direction: dir};
7752         if(!this.remoteSort){
7753             this.applySort();
7754             this.fireEvent("datachanged", this);
7755         }else{
7756             this.load(this.lastOptions);
7757         }
7758     },
7759
7760     /**
7761      * Calls the specified function for each of the Records in the cache.
7762      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7763      * Returning <em>false</em> aborts and exits the iteration.
7764      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7765      */
7766     each : function(fn, scope){
7767         this.data.each(fn, scope);
7768     },
7769
7770     /**
7771      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7772      * (e.g., during paging).
7773      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7774      */
7775     getModifiedRecords : function(){
7776         return this.modified;
7777     },
7778
7779     // private
7780     createFilterFn : function(property, value, anyMatch){
7781         if(!value.exec){ // not a regex
7782             value = String(value);
7783             if(value.length == 0){
7784                 return false;
7785             }
7786             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7787         }
7788         return function(r){
7789             return value.test(r.data[property]);
7790         };
7791     },
7792
7793     /**
7794      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7795      * @param {String} property A field on your records
7796      * @param {Number} start The record index to start at (defaults to 0)
7797      * @param {Number} end The last record index to include (defaults to length - 1)
7798      * @return {Number} The sum
7799      */
7800     sum : function(property, start, end){
7801         var rs = this.data.items, v = 0;
7802         start = start || 0;
7803         end = (end || end === 0) ? end : rs.length-1;
7804
7805         for(var i = start; i <= end; i++){
7806             v += (rs[i].data[property] || 0);
7807         }
7808         return v;
7809     },
7810
7811     /**
7812      * Filter the records by a specified property.
7813      * @param {String} field A field on your records
7814      * @param {String/RegExp} value Either a string that the field
7815      * should start with or a RegExp to test against the field
7816      * @param {Boolean} anyMatch True to match any part not just the beginning
7817      */
7818     filter : function(property, value, anyMatch){
7819         var fn = this.createFilterFn(property, value, anyMatch);
7820         return fn ? this.filterBy(fn) : this.clearFilter();
7821     },
7822
7823     /**
7824      * Filter by a function. The specified function will be called with each
7825      * record in this data source. If the function returns true the record is included,
7826      * otherwise it is filtered.
7827      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7828      * @param {Object} scope (optional) The scope of the function (defaults to this)
7829      */
7830     filterBy : function(fn, scope){
7831         this.snapshot = this.snapshot || this.data;
7832         this.data = this.queryBy(fn, scope||this);
7833         this.fireEvent("datachanged", this);
7834     },
7835
7836     /**
7837      * Query the records by a specified property.
7838      * @param {String} field A field on your records
7839      * @param {String/RegExp} value Either a string that the field
7840      * should start with or a RegExp to test against the field
7841      * @param {Boolean} anyMatch True to match any part not just the beginning
7842      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7843      */
7844     query : function(property, value, anyMatch){
7845         var fn = this.createFilterFn(property, value, anyMatch);
7846         return fn ? this.queryBy(fn) : this.data.clone();
7847     },
7848
7849     /**
7850      * Query by a function. The specified function will be called with each
7851      * record in this data source. If the function returns true the record is included
7852      * in the results.
7853      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7854      * @param {Object} scope (optional) The scope of the function (defaults to this)
7855       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7856      **/
7857     queryBy : function(fn, scope){
7858         var data = this.snapshot || this.data;
7859         return data.filterBy(fn, scope||this);
7860     },
7861
7862     /**
7863      * Collects unique values for a particular dataIndex from this store.
7864      * @param {String} dataIndex The property to collect
7865      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7866      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7867      * @return {Array} An array of the unique values
7868      **/
7869     collect : function(dataIndex, allowNull, bypassFilter){
7870         var d = (bypassFilter === true && this.snapshot) ?
7871                 this.snapshot.items : this.data.items;
7872         var v, sv, r = [], l = {};
7873         for(var i = 0, len = d.length; i < len; i++){
7874             v = d[i].data[dataIndex];
7875             sv = String(v);
7876             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7877                 l[sv] = true;
7878                 r[r.length] = v;
7879             }
7880         }
7881         return r;
7882     },
7883
7884     /**
7885      * Revert to a view of the Record cache with no filtering applied.
7886      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7887      */
7888     clearFilter : function(suppressEvent){
7889         if(this.snapshot && this.snapshot != this.data){
7890             this.data = this.snapshot;
7891             delete this.snapshot;
7892             if(suppressEvent !== true){
7893                 this.fireEvent("datachanged", this);
7894             }
7895         }
7896     },
7897
7898     // private
7899     afterEdit : function(record){
7900         if(this.modified.indexOf(record) == -1){
7901             this.modified.push(record);
7902         }
7903         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
7904     },
7905     
7906     // private
7907     afterReject : function(record){
7908         this.modified.remove(record);
7909         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
7910     },
7911
7912     // private
7913     afterCommit : function(record){
7914         this.modified.remove(record);
7915         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
7916     },
7917
7918     /**
7919      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
7920      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
7921      */
7922     commitChanges : function(){
7923         var m = this.modified.slice(0);
7924         this.modified = [];
7925         for(var i = 0, len = m.length; i < len; i++){
7926             m[i].commit();
7927         }
7928     },
7929
7930     /**
7931      * Cancel outstanding changes on all changed records.
7932      */
7933     rejectChanges : function(){
7934         var m = this.modified.slice(0);
7935         this.modified = [];
7936         for(var i = 0, len = m.length; i < len; i++){
7937             m[i].reject();
7938         }
7939     },
7940
7941     onMetaChange : function(meta, rtype, o){
7942         this.recordType = rtype;
7943         this.fields = rtype.prototype.fields;
7944         delete this.snapshot;
7945         this.sortInfo = meta.sortInfo || this.sortInfo;
7946         this.modified = [];
7947         this.fireEvent('metachange', this, this.reader.meta);
7948     },
7949     
7950     moveIndex : function(data, type)
7951     {
7952         var index = this.indexOf(data);
7953         
7954         var newIndex = index + type;
7955         
7956         this.remove(data);
7957         
7958         this.insert(newIndex, data);
7959         
7960     }
7961 });/*
7962  * Based on:
7963  * Ext JS Library 1.1.1
7964  * Copyright(c) 2006-2007, Ext JS, LLC.
7965  *
7966  * Originally Released Under LGPL - original licence link has changed is not relivant.
7967  *
7968  * Fork - LGPL
7969  * <script type="text/javascript">
7970  */
7971
7972 /**
7973  * @class Roo.data.SimpleStore
7974  * @extends Roo.data.Store
7975  * Small helper class to make creating Stores from Array data easier.
7976  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
7977  * @cfg {Array} fields An array of field definition objects, or field name strings.
7978  * @cfg {Array} data The multi-dimensional array of data
7979  * @constructor
7980  * @param {Object} config
7981  */
7982 Roo.data.SimpleStore = function(config){
7983     Roo.data.SimpleStore.superclass.constructor.call(this, {
7984         isLocal : true,
7985         reader: new Roo.data.ArrayReader({
7986                 id: config.id
7987             },
7988             Roo.data.Record.create(config.fields)
7989         ),
7990         proxy : new Roo.data.MemoryProxy(config.data)
7991     });
7992     this.load();
7993 };
7994 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
7995  * Based on:
7996  * Ext JS Library 1.1.1
7997  * Copyright(c) 2006-2007, Ext JS, LLC.
7998  *
7999  * Originally Released Under LGPL - original licence link has changed is not relivant.
8000  *
8001  * Fork - LGPL
8002  * <script type="text/javascript">
8003  */
8004
8005 /**
8006 /**
8007  * @extends Roo.data.Store
8008  * @class Roo.data.JsonStore
8009  * Small helper class to make creating Stores for JSON data easier. <br/>
8010 <pre><code>
8011 var store = new Roo.data.JsonStore({
8012     url: 'get-images.php',
8013     root: 'images',
8014     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8015 });
8016 </code></pre>
8017  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8018  * JsonReader and HttpProxy (unless inline data is provided).</b>
8019  * @cfg {Array} fields An array of field definition objects, or field name strings.
8020  * @constructor
8021  * @param {Object} config
8022  */
8023 Roo.data.JsonStore = function(c){
8024     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8025         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8026         reader: new Roo.data.JsonReader(c, c.fields)
8027     }));
8028 };
8029 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8030  * Based on:
8031  * Ext JS Library 1.1.1
8032  * Copyright(c) 2006-2007, Ext JS, LLC.
8033  *
8034  * Originally Released Under LGPL - original licence link has changed is not relivant.
8035  *
8036  * Fork - LGPL
8037  * <script type="text/javascript">
8038  */
8039
8040  
8041 Roo.data.Field = function(config){
8042     if(typeof config == "string"){
8043         config = {name: config};
8044     }
8045     Roo.apply(this, config);
8046     
8047     if(!this.type){
8048         this.type = "auto";
8049     }
8050     
8051     var st = Roo.data.SortTypes;
8052     // named sortTypes are supported, here we look them up
8053     if(typeof this.sortType == "string"){
8054         this.sortType = st[this.sortType];
8055     }
8056     
8057     // set default sortType for strings and dates
8058     if(!this.sortType){
8059         switch(this.type){
8060             case "string":
8061                 this.sortType = st.asUCString;
8062                 break;
8063             case "date":
8064                 this.sortType = st.asDate;
8065                 break;
8066             default:
8067                 this.sortType = st.none;
8068         }
8069     }
8070
8071     // define once
8072     var stripRe = /[\$,%]/g;
8073
8074     // prebuilt conversion function for this field, instead of
8075     // switching every time we're reading a value
8076     if(!this.convert){
8077         var cv, dateFormat = this.dateFormat;
8078         switch(this.type){
8079             case "":
8080             case "auto":
8081             case undefined:
8082                 cv = function(v){ return v; };
8083                 break;
8084             case "string":
8085                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8086                 break;
8087             case "int":
8088                 cv = function(v){
8089                     return v !== undefined && v !== null && v !== '' ?
8090                            parseInt(String(v).replace(stripRe, ""), 10) : '';
8091                     };
8092                 break;
8093             case "float":
8094                 cv = function(v){
8095                     return v !== undefined && v !== null && v !== '' ?
8096                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
8097                     };
8098                 break;
8099             case "bool":
8100             case "boolean":
8101                 cv = function(v){ return v === true || v === "true" || v == 1; };
8102                 break;
8103             case "date":
8104                 cv = function(v){
8105                     if(!v){
8106                         return '';
8107                     }
8108                     if(v instanceof Date){
8109                         return v;
8110                     }
8111                     if(dateFormat){
8112                         if(dateFormat == "timestamp"){
8113                             return new Date(v*1000);
8114                         }
8115                         return Date.parseDate(v, dateFormat);
8116                     }
8117                     var parsed = Date.parse(v);
8118                     return parsed ? new Date(parsed) : null;
8119                 };
8120              break;
8121             
8122         }
8123         this.convert = cv;
8124     }
8125 };
8126
8127 Roo.data.Field.prototype = {
8128     dateFormat: null,
8129     defaultValue: "",
8130     mapping: null,
8131     sortType : null,
8132     sortDir : "ASC"
8133 };/*
8134  * Based on:
8135  * Ext JS Library 1.1.1
8136  * Copyright(c) 2006-2007, Ext JS, LLC.
8137  *
8138  * Originally Released Under LGPL - original licence link has changed is not relivant.
8139  *
8140  * Fork - LGPL
8141  * <script type="text/javascript">
8142  */
8143  
8144 // Base class for reading structured data from a data source.  This class is intended to be
8145 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
8146
8147 /**
8148  * @class Roo.data.DataReader
8149  * Base class for reading structured data from a data source.  This class is intended to be
8150  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
8151  */
8152
8153 Roo.data.DataReader = function(meta, recordType){
8154     
8155     this.meta = meta;
8156     
8157     this.recordType = recordType instanceof Array ? 
8158         Roo.data.Record.create(recordType) : recordType;
8159 };
8160
8161 Roo.data.DataReader.prototype = {
8162      /**
8163      * Create an empty record
8164      * @param {Object} data (optional) - overlay some values
8165      * @return {Roo.data.Record} record created.
8166      */
8167     newRow :  function(d) {
8168         var da =  {};
8169         this.recordType.prototype.fields.each(function(c) {
8170             switch( c.type) {
8171                 case 'int' : da[c.name] = 0; break;
8172                 case 'date' : da[c.name] = new Date(); break;
8173                 case 'float' : da[c.name] = 0.0; break;
8174                 case 'boolean' : da[c.name] = false; break;
8175                 default : da[c.name] = ""; break;
8176             }
8177             
8178         });
8179         return new this.recordType(Roo.apply(da, d));
8180     }
8181     
8182 };/*
8183  * Based on:
8184  * Ext JS Library 1.1.1
8185  * Copyright(c) 2006-2007, Ext JS, LLC.
8186  *
8187  * Originally Released Under LGPL - original licence link has changed is not relivant.
8188  *
8189  * Fork - LGPL
8190  * <script type="text/javascript">
8191  */
8192
8193 /**
8194  * @class Roo.data.DataProxy
8195  * @extends Roo.data.Observable
8196  * This class is an abstract base class for implementations which provide retrieval of
8197  * unformatted data objects.<br>
8198  * <p>
8199  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8200  * (of the appropriate type which knows how to parse the data object) to provide a block of
8201  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8202  * <p>
8203  * Custom implementations must implement the load method as described in
8204  * {@link Roo.data.HttpProxy#load}.
8205  */
8206 Roo.data.DataProxy = function(){
8207     this.addEvents({
8208         /**
8209          * @event beforeload
8210          * Fires before a network request is made to retrieve a data object.
8211          * @param {Object} This DataProxy object.
8212          * @param {Object} params The params parameter to the load function.
8213          */
8214         beforeload : true,
8215         /**
8216          * @event load
8217          * Fires before the load method's callback is called.
8218          * @param {Object} This DataProxy object.
8219          * @param {Object} o The data object.
8220          * @param {Object} arg The callback argument object passed to the load function.
8221          */
8222         load : true,
8223         /**
8224          * @event loadexception
8225          * Fires if an Exception occurs during data retrieval.
8226          * @param {Object} This DataProxy object.
8227          * @param {Object} o The data object.
8228          * @param {Object} arg The callback argument object passed to the load function.
8229          * @param {Object} e The Exception.
8230          */
8231         loadexception : true
8232     });
8233     Roo.data.DataProxy.superclass.constructor.call(this);
8234 };
8235
8236 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
8237
8238     /**
8239      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
8240      */
8241 /*
8242  * Based on:
8243  * Ext JS Library 1.1.1
8244  * Copyright(c) 2006-2007, Ext JS, LLC.
8245  *
8246  * Originally Released Under LGPL - original licence link has changed is not relivant.
8247  *
8248  * Fork - LGPL
8249  * <script type="text/javascript">
8250  */
8251 /**
8252  * @class Roo.data.MemoryProxy
8253  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
8254  * to the Reader when its load method is called.
8255  * @constructor
8256  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
8257  */
8258 Roo.data.MemoryProxy = function(data){
8259     if (data.data) {
8260         data = data.data;
8261     }
8262     Roo.data.MemoryProxy.superclass.constructor.call(this);
8263     this.data = data;
8264 };
8265
8266 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
8267     /**
8268      * Load data from the requested source (in this case an in-memory
8269      * data object passed to the constructor), read the data object into
8270      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8271      * process that block using the passed callback.
8272      * @param {Object} params This parameter is not used by the MemoryProxy class.
8273      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8274      * object into a block of Roo.data.Records.
8275      * @param {Function} callback The function into which to pass the block of Roo.data.records.
8276      * The function must be passed <ul>
8277      * <li>The Record block object</li>
8278      * <li>The "arg" argument from the load function</li>
8279      * <li>A boolean success indicator</li>
8280      * </ul>
8281      * @param {Object} scope The scope in which to call the callback
8282      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8283      */
8284     load : function(params, reader, callback, scope, arg){
8285         params = params || {};
8286         var result;
8287         try {
8288             result = reader.readRecords(this.data);
8289         }catch(e){
8290             this.fireEvent("loadexception", this, arg, null, e);
8291             callback.call(scope, null, arg, false);
8292             return;
8293         }
8294         callback.call(scope, result, arg, true);
8295     },
8296     
8297     // private
8298     update : function(params, records){
8299         
8300     }
8301 });/*
8302  * Based on:
8303  * Ext JS Library 1.1.1
8304  * Copyright(c) 2006-2007, Ext JS, LLC.
8305  *
8306  * Originally Released Under LGPL - original licence link has changed is not relivant.
8307  *
8308  * Fork - LGPL
8309  * <script type="text/javascript">
8310  */
8311 /**
8312  * @class Roo.data.HttpProxy
8313  * @extends Roo.data.DataProxy
8314  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
8315  * configured to reference a certain URL.<br><br>
8316  * <p>
8317  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
8318  * from which the running page was served.<br><br>
8319  * <p>
8320  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
8321  * <p>
8322  * Be aware that to enable the browser to parse an XML document, the server must set
8323  * the Content-Type header in the HTTP response to "text/xml".
8324  * @constructor
8325  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
8326  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
8327  * will be used to make the request.
8328  */
8329 Roo.data.HttpProxy = function(conn){
8330     Roo.data.HttpProxy.superclass.constructor.call(this);
8331     // is conn a conn config or a real conn?
8332     this.conn = conn;
8333     this.useAjax = !conn || !conn.events;
8334   
8335 };
8336
8337 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
8338     // thse are take from connection...
8339     
8340     /**
8341      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
8342      */
8343     /**
8344      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
8345      * extra parameters to each request made by this object. (defaults to undefined)
8346      */
8347     /**
8348      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
8349      *  to each request made by this object. (defaults to undefined)
8350      */
8351     /**
8352      * @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)
8353      */
8354     /**
8355      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
8356      */
8357      /**
8358      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
8359      * @type Boolean
8360      */
8361   
8362
8363     /**
8364      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
8365      * @type Boolean
8366      */
8367     /**
8368      * Return the {@link Roo.data.Connection} object being used by this Proxy.
8369      * @return {Connection} The Connection object. This object may be used to subscribe to events on
8370      * a finer-grained basis than the DataProxy events.
8371      */
8372     getConnection : function(){
8373         return this.useAjax ? Roo.Ajax : this.conn;
8374     },
8375
8376     /**
8377      * Load data from the configured {@link Roo.data.Connection}, read the data object into
8378      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
8379      * process that block using the passed callback.
8380      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8381      * for the request to the remote server.
8382      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8383      * object into a block of Roo.data.Records.
8384      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8385      * The function must be passed <ul>
8386      * <li>The Record block object</li>
8387      * <li>The "arg" argument from the load function</li>
8388      * <li>A boolean success indicator</li>
8389      * </ul>
8390      * @param {Object} scope The scope in which to call the callback
8391      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8392      */
8393     load : function(params, reader, callback, scope, arg){
8394         if(this.fireEvent("beforeload", this, params) !== false){
8395             var  o = {
8396                 params : params || {},
8397                 request: {
8398                     callback : callback,
8399                     scope : scope,
8400                     arg : arg
8401                 },
8402                 reader: reader,
8403                 callback : this.loadResponse,
8404                 scope: this
8405             };
8406             if(this.useAjax){
8407                 Roo.applyIf(o, this.conn);
8408                 if(this.activeRequest){
8409                     Roo.Ajax.abort(this.activeRequest);
8410                 }
8411                 this.activeRequest = Roo.Ajax.request(o);
8412             }else{
8413                 this.conn.request(o);
8414             }
8415         }else{
8416             callback.call(scope||this, null, arg, false);
8417         }
8418     },
8419
8420     // private
8421     loadResponse : function(o, success, response){
8422         delete this.activeRequest;
8423         if(!success){
8424             this.fireEvent("loadexception", this, o, response);
8425             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8426             return;
8427         }
8428         var result;
8429         try {
8430             result = o.reader.read(response);
8431         }catch(e){
8432             this.fireEvent("loadexception", this, o, response, e);
8433             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8434             return;
8435         }
8436         
8437         this.fireEvent("load", this, o, o.request.arg);
8438         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8439     },
8440
8441     // private
8442     update : function(dataSet){
8443
8444     },
8445
8446     // private
8447     updateResponse : function(dataSet){
8448
8449     }
8450 });/*
8451  * Based on:
8452  * Ext JS Library 1.1.1
8453  * Copyright(c) 2006-2007, Ext JS, LLC.
8454  *
8455  * Originally Released Under LGPL - original licence link has changed is not relivant.
8456  *
8457  * Fork - LGPL
8458  * <script type="text/javascript">
8459  */
8460
8461 /**
8462  * @class Roo.data.ScriptTagProxy
8463  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8464  * other than the originating domain of the running page.<br><br>
8465  * <p>
8466  * <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
8467  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8468  * <p>
8469  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8470  * source code that is used as the source inside a &lt;script> tag.<br><br>
8471  * <p>
8472  * In order for the browser to process the returned data, the server must wrap the data object
8473  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8474  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8475  * depending on whether the callback name was passed:
8476  * <p>
8477  * <pre><code>
8478 boolean scriptTag = false;
8479 String cb = request.getParameter("callback");
8480 if (cb != null) {
8481     scriptTag = true;
8482     response.setContentType("text/javascript");
8483 } else {
8484     response.setContentType("application/x-json");
8485 }
8486 Writer out = response.getWriter();
8487 if (scriptTag) {
8488     out.write(cb + "(");
8489 }
8490 out.print(dataBlock.toJsonString());
8491 if (scriptTag) {
8492     out.write(");");
8493 }
8494 </pre></code>
8495  *
8496  * @constructor
8497  * @param {Object} config A configuration object.
8498  */
8499 Roo.data.ScriptTagProxy = function(config){
8500     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8501     Roo.apply(this, config);
8502     this.head = document.getElementsByTagName("head")[0];
8503 };
8504
8505 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8506
8507 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8508     /**
8509      * @cfg {String} url The URL from which to request the data object.
8510      */
8511     /**
8512      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8513      */
8514     timeout : 30000,
8515     /**
8516      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8517      * the server the name of the callback function set up by the load call to process the returned data object.
8518      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8519      * javascript output which calls this named function passing the data object as its only parameter.
8520      */
8521     callbackParam : "callback",
8522     /**
8523      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8524      * name to the request.
8525      */
8526     nocache : true,
8527
8528     /**
8529      * Load data from the configured URL, read the data object into
8530      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8531      * process that block using the passed callback.
8532      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8533      * for the request to the remote server.
8534      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8535      * object into a block of Roo.data.Records.
8536      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8537      * The function must be passed <ul>
8538      * <li>The Record block object</li>
8539      * <li>The "arg" argument from the load function</li>
8540      * <li>A boolean success indicator</li>
8541      * </ul>
8542      * @param {Object} scope The scope in which to call the callback
8543      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8544      */
8545     load : function(params, reader, callback, scope, arg){
8546         if(this.fireEvent("beforeload", this, params) !== false){
8547
8548             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8549
8550             var url = this.url;
8551             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8552             if(this.nocache){
8553                 url += "&_dc=" + (new Date().getTime());
8554             }
8555             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8556             var trans = {
8557                 id : transId,
8558                 cb : "stcCallback"+transId,
8559                 scriptId : "stcScript"+transId,
8560                 params : params,
8561                 arg : arg,
8562                 url : url,
8563                 callback : callback,
8564                 scope : scope,
8565                 reader : reader
8566             };
8567             var conn = this;
8568
8569             window[trans.cb] = function(o){
8570                 conn.handleResponse(o, trans);
8571             };
8572
8573             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8574
8575             if(this.autoAbort !== false){
8576                 this.abort();
8577             }
8578
8579             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8580
8581             var script = document.createElement("script");
8582             script.setAttribute("src", url);
8583             script.setAttribute("type", "text/javascript");
8584             script.setAttribute("id", trans.scriptId);
8585             this.head.appendChild(script);
8586
8587             this.trans = trans;
8588         }else{
8589             callback.call(scope||this, null, arg, false);
8590         }
8591     },
8592
8593     // private
8594     isLoading : function(){
8595         return this.trans ? true : false;
8596     },
8597
8598     /**
8599      * Abort the current server request.
8600      */
8601     abort : function(){
8602         if(this.isLoading()){
8603             this.destroyTrans(this.trans);
8604         }
8605     },
8606
8607     // private
8608     destroyTrans : function(trans, isLoaded){
8609         this.head.removeChild(document.getElementById(trans.scriptId));
8610         clearTimeout(trans.timeoutId);
8611         if(isLoaded){
8612             window[trans.cb] = undefined;
8613             try{
8614                 delete window[trans.cb];
8615             }catch(e){}
8616         }else{
8617             // if hasn't been loaded, wait for load to remove it to prevent script error
8618             window[trans.cb] = function(){
8619                 window[trans.cb] = undefined;
8620                 try{
8621                     delete window[trans.cb];
8622                 }catch(e){}
8623             };
8624         }
8625     },
8626
8627     // private
8628     handleResponse : function(o, trans){
8629         this.trans = false;
8630         this.destroyTrans(trans, true);
8631         var result;
8632         try {
8633             result = trans.reader.readRecords(o);
8634         }catch(e){
8635             this.fireEvent("loadexception", this, o, trans.arg, e);
8636             trans.callback.call(trans.scope||window, null, trans.arg, false);
8637             return;
8638         }
8639         this.fireEvent("load", this, o, trans.arg);
8640         trans.callback.call(trans.scope||window, result, trans.arg, true);
8641     },
8642
8643     // private
8644     handleFailure : function(trans){
8645         this.trans = false;
8646         this.destroyTrans(trans, false);
8647         this.fireEvent("loadexception", this, null, trans.arg);
8648         trans.callback.call(trans.scope||window, null, trans.arg, false);
8649     }
8650 });/*
8651  * Based on:
8652  * Ext JS Library 1.1.1
8653  * Copyright(c) 2006-2007, Ext JS, LLC.
8654  *
8655  * Originally Released Under LGPL - original licence link has changed is not relivant.
8656  *
8657  * Fork - LGPL
8658  * <script type="text/javascript">
8659  */
8660
8661 /**
8662  * @class Roo.data.JsonReader
8663  * @extends Roo.data.DataReader
8664  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8665  * based on mappings in a provided Roo.data.Record constructor.
8666  * 
8667  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8668  * in the reply previously. 
8669  * 
8670  * <p>
8671  * Example code:
8672  * <pre><code>
8673 var RecordDef = Roo.data.Record.create([
8674     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8675     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8676 ]);
8677 var myReader = new Roo.data.JsonReader({
8678     totalProperty: "results",    // The property which contains the total dataset size (optional)
8679     root: "rows",                // The property which contains an Array of row objects
8680     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8681 }, RecordDef);
8682 </code></pre>
8683  * <p>
8684  * This would consume a JSON file like this:
8685  * <pre><code>
8686 { 'results': 2, 'rows': [
8687     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8688     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8689 }
8690 </code></pre>
8691  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8692  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8693  * paged from the remote server.
8694  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8695  * @cfg {String} root name of the property which contains the Array of row objects.
8696  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8697  * @constructor
8698  * Create a new JsonReader
8699  * @param {Object} meta Metadata configuration options
8700  * @param {Object} recordType Either an Array of field definition objects,
8701  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8702  */
8703 Roo.data.JsonReader = function(meta, recordType){
8704     
8705     meta = meta || {};
8706     // set some defaults:
8707     Roo.applyIf(meta, {
8708         totalProperty: 'total',
8709         successProperty : 'success',
8710         root : 'data',
8711         id : 'id'
8712     });
8713     
8714     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8715 };
8716 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8717     
8718     /**
8719      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8720      * Used by Store query builder to append _requestMeta to params.
8721      * 
8722      */
8723     metaFromRemote : false,
8724     /**
8725      * This method is only used by a DataProxy which has retrieved data from a remote server.
8726      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8727      * @return {Object} data A data block which is used by an Roo.data.Store object as
8728      * a cache of Roo.data.Records.
8729      */
8730     read : function(response){
8731         var json = response.responseText;
8732        
8733         var o = /* eval:var:o */ eval("("+json+")");
8734         if(!o) {
8735             throw {message: "JsonReader.read: Json object not found"};
8736         }
8737         
8738         if(o.metaData){
8739             
8740             delete this.ef;
8741             this.metaFromRemote = true;
8742             this.meta = o.metaData;
8743             this.recordType = Roo.data.Record.create(o.metaData.fields);
8744             this.onMetaChange(this.meta, this.recordType, o);
8745         }
8746         return this.readRecords(o);
8747     },
8748
8749     // private function a store will implement
8750     onMetaChange : function(meta, recordType, o){
8751
8752     },
8753
8754     /**
8755          * @ignore
8756          */
8757     simpleAccess: function(obj, subsc) {
8758         return obj[subsc];
8759     },
8760
8761         /**
8762          * @ignore
8763          */
8764     getJsonAccessor: function(){
8765         var re = /[\[\.]/;
8766         return function(expr) {
8767             try {
8768                 return(re.test(expr))
8769                     ? new Function("obj", "return obj." + expr)
8770                     : function(obj){
8771                         return obj[expr];
8772                     };
8773             } catch(e){}
8774             return Roo.emptyFn;
8775         };
8776     }(),
8777
8778     /**
8779      * Create a data block containing Roo.data.Records from an XML document.
8780      * @param {Object} o An object which contains an Array of row objects in the property specified
8781      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8782      * which contains the total size of the dataset.
8783      * @return {Object} data A data block which is used by an Roo.data.Store object as
8784      * a cache of Roo.data.Records.
8785      */
8786     readRecords : function(o){
8787         /**
8788          * After any data loads, the raw JSON data is available for further custom processing.
8789          * @type Object
8790          */
8791         this.o = o;
8792         var s = this.meta, Record = this.recordType,
8793             f = Record.prototype.fields, fi = f.items, fl = f.length;
8794
8795 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8796         if (!this.ef) {
8797             if(s.totalProperty) {
8798                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8799                 }
8800                 if(s.successProperty) {
8801                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8802                 }
8803                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8804                 if (s.id) {
8805                         var g = this.getJsonAccessor(s.id);
8806                         this.getId = function(rec) {
8807                                 var r = g(rec);
8808                                 return (r === undefined || r === "") ? null : r;
8809                         };
8810                 } else {
8811                         this.getId = function(){return null;};
8812                 }
8813             this.ef = [];
8814             for(var jj = 0; jj < fl; jj++){
8815                 f = fi[jj];
8816                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8817                 this.ef[jj] = this.getJsonAccessor(map);
8818             }
8819         }
8820
8821         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8822         if(s.totalProperty){
8823             var vt = parseInt(this.getTotal(o), 10);
8824             if(!isNaN(vt)){
8825                 totalRecords = vt;
8826             }
8827         }
8828         if(s.successProperty){
8829             var vs = this.getSuccess(o);
8830             if(vs === false || vs === 'false'){
8831                 success = false;
8832             }
8833         }
8834         var records = [];
8835             for(var i = 0; i < c; i++){
8836                     var n = root[i];
8837                 var values = {};
8838                 var id = this.getId(n);
8839                 for(var j = 0; j < fl; j++){
8840                     f = fi[j];
8841                 var v = this.ef[j](n);
8842                 if (!f.convert) {
8843                     Roo.log('missing convert for ' + f.name);
8844                     Roo.log(f);
8845                     continue;
8846                 }
8847                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8848                 }
8849                 var record = new Record(values, id);
8850                 record.json = n;
8851                 records[i] = record;
8852             }
8853             return {
8854             raw : o,
8855                 success : success,
8856                 records : records,
8857                 totalRecords : totalRecords
8858             };
8859     }
8860 });/*
8861  * Based on:
8862  * Ext JS Library 1.1.1
8863  * Copyright(c) 2006-2007, Ext JS, LLC.
8864  *
8865  * Originally Released Under LGPL - original licence link has changed is not relivant.
8866  *
8867  * Fork - LGPL
8868  * <script type="text/javascript">
8869  */
8870
8871 /**
8872  * @class Roo.data.ArrayReader
8873  * @extends Roo.data.DataReader
8874  * Data reader class to create an Array of Roo.data.Record objects from an Array.
8875  * Each element of that Array represents a row of data fields. The
8876  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8877  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8878  * <p>
8879  * Example code:.
8880  * <pre><code>
8881 var RecordDef = Roo.data.Record.create([
8882     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
8883     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
8884 ]);
8885 var myReader = new Roo.data.ArrayReader({
8886     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
8887 }, RecordDef);
8888 </code></pre>
8889  * <p>
8890  * This would consume an Array like this:
8891  * <pre><code>
8892 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
8893   </code></pre>
8894  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
8895  * @constructor
8896  * Create a new JsonReader
8897  * @param {Object} meta Metadata configuration options.
8898  * @param {Object} recordType Either an Array of field definition objects
8899  * as specified to {@link Roo.data.Record#create},
8900  * or an {@link Roo.data.Record} object
8901  * created using {@link Roo.data.Record#create}.
8902  */
8903 Roo.data.ArrayReader = function(meta, recordType){
8904     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
8905 };
8906
8907 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
8908     /**
8909      * Create a data block containing Roo.data.Records from an XML document.
8910      * @param {Object} o An Array of row objects which represents the dataset.
8911      * @return {Object} data A data block which is used by an Roo.data.Store object as
8912      * a cache of Roo.data.Records.
8913      */
8914     readRecords : function(o){
8915         var sid = this.meta ? this.meta.id : null;
8916         var recordType = this.recordType, fields = recordType.prototype.fields;
8917         var records = [];
8918         var root = o;
8919             for(var i = 0; i < root.length; i++){
8920                     var n = root[i];
8921                 var values = {};
8922                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
8923                 for(var j = 0, jlen = fields.length; j < jlen; j++){
8924                 var f = fields.items[j];
8925                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
8926                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
8927                 v = f.convert(v);
8928                 values[f.name] = v;
8929             }
8930                 var record = new recordType(values, id);
8931                 record.json = n;
8932                 records[records.length] = record;
8933             }
8934             return {
8935                 records : records,
8936                 totalRecords : records.length
8937             };
8938     }
8939 });/*
8940  * - LGPL
8941  * * 
8942  */
8943
8944 /**
8945  * @class Roo.bootstrap.ComboBox
8946  * @extends Roo.bootstrap.TriggerField
8947  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
8948  * @cfg {Boolean} append (true|false) default false
8949  * @constructor
8950  * Create a new ComboBox.
8951  * @param {Object} config Configuration options
8952  */
8953 Roo.bootstrap.ComboBox = function(config){
8954     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
8955     this.addEvents({
8956         /**
8957          * @event expand
8958          * Fires when the dropdown list is expanded
8959              * @param {Roo.bootstrap.ComboBox} combo This combo box
8960              */
8961         'expand' : true,
8962         /**
8963          * @event collapse
8964          * Fires when the dropdown list is collapsed
8965              * @param {Roo.bootstrap.ComboBox} combo This combo box
8966              */
8967         'collapse' : true,
8968         /**
8969          * @event beforeselect
8970          * Fires before a list item is selected. Return false to cancel the selection.
8971              * @param {Roo.bootstrap.ComboBox} combo This combo box
8972              * @param {Roo.data.Record} record The data record returned from the underlying store
8973              * @param {Number} index The index of the selected item in the dropdown list
8974              */
8975         'beforeselect' : true,
8976         /**
8977          * @event select
8978          * Fires when a list item is selected
8979              * @param {Roo.bootstrap.ComboBox} combo This combo box
8980              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
8981              * @param {Number} index The index of the selected item in the dropdown list
8982              */
8983         'select' : true,
8984         /**
8985          * @event beforequery
8986          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
8987          * The event object passed has these properties:
8988              * @param {Roo.bootstrap.ComboBox} combo This combo box
8989              * @param {String} query The query
8990              * @param {Boolean} forceAll true to force "all" query
8991              * @param {Boolean} cancel true to cancel the query
8992              * @param {Object} e The query event object
8993              */
8994         'beforequery': true,
8995          /**
8996          * @event add
8997          * Fires when the 'add' icon is pressed (add a listener to enable add button)
8998              * @param {Roo.bootstrap.ComboBox} combo This combo box
8999              */
9000         'add' : true,
9001         /**
9002          * @event edit
9003          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9004              * @param {Roo.bootstrap.ComboBox} combo This combo box
9005              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9006              */
9007         'edit' : true,
9008         /**
9009          * @event remove
9010          * Fires when the remove value from the combobox array
9011              * @param {Roo.bootstrap.ComboBox} combo This combo box
9012              */
9013         'remove' : true
9014         
9015     });
9016     
9017     
9018     this.selectedIndex = -1;
9019     if(this.mode == 'local'){
9020         if(config.queryDelay === undefined){
9021             this.queryDelay = 10;
9022         }
9023         if(config.minChars === undefined){
9024             this.minChars = 0;
9025         }
9026     }
9027 };
9028
9029 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9030      
9031     /**
9032      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9033      * rendering into an Roo.Editor, defaults to false)
9034      */
9035     /**
9036      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9037      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9038      */
9039     /**
9040      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9041      */
9042     /**
9043      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9044      * the dropdown list (defaults to undefined, with no header element)
9045      */
9046
9047      /**
9048      * @cfg {String/Roo.Template} tpl The template to use to render the output
9049      */
9050      
9051      /**
9052      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9053      */
9054     listWidth: undefined,
9055     /**
9056      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9057      * mode = 'remote' or 'text' if mode = 'local')
9058      */
9059     displayField: undefined,
9060     /**
9061      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9062      * mode = 'remote' or 'value' if mode = 'local'). 
9063      * Note: use of a valueField requires the user make a selection
9064      * in order for a value to be mapped.
9065      */
9066     valueField: undefined,
9067     
9068     
9069     /**
9070      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9071      * field's data value (defaults to the underlying DOM element's name)
9072      */
9073     hiddenName: undefined,
9074     /**
9075      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9076      */
9077     listClass: '',
9078     /**
9079      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9080      */
9081     selectedClass: 'active',
9082     
9083     /**
9084      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9085      */
9086     shadow:'sides',
9087     /**
9088      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9089      * anchor positions (defaults to 'tl-bl')
9090      */
9091     listAlign: 'tl-bl?',
9092     /**
9093      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9094      */
9095     maxHeight: 300,
9096     /**
9097      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
9098      * query specified by the allQuery config option (defaults to 'query')
9099      */
9100     triggerAction: 'query',
9101     /**
9102      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9103      * (defaults to 4, does not apply if editable = false)
9104      */
9105     minChars : 4,
9106     /**
9107      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9108      * delay (typeAheadDelay) if it matches a known value (defaults to false)
9109      */
9110     typeAhead: false,
9111     /**
9112      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
9113      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
9114      */
9115     queryDelay: 500,
9116     /**
9117      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
9118      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
9119      */
9120     pageSize: 0,
9121     /**
9122      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
9123      * when editable = true (defaults to false)
9124      */
9125     selectOnFocus:false,
9126     /**
9127      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
9128      */
9129     queryParam: 'query',
9130     /**
9131      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
9132      * when mode = 'remote' (defaults to 'Loading...')
9133      */
9134     loadingText: 'Loading...',
9135     /**
9136      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
9137      */
9138     resizable: false,
9139     /**
9140      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
9141      */
9142     handleHeight : 8,
9143     /**
9144      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
9145      * traditional select (defaults to true)
9146      */
9147     editable: true,
9148     /**
9149      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
9150      */
9151     allQuery: '',
9152     /**
9153      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
9154      */
9155     mode: 'remote',
9156     /**
9157      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
9158      * listWidth has a higher value)
9159      */
9160     minListWidth : 70,
9161     /**
9162      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
9163      * allow the user to set arbitrary text into the field (defaults to false)
9164      */
9165     forceSelection:false,
9166     /**
9167      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
9168      * if typeAhead = true (defaults to 250)
9169      */
9170     typeAheadDelay : 250,
9171     /**
9172      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
9173      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
9174      */
9175     valueNotFoundText : undefined,
9176     /**
9177      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
9178      */
9179     blockFocus : false,
9180     
9181     /**
9182      * @cfg {Boolean} disableClear Disable showing of clear button.
9183      */
9184     disableClear : false,
9185     /**
9186      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
9187      */
9188     alwaysQuery : false,
9189     
9190     /**
9191      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
9192      */
9193     multiple : false,
9194     
9195     //private
9196     addicon : false,
9197     editicon: false,
9198     
9199     page: 0,
9200     hasQuery: false,
9201     append: false,
9202     loadNext: false,
9203     item: [],
9204     
9205     // element that contains real text value.. (when hidden is used..)
9206      
9207     // private
9208     initEvents: function(){
9209         
9210         if (!this.store) {
9211             throw "can not find store for combo";
9212         }
9213         this.store = Roo.factory(this.store, Roo.data);
9214         
9215         
9216         
9217         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9218         
9219         
9220         if(this.hiddenName){
9221             
9222             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
9223             
9224             this.hiddenField.dom.value =
9225                 this.hiddenValue !== undefined ? this.hiddenValue :
9226                 this.value !== undefined ? this.value : '';
9227
9228             // prevent input submission
9229             this.el.dom.removeAttribute('name');
9230             this.hiddenField.dom.setAttribute('name', this.hiddenName);
9231              
9232              
9233         }
9234         //if(Roo.isGecko){
9235         //    this.el.dom.setAttribute('autocomplete', 'off');
9236         //}
9237
9238         var cls = 'x-combo-list';
9239         this.list = this.el.select('ul.dropdown-menu',true).first();
9240
9241         //this.list = new Roo.Layer({
9242         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
9243         //});
9244         
9245         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
9246         this.list.setWidth(lw);
9247         
9248         this.list.on('mouseover', this.onViewOver, this);
9249         this.list.on('mousemove', this.onViewMove, this);
9250         
9251         this.list.on('scroll', this.onViewScroll, this);
9252         
9253         /*
9254         this.list.swallowEvent('mousewheel');
9255         this.assetHeight = 0;
9256
9257         if(this.title){
9258             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
9259             this.assetHeight += this.header.getHeight();
9260         }
9261
9262         this.innerList = this.list.createChild({cls:cls+'-inner'});
9263         this.innerList.on('mouseover', this.onViewOver, this);
9264         this.innerList.on('mousemove', this.onViewMove, this);
9265         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9266         
9267         if(this.allowBlank && !this.pageSize && !this.disableClear){
9268             this.footer = this.list.createChild({cls:cls+'-ft'});
9269             this.pageTb = new Roo.Toolbar(this.footer);
9270            
9271         }
9272         if(this.pageSize){
9273             this.footer = this.list.createChild({cls:cls+'-ft'});
9274             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
9275                     {pageSize: this.pageSize});
9276             
9277         }
9278         
9279         if (this.pageTb && this.allowBlank && !this.disableClear) {
9280             var _this = this;
9281             this.pageTb.add(new Roo.Toolbar.Fill(), {
9282                 cls: 'x-btn-icon x-btn-clear',
9283                 text: '&#160;',
9284                 handler: function()
9285                 {
9286                     _this.collapse();
9287                     _this.clearValue();
9288                     _this.onSelect(false, -1);
9289                 }
9290             });
9291         }
9292         if (this.footer) {
9293             this.assetHeight += this.footer.getHeight();
9294         }
9295         */
9296             
9297         if(!this.tpl){
9298             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
9299         }
9300
9301         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
9302             singleSelect:true, store: this.store, selectedClass: this.selectedClass
9303         });
9304         //this.view.wrapEl.setDisplayed(false);
9305         this.view.on('click', this.onViewClick, this);
9306         
9307         
9308         
9309         this.store.on('beforeload', this.onBeforeLoad, this);
9310         this.store.on('load', this.onLoad, this);
9311         this.store.on('loadexception', this.onLoadException, this);
9312         /*
9313         if(this.resizable){
9314             this.resizer = new Roo.Resizable(this.list,  {
9315                pinned:true, handles:'se'
9316             });
9317             this.resizer.on('resize', function(r, w, h){
9318                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
9319                 this.listWidth = w;
9320                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
9321                 this.restrictHeight();
9322             }, this);
9323             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
9324         }
9325         */
9326         if(!this.editable){
9327             this.editable = true;
9328             this.setEditable(false);
9329         }
9330         
9331         /*
9332         
9333         if (typeof(this.events.add.listeners) != 'undefined') {
9334             
9335             this.addicon = this.wrap.createChild(
9336                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
9337        
9338             this.addicon.on('click', function(e) {
9339                 this.fireEvent('add', this);
9340             }, this);
9341         }
9342         if (typeof(this.events.edit.listeners) != 'undefined') {
9343             
9344             this.editicon = this.wrap.createChild(
9345                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
9346             if (this.addicon) {
9347                 this.editicon.setStyle('margin-left', '40px');
9348             }
9349             this.editicon.on('click', function(e) {
9350                 
9351                 // we fire even  if inothing is selected..
9352                 this.fireEvent('edit', this, this.lastData );
9353                 
9354             }, this);
9355         }
9356         */
9357         
9358         this.keyNav = new Roo.KeyNav(this.inputEl(), {
9359             "up" : function(e){
9360                 this.inKeyMode = true;
9361                 this.selectPrev();
9362             },
9363
9364             "down" : function(e){
9365                 if(!this.isExpanded()){
9366                     this.onTriggerClick();
9367                 }else{
9368                     this.inKeyMode = true;
9369                     this.selectNext();
9370                 }
9371             },
9372
9373             "enter" : function(e){
9374                 this.onViewClick();
9375                 //return true;
9376             },
9377
9378             "esc" : function(e){
9379                 this.collapse();
9380             },
9381
9382             "tab" : function(e){
9383                 this.collapse();
9384                 
9385                 if(this.fireEvent("specialkey", this, e)){
9386                     this.onViewClick(false);
9387                 }
9388                 
9389                 return true;
9390             },
9391
9392             scope : this,
9393
9394             doRelay : function(foo, bar, hname){
9395                 if(hname == 'down' || this.scope.isExpanded()){
9396                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
9397                 }
9398                 return true;
9399             },
9400
9401             forceKeyDown: true
9402         });
9403         
9404         
9405         this.queryDelay = Math.max(this.queryDelay || 10,
9406                 this.mode == 'local' ? 10 : 250);
9407         
9408         
9409         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
9410         
9411         if(this.typeAhead){
9412             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
9413         }
9414         if(this.editable !== false){
9415             this.inputEl().on("keyup", this.onKeyUp, this);
9416         }
9417         if(this.forceSelection){
9418             this.inputEl().on('blur', this.doForce, this);
9419         }
9420         
9421         if(this.multiple){
9422             this.choices = this.el.select('ul.select2-choices', true).first();
9423             this.searchField = this.el.select('ul li.select2-search-field', true).first();
9424         }
9425     },
9426
9427     onDestroy : function(){
9428         if(this.view){
9429             this.view.setStore(null);
9430             this.view.el.removeAllListeners();
9431             this.view.el.remove();
9432             this.view.purgeListeners();
9433         }
9434         if(this.list){
9435             this.list.dom.innerHTML  = '';
9436         }
9437         if(this.store){
9438             this.store.un('beforeload', this.onBeforeLoad, this);
9439             this.store.un('load', this.onLoad, this);
9440             this.store.un('loadexception', this.onLoadException, this);
9441         }
9442         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9443     },
9444
9445     // private
9446     fireKey : function(e){
9447         if(e.isNavKeyPress() && !this.list.isVisible()){
9448             this.fireEvent("specialkey", this, e);
9449         }
9450     },
9451
9452     // private
9453     onResize: function(w, h){
9454 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9455 //        
9456 //        if(typeof w != 'number'){
9457 //            // we do not handle it!?!?
9458 //            return;
9459 //        }
9460 //        var tw = this.trigger.getWidth();
9461 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9462 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9463 //        var x = w - tw;
9464 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9465 //            
9466 //        //this.trigger.setStyle('left', x+'px');
9467 //        
9468 //        if(this.list && this.listWidth === undefined){
9469 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9470 //            this.list.setWidth(lw);
9471 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9472 //        }
9473         
9474     
9475         
9476     },
9477
9478     /**
9479      * Allow or prevent the user from directly editing the field text.  If false is passed,
9480      * the user will only be able to select from the items defined in the dropdown list.  This method
9481      * is the runtime equivalent of setting the 'editable' config option at config time.
9482      * @param {Boolean} value True to allow the user to directly edit the field text
9483      */
9484     setEditable : function(value){
9485         if(value == this.editable){
9486             return;
9487         }
9488         this.editable = value;
9489         if(!value){
9490             this.inputEl().dom.setAttribute('readOnly', true);
9491             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9492             this.inputEl().addClass('x-combo-noedit');
9493         }else{
9494             this.inputEl().dom.setAttribute('readOnly', false);
9495             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9496             this.inputEl().removeClass('x-combo-noedit');
9497         }
9498     },
9499
9500     // private
9501     
9502     onBeforeLoad : function(combo,opts){
9503         if(!this.hasFocus){
9504             return;
9505         }
9506          if (!opts.add) {
9507             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9508          }
9509         this.restrictHeight();
9510         this.selectedIndex = -1;
9511     },
9512
9513     // private
9514     onLoad : function(){
9515         
9516         this.hasQuery = false;
9517         
9518         if(!this.hasFocus){
9519             return;
9520         }
9521         
9522         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9523             this.loading.hide();
9524         }
9525         
9526         if(this.store.getCount() > 0){
9527             this.expand();
9528             this.restrictHeight();
9529             if(this.lastQuery == this.allQuery){
9530                 if(this.editable){
9531                     this.inputEl().dom.select();
9532                 }
9533                 if(!this.selectByValue(this.value, true)){
9534                     this.select(0, true);
9535                 }
9536             }else{
9537                 this.selectNext();
9538                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9539                     this.taTask.delay(this.typeAheadDelay);
9540                 }
9541             }
9542         }else{
9543             this.onEmptyResults();
9544         }
9545         
9546         //this.el.focus();
9547     },
9548     // private
9549     onLoadException : function()
9550     {
9551         this.hasQuery = false;
9552         
9553         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9554             this.loading.hide();
9555         }
9556         
9557         this.collapse();
9558         Roo.log(this.store.reader.jsonData);
9559         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9560             // fixme
9561             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9562         }
9563         
9564         
9565     },
9566     // private
9567     onTypeAhead : function(){
9568         if(this.store.getCount() > 0){
9569             var r = this.store.getAt(0);
9570             var newValue = r.data[this.displayField];
9571             var len = newValue.length;
9572             var selStart = this.getRawValue().length;
9573             
9574             if(selStart != len){
9575                 this.setRawValue(newValue);
9576                 this.selectText(selStart, newValue.length);
9577             }
9578         }
9579     },
9580
9581     // private
9582     onSelect : function(record, index){
9583         
9584         if(this.fireEvent('beforeselect', this, record, index) !== false){
9585         
9586             this.setFromData(index > -1 ? record.data : false);
9587             
9588             this.collapse();
9589             this.fireEvent('select', this, record, index);
9590         }
9591     },
9592
9593     /**
9594      * Returns the currently selected field value or empty string if no value is set.
9595      * @return {String} value The selected value
9596      */
9597     getValue : function(){
9598         
9599         if(this.multiple){
9600             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9601         }
9602         
9603         if(this.valueField){
9604             return typeof this.value != 'undefined' ? this.value : '';
9605         }else{
9606             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9607         }
9608     },
9609
9610     /**
9611      * Clears any text/value currently set in the field
9612      */
9613     clearValue : function(){
9614         if(this.hiddenField){
9615             this.hiddenField.dom.value = '';
9616         }
9617         this.value = '';
9618         this.setRawValue('');
9619         this.lastSelectionText = '';
9620         
9621     },
9622
9623     /**
9624      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9625      * will be displayed in the field.  If the value does not match the data value of an existing item,
9626      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9627      * Otherwise the field will be blank (although the value will still be set).
9628      * @param {String} value The value to match
9629      */
9630     setValue : function(v){
9631         if(this.multiple){
9632             this.syncValue();
9633             return;
9634         }
9635         
9636         var text = v;
9637         if(this.valueField){
9638             var r = this.findRecord(this.valueField, v);
9639             if(r){
9640                 text = r.data[this.displayField];
9641             }else if(this.valueNotFoundText !== undefined){
9642                 text = this.valueNotFoundText;
9643             }
9644         }
9645         this.lastSelectionText = text;
9646         if(this.hiddenField){
9647             this.hiddenField.dom.value = v;
9648         }
9649         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9650         this.value = v;
9651     },
9652     /**
9653      * @property {Object} the last set data for the element
9654      */
9655     
9656     lastData : false,
9657     /**
9658      * Sets the value of the field based on a object which is related to the record format for the store.
9659      * @param {Object} value the value to set as. or false on reset?
9660      */
9661     setFromData : function(o){
9662         
9663         if(this.multiple){
9664             this.addItem(o);
9665             return;
9666         }
9667             
9668         var dv = ''; // display value
9669         var vv = ''; // value value..
9670         this.lastData = o;
9671         if (this.displayField) {
9672             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9673         } else {
9674             // this is an error condition!!!
9675             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9676         }
9677         
9678         if(this.valueField){
9679             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9680         }
9681         
9682         if(this.hiddenField){
9683             this.hiddenField.dom.value = vv;
9684             
9685             this.lastSelectionText = dv;
9686             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9687             this.value = vv;
9688             return;
9689         }
9690         // no hidden field.. - we store the value in 'value', but still display
9691         // display field!!!!
9692         this.lastSelectionText = dv;
9693         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9694         this.value = vv;
9695         
9696         
9697     },
9698     // private
9699     reset : function(){
9700         // overridden so that last data is reset..
9701         this.setValue(this.originalValue);
9702         this.clearInvalid();
9703         this.lastData = false;
9704         if (this.view) {
9705             this.view.clearSelections();
9706         }
9707     },
9708     // private
9709     findRecord : function(prop, value){
9710         var record;
9711         if(this.store.getCount() > 0){
9712             this.store.each(function(r){
9713                 if(r.data[prop] == value){
9714                     record = r;
9715                     return false;
9716                 }
9717                 return true;
9718             });
9719         }
9720         return record;
9721     },
9722     
9723     getName: function()
9724     {
9725         // returns hidden if it's set..
9726         if (!this.rendered) {return ''};
9727         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9728         
9729     },
9730     // private
9731     onViewMove : function(e, t){
9732         this.inKeyMode = false;
9733     },
9734
9735     // private
9736     onViewOver : function(e, t){
9737         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9738             return;
9739         }
9740         var item = this.view.findItemFromChild(t);
9741         if(item){
9742             var index = this.view.indexOf(item);
9743             this.select(index, false);
9744         }
9745     },
9746
9747     // private
9748     onViewClick : function(doFocus)
9749     {
9750         var index = this.view.getSelectedIndexes()[0];
9751         var r = this.store.getAt(index);
9752         if(r){
9753             this.onSelect(r, index);
9754         }
9755         if(doFocus !== false && !this.blockFocus){
9756             this.inputEl().focus();
9757         }
9758     },
9759
9760     // private
9761     restrictHeight : function(){
9762         //this.innerList.dom.style.height = '';
9763         //var inner = this.innerList.dom;
9764         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9765         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9766         //this.list.beginUpdate();
9767         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9768         this.list.alignTo(this.inputEl(), this.listAlign);
9769         //this.list.endUpdate();
9770     },
9771
9772     // private
9773     onEmptyResults : function(){
9774         this.collapse();
9775     },
9776
9777     /**
9778      * Returns true if the dropdown list is expanded, else false.
9779      */
9780     isExpanded : function(){
9781         return this.list.isVisible();
9782     },
9783
9784     /**
9785      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9786      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9787      * @param {String} value The data value of the item to select
9788      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9789      * selected item if it is not currently in view (defaults to true)
9790      * @return {Boolean} True if the value matched an item in the list, else false
9791      */
9792     selectByValue : function(v, scrollIntoView){
9793         if(v !== undefined && v !== null){
9794             var r = this.findRecord(this.valueField || this.displayField, v);
9795             if(r){
9796                 this.select(this.store.indexOf(r), scrollIntoView);
9797                 return true;
9798             }
9799         }
9800         return false;
9801     },
9802
9803     /**
9804      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9805      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9806      * @param {Number} index The zero-based index of the list item to select
9807      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9808      * selected item if it is not currently in view (defaults to true)
9809      */
9810     select : function(index, scrollIntoView){
9811         this.selectedIndex = index;
9812         this.view.select(index);
9813         if(scrollIntoView !== false){
9814             var el = this.view.getNode(index);
9815             if(el){
9816                 //this.innerList.scrollChildIntoView(el, false);
9817                 
9818             }
9819         }
9820     },
9821
9822     // private
9823     selectNext : function(){
9824         var ct = this.store.getCount();
9825         if(ct > 0){
9826             if(this.selectedIndex == -1){
9827                 this.select(0);
9828             }else if(this.selectedIndex < ct-1){
9829                 this.select(this.selectedIndex+1);
9830             }
9831         }
9832     },
9833
9834     // private
9835     selectPrev : function(){
9836         var ct = this.store.getCount();
9837         if(ct > 0){
9838             if(this.selectedIndex == -1){
9839                 this.select(0);
9840             }else if(this.selectedIndex != 0){
9841                 this.select(this.selectedIndex-1);
9842             }
9843         }
9844     },
9845
9846     // private
9847     onKeyUp : function(e){
9848         if(this.editable !== false && !e.isSpecialKey()){
9849             this.lastKey = e.getKey();
9850             this.dqTask.delay(this.queryDelay);
9851         }
9852     },
9853
9854     // private
9855     validateBlur : function(){
9856         return !this.list || !this.list.isVisible();   
9857     },
9858
9859     // private
9860     initQuery : function(){
9861         this.doQuery(this.getRawValue());
9862     },
9863
9864     // private
9865     doForce : function(){
9866         if(this.inputEl().dom.value.length > 0){
9867             this.inputEl().dom.value =
9868                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9869              
9870         }
9871     },
9872
9873     /**
9874      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
9875      * query allowing the query action to be canceled if needed.
9876      * @param {String} query The SQL query to execute
9877      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9878      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
9879      * saved in the current store (defaults to false)
9880      */
9881     doQuery : function(q, forceAll){
9882         
9883         if(q === undefined || q === null){
9884             q = '';
9885         }
9886         var qe = {
9887             query: q,
9888             forceAll: forceAll,
9889             combo: this,
9890             cancel:false
9891         };
9892         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
9893             return false;
9894         }
9895         q = qe.query;
9896         
9897         forceAll = qe.forceAll;
9898         if(forceAll === true || (q.length >= this.minChars)){
9899             
9900             this.hasQuery = true;
9901             
9902             if(this.lastQuery != q || this.alwaysQuery){
9903                 this.lastQuery = q;
9904                 if(this.mode == 'local'){
9905                     this.selectedIndex = -1;
9906                     if(forceAll){
9907                         this.store.clearFilter();
9908                     }else{
9909                         this.store.filter(this.displayField, q);
9910                     }
9911                     this.onLoad();
9912                 }else{
9913                     this.store.baseParams[this.queryParam] = q;
9914                     
9915                     var options = {params : this.getParams(q)};
9916                     
9917                     if(this.loadNext){
9918                         options.add = true;
9919                         options.params.start = this.page * this.pageSize;
9920                     }
9921                     
9922                     this.store.load(options);
9923                     this.expand();
9924                 }
9925             }else{
9926                 this.selectedIndex = -1;
9927                 this.onLoad();   
9928             }
9929         }
9930         
9931         this.loadNext = false;
9932     },
9933
9934     // private
9935     getParams : function(q){
9936         var p = {};
9937         //p[this.queryParam] = q;
9938         
9939         if(this.pageSize){
9940             p.start = 0;
9941             p.limit = this.pageSize;
9942         }
9943         return p;
9944     },
9945
9946     /**
9947      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
9948      */
9949     collapse : function(){
9950         if(!this.isExpanded()){
9951             return;
9952         }
9953         
9954         this.list.hide();
9955         Roo.get(document).un('mousedown', this.collapseIf, this);
9956         Roo.get(document).un('mousewheel', this.collapseIf, this);
9957         if (!this.editable) {
9958             Roo.get(document).un('keydown', this.listKeyPress, this);
9959         }
9960         this.fireEvent('collapse', this);
9961     },
9962
9963     // private
9964     collapseIf : function(e){
9965         var in_combo  = e.within(this.el);
9966         var in_list =  e.within(this.list);
9967         
9968         if (in_combo || in_list) {
9969             //e.stopPropagation();
9970             return;
9971         }
9972
9973         this.collapse();
9974         
9975     },
9976
9977     /**
9978      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
9979      */
9980     expand : function(){
9981        
9982         if(this.isExpanded() || !this.hasFocus){
9983             return;
9984         }
9985          Roo.log('expand');
9986         this.list.alignTo(this.inputEl(), this.listAlign);
9987         this.list.show();
9988         Roo.get(document).on('mousedown', this.collapseIf, this);
9989         Roo.get(document).on('mousewheel', this.collapseIf, this);
9990         if (!this.editable) {
9991             Roo.get(document).on('keydown', this.listKeyPress, this);
9992         }
9993         
9994         this.fireEvent('expand', this);
9995     },
9996
9997     // private
9998     // Implements the default empty TriggerField.onTriggerClick function
9999     onTriggerClick : function()
10000     {
10001         Roo.log('trigger click');
10002         
10003         if(this.disabled){
10004             return;
10005         }
10006         
10007         this.page = 0;
10008         this.loadNext = false;
10009         
10010         if(this.isExpanded()){
10011             this.collapse();
10012             if (!this.blockFocus) {
10013                 this.inputEl().focus();
10014             }
10015             
10016         }else {
10017             this.hasFocus = true;
10018             if(this.triggerAction == 'all') {
10019                 this.doQuery(this.allQuery, true);
10020             } else {
10021                 this.doQuery(this.getRawValue());
10022             }
10023             if (!this.blockFocus) {
10024                 this.inputEl().focus();
10025             }
10026         }
10027     },
10028     listKeyPress : function(e)
10029     {
10030         //Roo.log('listkeypress');
10031         // scroll to first matching element based on key pres..
10032         if (e.isSpecialKey()) {
10033             return false;
10034         }
10035         var k = String.fromCharCode(e.getKey()).toUpperCase();
10036         //Roo.log(k);
10037         var match  = false;
10038         var csel = this.view.getSelectedNodes();
10039         var cselitem = false;
10040         if (csel.length) {
10041             var ix = this.view.indexOf(csel[0]);
10042             cselitem  = this.store.getAt(ix);
10043             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
10044                 cselitem = false;
10045             }
10046             
10047         }
10048         
10049         this.store.each(function(v) { 
10050             if (cselitem) {
10051                 // start at existing selection.
10052                 if (cselitem.id == v.id) {
10053                     cselitem = false;
10054                 }
10055                 return true;
10056             }
10057                 
10058             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
10059                 match = this.store.indexOf(v);
10060                 return false;
10061             }
10062             return true;
10063         }, this);
10064         
10065         if (match === false) {
10066             return true; // no more action?
10067         }
10068         // scroll to?
10069         this.view.select(match);
10070         var sn = Roo.get(this.view.getSelectedNodes()[0])
10071         //sn.scrollIntoView(sn.dom.parentNode, false);
10072     },
10073     
10074     onViewScroll : function(e, t){
10075         
10076         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
10077             return;
10078         }
10079         
10080         this.hasQuery = true;
10081         
10082         this.loading = this.list.select('.loading', true).first();
10083         
10084         if(this.loading === null){
10085             this.list.createChild({
10086                 tag: 'div',
10087                 cls: 'loading select2-more-results select2-active',
10088                 html: 'Loading more results...'
10089             })
10090             
10091             this.loading = this.list.select('.loading', true).first();
10092             
10093             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
10094             
10095             this.loading.hide();
10096         }
10097         
10098         this.loading.show();
10099         
10100         var _combo = this;
10101         
10102         this.page++;
10103         this.loadNext = true;
10104         
10105         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
10106         
10107         return;
10108     },
10109     
10110     addItem : function(o)
10111     {   
10112         var dv = ''; // display value
10113         
10114         if (this.displayField) {
10115             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10116         } else {
10117             // this is an error condition!!!
10118             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10119         }
10120         
10121         if(!dv.length){
10122             return;
10123         }
10124         
10125         var choice = this.choices.createChild({
10126             tag: 'li',
10127             cls: 'select2-search-choice',
10128             cn: [
10129                 {
10130                     tag: 'div',
10131                     html: dv
10132                 },
10133                 {
10134                     tag: 'a',
10135                     href: '#',
10136                     cls: 'select2-search-choice-close',
10137                     tabindex: '-1'
10138                 }
10139             ]
10140             
10141         }, this.searchField);
10142         
10143         var close = choice.select('a.select2-search-choice-close', true).first()
10144         
10145         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
10146         
10147         this.item.push(o);
10148         this.lastData = o;
10149         
10150         this.syncValue();
10151         
10152         this.inputEl().dom.value = '';
10153         
10154     },
10155     
10156     onRemoveItem : function(e, _self, o)
10157     {
10158         e.preventDefault();
10159         var index = this.item.indexOf(o.data) * 1;
10160         
10161         if( index < 0){
10162             Roo.log('not this item?!');
10163             return;
10164         }
10165         
10166         this.item.splice(index, 1);
10167         o.item.remove();
10168         
10169         this.syncValue();
10170         
10171         this.fireEvent('remove', this, e);
10172         
10173     },
10174     
10175     syncValue : function()
10176     {
10177         if(!this.item.length){
10178             this.clearValue();
10179             return;
10180         }
10181             
10182         var value = [];
10183         var _this = this;
10184         Roo.each(this.item, function(i){
10185             if(_this.valueField){
10186                 value.push(i[_this.valueField]);
10187                 return;
10188             }
10189
10190             value.push(i);
10191         });
10192
10193         this.value = value.join(',');
10194
10195         if(this.hiddenField){
10196             this.hiddenField.dom.value = this.value;
10197         }
10198     },
10199     
10200     clearItem : function()
10201     {
10202         if(!this.multiple){
10203             return;
10204         }
10205         
10206         this.item = [];
10207         
10208         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
10209            c.remove();
10210         });
10211         
10212         this.syncValue();
10213     }
10214     
10215     
10216
10217     /** 
10218     * @cfg {Boolean} grow 
10219     * @hide 
10220     */
10221     /** 
10222     * @cfg {Number} growMin 
10223     * @hide 
10224     */
10225     /** 
10226     * @cfg {Number} growMax 
10227     * @hide 
10228     */
10229     /**
10230      * @hide
10231      * @method autoSize
10232      */
10233 });
10234 /*
10235  * Based on:
10236  * Ext JS Library 1.1.1
10237  * Copyright(c) 2006-2007, Ext JS, LLC.
10238  *
10239  * Originally Released Under LGPL - original licence link has changed is not relivant.
10240  *
10241  * Fork - LGPL
10242  * <script type="text/javascript">
10243  */
10244
10245 /**
10246  * @class Roo.View
10247  * @extends Roo.util.Observable
10248  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
10249  * This class also supports single and multi selection modes. <br>
10250  * Create a data model bound view:
10251  <pre><code>
10252  var store = new Roo.data.Store(...);
10253
10254  var view = new Roo.View({
10255     el : "my-element",
10256     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
10257  
10258     singleSelect: true,
10259     selectedClass: "ydataview-selected",
10260     store: store
10261  });
10262
10263  // listen for node click?
10264  view.on("click", function(vw, index, node, e){
10265  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
10266  });
10267
10268  // load XML data
10269  dataModel.load("foobar.xml");
10270  </code></pre>
10271  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
10272  * <br><br>
10273  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
10274  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
10275  * 
10276  * Note: old style constructor is still suported (container, template, config)
10277  * 
10278  * @constructor
10279  * Create a new View
10280  * @param {Object} config The config object
10281  * 
10282  */
10283 Roo.View = function(config, depreciated_tpl, depreciated_config){
10284     
10285     if (typeof(depreciated_tpl) == 'undefined') {
10286         // new way.. - universal constructor.
10287         Roo.apply(this, config);
10288         this.el  = Roo.get(this.el);
10289     } else {
10290         // old format..
10291         this.el  = Roo.get(config);
10292         this.tpl = depreciated_tpl;
10293         Roo.apply(this, depreciated_config);
10294     }
10295     this.wrapEl  = this.el.wrap().wrap();
10296     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
10297     
10298     
10299     if(typeof(this.tpl) == "string"){
10300         this.tpl = new Roo.Template(this.tpl);
10301     } else {
10302         // support xtype ctors..
10303         this.tpl = new Roo.factory(this.tpl, Roo);
10304     }
10305     
10306     
10307     this.tpl.compile();
10308    
10309   
10310     
10311      
10312     /** @private */
10313     this.addEvents({
10314         /**
10315          * @event beforeclick
10316          * Fires before a click is processed. Returns false to cancel the default action.
10317          * @param {Roo.View} this
10318          * @param {Number} index The index of the target node
10319          * @param {HTMLElement} node The target node
10320          * @param {Roo.EventObject} e The raw event object
10321          */
10322             "beforeclick" : true,
10323         /**
10324          * @event click
10325          * Fires when a template node is clicked.
10326          * @param {Roo.View} this
10327          * @param {Number} index The index of the target node
10328          * @param {HTMLElement} node The target node
10329          * @param {Roo.EventObject} e The raw event object
10330          */
10331             "click" : true,
10332         /**
10333          * @event dblclick
10334          * Fires when a template node is double clicked.
10335          * @param {Roo.View} this
10336          * @param {Number} index The index of the target node
10337          * @param {HTMLElement} node The target node
10338          * @param {Roo.EventObject} e The raw event object
10339          */
10340             "dblclick" : true,
10341         /**
10342          * @event contextmenu
10343          * Fires when a template node is right clicked.
10344          * @param {Roo.View} this
10345          * @param {Number} index The index of the target node
10346          * @param {HTMLElement} node The target node
10347          * @param {Roo.EventObject} e The raw event object
10348          */
10349             "contextmenu" : true,
10350         /**
10351          * @event selectionchange
10352          * Fires when the selected nodes change.
10353          * @param {Roo.View} this
10354          * @param {Array} selections Array of the selected nodes
10355          */
10356             "selectionchange" : true,
10357     
10358         /**
10359          * @event beforeselect
10360          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
10361          * @param {Roo.View} this
10362          * @param {HTMLElement} node The node to be selected
10363          * @param {Array} selections Array of currently selected nodes
10364          */
10365             "beforeselect" : true,
10366         /**
10367          * @event preparedata
10368          * Fires on every row to render, to allow you to change the data.
10369          * @param {Roo.View} this
10370          * @param {Object} data to be rendered (change this)
10371          */
10372           "preparedata" : true
10373           
10374           
10375         });
10376
10377
10378
10379     this.el.on({
10380         "click": this.onClick,
10381         "dblclick": this.onDblClick,
10382         "contextmenu": this.onContextMenu,
10383         scope:this
10384     });
10385
10386     this.selections = [];
10387     this.nodes = [];
10388     this.cmp = new Roo.CompositeElementLite([]);
10389     if(this.store){
10390         this.store = Roo.factory(this.store, Roo.data);
10391         this.setStore(this.store, true);
10392     }
10393     
10394     if ( this.footer && this.footer.xtype) {
10395            
10396          var fctr = this.wrapEl.appendChild(document.createElement("div"));
10397         
10398         this.footer.dataSource = this.store
10399         this.footer.container = fctr;
10400         this.footer = Roo.factory(this.footer, Roo);
10401         fctr.insertFirst(this.el);
10402         
10403         // this is a bit insane - as the paging toolbar seems to detach the el..
10404 //        dom.parentNode.parentNode.parentNode
10405          // they get detached?
10406     }
10407     
10408     
10409     Roo.View.superclass.constructor.call(this);
10410     
10411     
10412 };
10413
10414 Roo.extend(Roo.View, Roo.util.Observable, {
10415     
10416      /**
10417      * @cfg {Roo.data.Store} store Data store to load data from.
10418      */
10419     store : false,
10420     
10421     /**
10422      * @cfg {String|Roo.Element} el The container element.
10423      */
10424     el : '',
10425     
10426     /**
10427      * @cfg {String|Roo.Template} tpl The template used by this View 
10428      */
10429     tpl : false,
10430     /**
10431      * @cfg {String} dataName the named area of the template to use as the data area
10432      *                          Works with domtemplates roo-name="name"
10433      */
10434     dataName: false,
10435     /**
10436      * @cfg {String} selectedClass The css class to add to selected nodes
10437      */
10438     selectedClass : "x-view-selected",
10439      /**
10440      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10441      */
10442     emptyText : "",
10443     
10444     /**
10445      * @cfg {String} text to display on mask (default Loading)
10446      */
10447     mask : false,
10448     /**
10449      * @cfg {Boolean} multiSelect Allow multiple selection
10450      */
10451     multiSelect : false,
10452     /**
10453      * @cfg {Boolean} singleSelect Allow single selection
10454      */
10455     singleSelect:  false,
10456     
10457     /**
10458      * @cfg {Boolean} toggleSelect - selecting 
10459      */
10460     toggleSelect : false,
10461     
10462     /**
10463      * Returns the element this view is bound to.
10464      * @return {Roo.Element}
10465      */
10466     getEl : function(){
10467         return this.wrapEl;
10468     },
10469     
10470     
10471
10472     /**
10473      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10474      */
10475     refresh : function(){
10476         Roo.log('refresh');
10477         var t = this.tpl;
10478         
10479         // if we are using something like 'domtemplate', then
10480         // the what gets used is:
10481         // t.applySubtemplate(NAME, data, wrapping data..)
10482         // the outer template then get' applied with
10483         //     the store 'extra data'
10484         // and the body get's added to the
10485         //      roo-name="data" node?
10486         //      <span class='roo-tpl-{name}'></span> ?????
10487         
10488         
10489         
10490         this.clearSelections();
10491         this.el.update("");
10492         var html = [];
10493         var records = this.store.getRange();
10494         if(records.length < 1) {
10495             
10496             // is this valid??  = should it render a template??
10497             
10498             this.el.update(this.emptyText);
10499             return;
10500         }
10501         var el = this.el;
10502         if (this.dataName) {
10503             this.el.update(t.apply(this.store.meta)); //????
10504             el = this.el.child('.roo-tpl-' + this.dataName);
10505         }
10506         
10507         for(var i = 0, len = records.length; i < len; i++){
10508             var data = this.prepareData(records[i].data, i, records[i]);
10509             this.fireEvent("preparedata", this, data, i, records[i]);
10510             html[html.length] = Roo.util.Format.trim(
10511                 this.dataName ?
10512                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10513                     t.apply(data)
10514             );
10515         }
10516         
10517         
10518         
10519         el.update(html.join(""));
10520         this.nodes = el.dom.childNodes;
10521         this.updateIndexes(0);
10522     },
10523     
10524
10525     /**
10526      * Function to override to reformat the data that is sent to
10527      * the template for each node.
10528      * DEPRICATED - use the preparedata event handler.
10529      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10530      * a JSON object for an UpdateManager bound view).
10531      */
10532     prepareData : function(data, index, record)
10533     {
10534         this.fireEvent("preparedata", this, data, index, record);
10535         return data;
10536     },
10537
10538     onUpdate : function(ds, record){
10539          Roo.log('on update');   
10540         this.clearSelections();
10541         var index = this.store.indexOf(record);
10542         var n = this.nodes[index];
10543         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10544         n.parentNode.removeChild(n);
10545         this.updateIndexes(index, index);
10546     },
10547
10548     
10549     
10550 // --------- FIXME     
10551     onAdd : function(ds, records, index)
10552     {
10553         Roo.log(['on Add', ds, records, index] );        
10554         this.clearSelections();
10555         if(this.nodes.length == 0){
10556             this.refresh();
10557             return;
10558         }
10559         var n = this.nodes[index];
10560         for(var i = 0, len = records.length; i < len; i++){
10561             var d = this.prepareData(records[i].data, i, records[i]);
10562             if(n){
10563                 this.tpl.insertBefore(n, d);
10564             }else{
10565                 
10566                 this.tpl.append(this.el, d);
10567             }
10568         }
10569         this.updateIndexes(index);
10570     },
10571
10572     onRemove : function(ds, record, index){
10573         Roo.log('onRemove');
10574         this.clearSelections();
10575         var el = this.dataName  ?
10576             this.el.child('.roo-tpl-' + this.dataName) :
10577             this.el; 
10578         
10579         el.dom.removeChild(this.nodes[index]);
10580         this.updateIndexes(index);
10581     },
10582
10583     /**
10584      * Refresh an individual node.
10585      * @param {Number} index
10586      */
10587     refreshNode : function(index){
10588         this.onUpdate(this.store, this.store.getAt(index));
10589     },
10590
10591     updateIndexes : function(startIndex, endIndex){
10592         var ns = this.nodes;
10593         startIndex = startIndex || 0;
10594         endIndex = endIndex || ns.length - 1;
10595         for(var i = startIndex; i <= endIndex; i++){
10596             ns[i].nodeIndex = i;
10597         }
10598     },
10599
10600     /**
10601      * Changes the data store this view uses and refresh the view.
10602      * @param {Store} store
10603      */
10604     setStore : function(store, initial){
10605         if(!initial && this.store){
10606             this.store.un("datachanged", this.refresh);
10607             this.store.un("add", this.onAdd);
10608             this.store.un("remove", this.onRemove);
10609             this.store.un("update", this.onUpdate);
10610             this.store.un("clear", this.refresh);
10611             this.store.un("beforeload", this.onBeforeLoad);
10612             this.store.un("load", this.onLoad);
10613             this.store.un("loadexception", this.onLoad);
10614         }
10615         if(store){
10616           
10617             store.on("datachanged", this.refresh, this);
10618             store.on("add", this.onAdd, this);
10619             store.on("remove", this.onRemove, this);
10620             store.on("update", this.onUpdate, this);
10621             store.on("clear", this.refresh, this);
10622             store.on("beforeload", this.onBeforeLoad, this);
10623             store.on("load", this.onLoad, this);
10624             store.on("loadexception", this.onLoad, this);
10625         }
10626         
10627         if(store){
10628             this.refresh();
10629         }
10630     },
10631     /**
10632      * onbeforeLoad - masks the loading area.
10633      *
10634      */
10635     onBeforeLoad : function(store,opts)
10636     {
10637          Roo.log('onBeforeLoad');   
10638         if (!opts.add) {
10639             this.el.update("");
10640         }
10641         this.el.mask(this.mask ? this.mask : "Loading" ); 
10642     },
10643     onLoad : function ()
10644     {
10645         this.el.unmask();
10646     },
10647     
10648
10649     /**
10650      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10651      * @param {HTMLElement} node
10652      * @return {HTMLElement} The template node
10653      */
10654     findItemFromChild : function(node){
10655         var el = this.dataName  ?
10656             this.el.child('.roo-tpl-' + this.dataName,true) :
10657             this.el.dom; 
10658         
10659         if(!node || node.parentNode == el){
10660                     return node;
10661             }
10662             var p = node.parentNode;
10663             while(p && p != el){
10664             if(p.parentNode == el){
10665                 return p;
10666             }
10667             p = p.parentNode;
10668         }
10669             return null;
10670     },
10671
10672     /** @ignore */
10673     onClick : function(e){
10674         var item = this.findItemFromChild(e.getTarget());
10675         if(item){
10676             var index = this.indexOf(item);
10677             if(this.onItemClick(item, index, e) !== false){
10678                 this.fireEvent("click", this, index, item, e);
10679             }
10680         }else{
10681             this.clearSelections();
10682         }
10683     },
10684
10685     /** @ignore */
10686     onContextMenu : function(e){
10687         var item = this.findItemFromChild(e.getTarget());
10688         if(item){
10689             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10690         }
10691     },
10692
10693     /** @ignore */
10694     onDblClick : function(e){
10695         var item = this.findItemFromChild(e.getTarget());
10696         if(item){
10697             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10698         }
10699     },
10700
10701     onItemClick : function(item, index, e)
10702     {
10703         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10704             return false;
10705         }
10706         if (this.toggleSelect) {
10707             var m = this.isSelected(item) ? 'unselect' : 'select';
10708             Roo.log(m);
10709             var _t = this;
10710             _t[m](item, true, false);
10711             return true;
10712         }
10713         if(this.multiSelect || this.singleSelect){
10714             if(this.multiSelect && e.shiftKey && this.lastSelection){
10715                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10716             }else{
10717                 this.select(item, this.multiSelect && e.ctrlKey);
10718                 this.lastSelection = item;
10719             }
10720             e.preventDefault();
10721         }
10722         return true;
10723     },
10724
10725     /**
10726      * Get the number of selected nodes.
10727      * @return {Number}
10728      */
10729     getSelectionCount : function(){
10730         return this.selections.length;
10731     },
10732
10733     /**
10734      * Get the currently selected nodes.
10735      * @return {Array} An array of HTMLElements
10736      */
10737     getSelectedNodes : function(){
10738         return this.selections;
10739     },
10740
10741     /**
10742      * Get the indexes of the selected nodes.
10743      * @return {Array}
10744      */
10745     getSelectedIndexes : function(){
10746         var indexes = [], s = this.selections;
10747         for(var i = 0, len = s.length; i < len; i++){
10748             indexes.push(s[i].nodeIndex);
10749         }
10750         return indexes;
10751     },
10752
10753     /**
10754      * Clear all selections
10755      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10756      */
10757     clearSelections : function(suppressEvent){
10758         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10759             this.cmp.elements = this.selections;
10760             this.cmp.removeClass(this.selectedClass);
10761             this.selections = [];
10762             if(!suppressEvent){
10763                 this.fireEvent("selectionchange", this, this.selections);
10764             }
10765         }
10766     },
10767
10768     /**
10769      * Returns true if the passed node is selected
10770      * @param {HTMLElement/Number} node The node or node index
10771      * @return {Boolean}
10772      */
10773     isSelected : function(node){
10774         var s = this.selections;
10775         if(s.length < 1){
10776             return false;
10777         }
10778         node = this.getNode(node);
10779         return s.indexOf(node) !== -1;
10780     },
10781
10782     /**
10783      * Selects nodes.
10784      * @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
10785      * @param {Boolean} keepExisting (optional) true to keep existing selections
10786      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10787      */
10788     select : function(nodeInfo, keepExisting, suppressEvent){
10789         if(nodeInfo instanceof Array){
10790             if(!keepExisting){
10791                 this.clearSelections(true);
10792             }
10793             for(var i = 0, len = nodeInfo.length; i < len; i++){
10794                 this.select(nodeInfo[i], true, true);
10795             }
10796             return;
10797         } 
10798         var node = this.getNode(nodeInfo);
10799         if(!node || this.isSelected(node)){
10800             return; // already selected.
10801         }
10802         if(!keepExisting){
10803             this.clearSelections(true);
10804         }
10805         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10806             Roo.fly(node).addClass(this.selectedClass);
10807             this.selections.push(node);
10808             if(!suppressEvent){
10809                 this.fireEvent("selectionchange", this, this.selections);
10810             }
10811         }
10812         
10813         
10814     },
10815       /**
10816      * Unselects nodes.
10817      * @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
10818      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10819      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10820      */
10821     unselect : function(nodeInfo, keepExisting, suppressEvent)
10822     {
10823         if(nodeInfo instanceof Array){
10824             Roo.each(this.selections, function(s) {
10825                 this.unselect(s, nodeInfo);
10826             }, this);
10827             return;
10828         }
10829         var node = this.getNode(nodeInfo);
10830         if(!node || !this.isSelected(node)){
10831             Roo.log("not selected");
10832             return; // not selected.
10833         }
10834         // fireevent???
10835         var ns = [];
10836         Roo.each(this.selections, function(s) {
10837             if (s == node ) {
10838                 Roo.fly(node).removeClass(this.selectedClass);
10839
10840                 return;
10841             }
10842             ns.push(s);
10843         },this);
10844         
10845         this.selections= ns;
10846         this.fireEvent("selectionchange", this, this.selections);
10847     },
10848
10849     /**
10850      * Gets a template node.
10851      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10852      * @return {HTMLElement} The node or null if it wasn't found
10853      */
10854     getNode : function(nodeInfo){
10855         if(typeof nodeInfo == "string"){
10856             return document.getElementById(nodeInfo);
10857         }else if(typeof nodeInfo == "number"){
10858             return this.nodes[nodeInfo];
10859         }
10860         return nodeInfo;
10861     },
10862
10863     /**
10864      * Gets a range template nodes.
10865      * @param {Number} startIndex
10866      * @param {Number} endIndex
10867      * @return {Array} An array of nodes
10868      */
10869     getNodes : function(start, end){
10870         var ns = this.nodes;
10871         start = start || 0;
10872         end = typeof end == "undefined" ? ns.length - 1 : end;
10873         var nodes = [];
10874         if(start <= end){
10875             for(var i = start; i <= end; i++){
10876                 nodes.push(ns[i]);
10877             }
10878         } else{
10879             for(var i = start; i >= end; i--){
10880                 nodes.push(ns[i]);
10881             }
10882         }
10883         return nodes;
10884     },
10885
10886     /**
10887      * Finds the index of the passed node
10888      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10889      * @return {Number} The index of the node or -1
10890      */
10891     indexOf : function(node){
10892         node = this.getNode(node);
10893         if(typeof node.nodeIndex == "number"){
10894             return node.nodeIndex;
10895         }
10896         var ns = this.nodes;
10897         for(var i = 0, len = ns.length; i < len; i++){
10898             if(ns[i] == node){
10899                 return i;
10900             }
10901         }
10902         return -1;
10903     }
10904 });
10905 /*
10906  * - LGPL
10907  *
10908  * based on jquery fullcalendar
10909  * 
10910  */
10911
10912 Roo.bootstrap = Roo.bootstrap || {};
10913 /**
10914  * @class Roo.bootstrap.Calendar
10915  * @extends Roo.bootstrap.Component
10916  * Bootstrap Calendar class
10917  * @cfg {Boolean} loadMask (true|false) default false
10918  * @cfg {Object} header generate the user specific header of the calendar, default false
10919
10920  * @constructor
10921  * Create a new Container
10922  * @param {Object} config The config object
10923  */
10924
10925
10926
10927 Roo.bootstrap.Calendar = function(config){
10928     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
10929      this.addEvents({
10930         /**
10931              * @event select
10932              * Fires when a date is selected
10933              * @param {DatePicker} this
10934              * @param {Date} date The selected date
10935              */
10936         'select': true,
10937         /**
10938              * @event monthchange
10939              * Fires when the displayed month changes 
10940              * @param {DatePicker} this
10941              * @param {Date} date The selected month
10942              */
10943         'monthchange': true,
10944         /**
10945              * @event evententer
10946              * Fires when mouse over an event
10947              * @param {Calendar} this
10948              * @param {event} Event
10949              */
10950         'evententer': true,
10951         /**
10952              * @event eventleave
10953              * Fires when the mouse leaves an
10954              * @param {Calendar} this
10955              * @param {event}
10956              */
10957         'eventleave': true,
10958         /**
10959              * @event eventclick
10960              * Fires when the mouse click an
10961              * @param {Calendar} this
10962              * @param {event}
10963              */
10964         'eventclick': true
10965         
10966     });
10967
10968 };
10969
10970 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
10971     
10972      /**
10973      * @cfg {Number} startDay
10974      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10975      */
10976     startDay : 0,
10977     
10978     loadMask : false,
10979     
10980     header : false,
10981       
10982     getAutoCreate : function(){
10983         
10984         
10985         var fc_button = function(name, corner, style, content ) {
10986             return Roo.apply({},{
10987                 tag : 'span',
10988                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
10989                          (corner.length ?
10990                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
10991                             ''
10992                         ),
10993                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
10994                 unselectable: 'on'
10995             });
10996         };
10997         
10998         var header = {};
10999         
11000         if(!this.header){
11001             header = {
11002                 tag : 'table',
11003                 cls : 'fc-header',
11004                 style : 'width:100%',
11005                 cn : [
11006                     {
11007                         tag: 'tr',
11008                         cn : [
11009                             {
11010                                 tag : 'td',
11011                                 cls : 'fc-header-left',
11012                                 cn : [
11013                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
11014                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
11015                                     { tag: 'span', cls: 'fc-header-space' },
11016                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
11017
11018
11019                                 ]
11020                             },
11021
11022                             {
11023                                 tag : 'td',
11024                                 cls : 'fc-header-center',
11025                                 cn : [
11026                                     {
11027                                         tag: 'span',
11028                                         cls: 'fc-header-title',
11029                                         cn : {
11030                                             tag: 'H2',
11031                                             html : 'month / year'
11032                                         }
11033                                     }
11034
11035                                 ]
11036                             },
11037                             {
11038                                 tag : 'td',
11039                                 cls : 'fc-header-right',
11040                                 cn : [
11041                               /*      fc_button('month', 'left', '', 'month' ),
11042                                     fc_button('week', '', '', 'week' ),
11043                                     fc_button('day', 'right', '', 'day' )
11044                                 */    
11045
11046                                 ]
11047                             }
11048
11049                         ]
11050                     }
11051                 ]
11052             };
11053         }
11054         
11055         header = this.header;
11056         
11057        
11058         var cal_heads = function() {
11059             var ret = [];
11060             // fixme - handle this.
11061             
11062             for (var i =0; i < Date.dayNames.length; i++) {
11063                 var d = Date.dayNames[i];
11064                 ret.push({
11065                     tag: 'th',
11066                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
11067                     html : d.substring(0,3)
11068                 });
11069                 
11070             }
11071             ret[0].cls += ' fc-first';
11072             ret[6].cls += ' fc-last';
11073             return ret;
11074         };
11075         var cal_cell = function(n) {
11076             return  {
11077                 tag: 'td',
11078                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
11079                 cn : [
11080                     {
11081                         cn : [
11082                             {
11083                                 cls: 'fc-day-number',
11084                                 html: 'D'
11085                             },
11086                             {
11087                                 cls: 'fc-day-content',
11088                              
11089                                 cn : [
11090                                      {
11091                                         style: 'position: relative;' // height: 17px;
11092                                     }
11093                                 ]
11094                             }
11095                             
11096                             
11097                         ]
11098                     }
11099                 ]
11100                 
11101             }
11102         };
11103         var cal_rows = function() {
11104             
11105             var ret = []
11106             for (var r = 0; r < 6; r++) {
11107                 var row= {
11108                     tag : 'tr',
11109                     cls : 'fc-week',
11110                     cn : []
11111                 };
11112                 
11113                 for (var i =0; i < Date.dayNames.length; i++) {
11114                     var d = Date.dayNames[i];
11115                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
11116
11117                 }
11118                 row.cn[0].cls+=' fc-first';
11119                 row.cn[0].cn[0].style = 'min-height:90px';
11120                 row.cn[6].cls+=' fc-last';
11121                 ret.push(row);
11122                 
11123             }
11124             ret[0].cls += ' fc-first';
11125             ret[4].cls += ' fc-prev-last';
11126             ret[5].cls += ' fc-last';
11127             return ret;
11128             
11129         };
11130         
11131         var cal_table = {
11132             tag: 'table',
11133             cls: 'fc-border-separate',
11134             style : 'width:100%',
11135             cellspacing  : 0,
11136             cn : [
11137                 { 
11138                     tag: 'thead',
11139                     cn : [
11140                         { 
11141                             tag: 'tr',
11142                             cls : 'fc-first fc-last',
11143                             cn : cal_heads()
11144                         }
11145                     ]
11146                 },
11147                 { 
11148                     tag: 'tbody',
11149                     cn : cal_rows()
11150                 }
11151                   
11152             ]
11153         };
11154          
11155          var cfg = {
11156             cls : 'fc fc-ltr',
11157             cn : [
11158                 header,
11159                 {
11160                     cls : 'fc-content',
11161                     style : "position: relative;",
11162                     cn : [
11163                         {
11164                             cls : 'fc-view fc-view-month fc-grid',
11165                             style : 'position: relative',
11166                             unselectable : 'on',
11167                             cn : [
11168                                 {
11169                                     cls : 'fc-event-container',
11170                                     style : 'position:absolute;z-index:8;top:0;left:0;'
11171                                 },
11172                                 cal_table
11173                             ]
11174                         }
11175                     ]
11176     
11177                 }
11178            ] 
11179             
11180         };
11181         
11182          
11183         
11184         return cfg;
11185     },
11186     
11187     
11188     initEvents : function()
11189     {
11190         if(!this.store){
11191             throw "can not find store for calendar";
11192         }
11193         
11194         var mark = {
11195             tag: "div",
11196             cls:"x-dlg-mask",
11197             style: "text-align:center",
11198             cn: [
11199                 {
11200                     tag: "div",
11201                     style: "background-color:white;width:50%;margin:250 auto",
11202                     cn: [
11203                         {
11204                             tag: "img",
11205                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
11206                         },
11207                         {
11208                             tag: "span",
11209                             html: "Loading"
11210                         }
11211                         
11212                     ]
11213                 }
11214             ]
11215         }
11216         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
11217         
11218         var size = this.el.select('.fc-content', true).first().getSize();
11219         this.maskEl.setSize(size.width, size.height);
11220         this.maskEl.enableDisplayMode("block");
11221         if(!this.loadMask){
11222             this.maskEl.hide();
11223         }
11224         
11225         this.store = Roo.factory(this.store, Roo.data);
11226         this.store.on('load', this.onLoad, this);
11227         this.store.on('beforeload', this.onBeforeLoad, this);
11228         
11229         this.resize();
11230         
11231         this.cells = this.el.select('.fc-day',true);
11232         //Roo.log(this.cells);
11233         this.textNodes = this.el.query('.fc-day-number');
11234         this.cells.addClassOnOver('fc-state-hover');
11235         
11236         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
11237         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
11238         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
11239         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
11240         
11241         this.on('monthchange', this.onMonthChange, this);
11242         
11243         this.update(new Date().clearTime());
11244     },
11245     
11246     resize : function() {
11247         var sz  = this.el.getSize();
11248         
11249         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
11250         this.el.select('.fc-day-content div',true).setHeight(34);
11251     },
11252     
11253     
11254     // private
11255     showPrevMonth : function(e){
11256         this.update(this.activeDate.add("mo", -1));
11257     },
11258     showToday : function(e){
11259         this.update(new Date().clearTime());
11260     },
11261     // private
11262     showNextMonth : function(e){
11263         this.update(this.activeDate.add("mo", 1));
11264     },
11265
11266     // private
11267     showPrevYear : function(){
11268         this.update(this.activeDate.add("y", -1));
11269     },
11270
11271     // private
11272     showNextYear : function(){
11273         this.update(this.activeDate.add("y", 1));
11274     },
11275
11276     
11277    // private
11278     update : function(date)
11279     {
11280         var vd = this.activeDate;
11281         this.activeDate = date;
11282 //        if(vd && this.el){
11283 //            var t = date.getTime();
11284 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
11285 //                Roo.log('using add remove');
11286 //                
11287 //                this.fireEvent('monthchange', this, date);
11288 //                
11289 //                this.cells.removeClass("fc-state-highlight");
11290 //                this.cells.each(function(c){
11291 //                   if(c.dateValue == t){
11292 //                       c.addClass("fc-state-highlight");
11293 //                       setTimeout(function(){
11294 //                            try{c.dom.firstChild.focus();}catch(e){}
11295 //                       }, 50);
11296 //                       return false;
11297 //                   }
11298 //                   return true;
11299 //                });
11300 //                return;
11301 //            }
11302 //        }
11303         
11304         var days = date.getDaysInMonth();
11305         
11306         var firstOfMonth = date.getFirstDateOfMonth();
11307         var startingPos = firstOfMonth.getDay()-this.startDay;
11308         
11309         if(startingPos < this.startDay){
11310             startingPos += 7;
11311         }
11312         
11313         var pm = date.add(Date.MONTH, -1);
11314         var prevStart = pm.getDaysInMonth()-startingPos;
11315 //        
11316         this.cells = this.el.select('.fc-day',true);
11317         this.textNodes = this.el.query('.fc-day-number');
11318         this.cells.addClassOnOver('fc-state-hover');
11319         
11320         var cells = this.cells.elements;
11321         var textEls = this.textNodes;
11322         
11323         Roo.each(cells, function(cell){
11324             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
11325         });
11326         
11327         days += startingPos;
11328
11329         // convert everything to numbers so it's fast
11330         var day = 86400000;
11331         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
11332         //Roo.log(d);
11333         //Roo.log(pm);
11334         //Roo.log(prevStart);
11335         
11336         var today = new Date().clearTime().getTime();
11337         var sel = date.clearTime().getTime();
11338         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
11339         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
11340         var ddMatch = this.disabledDatesRE;
11341         var ddText = this.disabledDatesText;
11342         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
11343         var ddaysText = this.disabledDaysText;
11344         var format = this.format;
11345         
11346         var setCellClass = function(cal, cell){
11347             cell.row = 0;
11348             cell.events = [];
11349             cell.more = [];
11350             //Roo.log('set Cell Class');
11351             cell.title = "";
11352             var t = d.getTime();
11353             
11354             //Roo.log(d);
11355             
11356             cell.dateValue = t;
11357             if(t == today){
11358                 cell.className += " fc-today";
11359                 cell.className += " fc-state-highlight";
11360                 cell.title = cal.todayText;
11361             }
11362             if(t == sel){
11363                 // disable highlight in other month..
11364                 //cell.className += " fc-state-highlight";
11365                 
11366             }
11367             // disabling
11368             if(t < min) {
11369                 cell.className = " fc-state-disabled";
11370                 cell.title = cal.minText;
11371                 return;
11372             }
11373             if(t > max) {
11374                 cell.className = " fc-state-disabled";
11375                 cell.title = cal.maxText;
11376                 return;
11377             }
11378             if(ddays){
11379                 if(ddays.indexOf(d.getDay()) != -1){
11380                     cell.title = ddaysText;
11381                     cell.className = " fc-state-disabled";
11382                 }
11383             }
11384             if(ddMatch && format){
11385                 var fvalue = d.dateFormat(format);
11386                 if(ddMatch.test(fvalue)){
11387                     cell.title = ddText.replace("%0", fvalue);
11388                     cell.className = " fc-state-disabled";
11389                 }
11390             }
11391             
11392             if (!cell.initialClassName) {
11393                 cell.initialClassName = cell.dom.className;
11394             }
11395             
11396             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
11397         };
11398
11399         var i = 0;
11400         
11401         for(; i < startingPos; i++) {
11402             textEls[i].innerHTML = (++prevStart);
11403             d.setDate(d.getDate()+1);
11404             
11405             cells[i].className = "fc-past fc-other-month";
11406             setCellClass(this, cells[i]);
11407         }
11408         
11409         var intDay = 0;
11410         
11411         for(; i < days; i++){
11412             intDay = i - startingPos + 1;
11413             textEls[i].innerHTML = (intDay);
11414             d.setDate(d.getDate()+1);
11415             
11416             cells[i].className = ''; // "x-date-active";
11417             setCellClass(this, cells[i]);
11418         }
11419         var extraDays = 0;
11420         
11421         for(; i < 42; i++) {
11422             textEls[i].innerHTML = (++extraDays);
11423             d.setDate(d.getDate()+1);
11424             
11425             cells[i].className = "fc-future fc-other-month";
11426             setCellClass(this, cells[i]);
11427         }
11428         
11429         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11430         
11431         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11432         
11433         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11434         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11435         
11436         if(totalRows != 6){
11437             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11438             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11439         }
11440         
11441         this.fireEvent('monthchange', this, date);
11442         
11443         
11444         /*
11445         if(!this.internalRender){
11446             var main = this.el.dom.firstChild;
11447             var w = main.offsetWidth;
11448             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11449             Roo.fly(main).setWidth(w);
11450             this.internalRender = true;
11451             // opera does not respect the auto grow header center column
11452             // then, after it gets a width opera refuses to recalculate
11453             // without a second pass
11454             if(Roo.isOpera && !this.secondPass){
11455                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11456                 this.secondPass = true;
11457                 this.update.defer(10, this, [date]);
11458             }
11459         }
11460         */
11461         
11462     },
11463     
11464     findCell : function(dt) {
11465         dt = dt.clearTime().getTime();
11466         var ret = false;
11467         this.cells.each(function(c){
11468             //Roo.log("check " +c.dateValue + '?=' + dt);
11469             if(c.dateValue == dt){
11470                 ret = c;
11471                 return false;
11472             }
11473             return true;
11474         });
11475         
11476         return ret;
11477     },
11478     
11479     findCells : function(ev) {
11480         var s = ev.start.clone().clearTime().getTime();
11481        // Roo.log(s);
11482         var e= ev.end.clone().clearTime().getTime();
11483        // Roo.log(e);
11484         var ret = [];
11485         this.cells.each(function(c){
11486              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11487             
11488             if(c.dateValue > e){
11489                 return ;
11490             }
11491             if(c.dateValue < s){
11492                 return ;
11493             }
11494             ret.push(c);
11495         });
11496         
11497         return ret;    
11498     },
11499     
11500 //    findBestRow: function(cells)
11501 //    {
11502 //        var ret = 0;
11503 //        
11504 //        for (var i =0 ; i < cells.length;i++) {
11505 //            ret  = Math.max(cells[i].rows || 0,ret);
11506 //        }
11507 //        return ret;
11508 //        
11509 //    },
11510     
11511     
11512     addItem : function(ev)
11513     {
11514         // look for vertical location slot in
11515         var cells = this.findCells(ev);
11516         
11517 //        ev.row = this.findBestRow(cells);
11518         
11519         // work out the location.
11520         
11521         var crow = false;
11522         var rows = [];
11523         for(var i =0; i < cells.length; i++) {
11524             
11525             cells[i].row = cells[0].row;
11526             
11527             if(i == 0){
11528                 cells[i].row = cells[i].row + 1;
11529             }
11530             
11531             if (!crow) {
11532                 crow = {
11533                     start : cells[i],
11534                     end :  cells[i]
11535                 };
11536                 continue;
11537             }
11538             if (crow.start.getY() == cells[i].getY()) {
11539                 // on same row.
11540                 crow.end = cells[i];
11541                 continue;
11542             }
11543             // different row.
11544             rows.push(crow);
11545             crow = {
11546                 start: cells[i],
11547                 end : cells[i]
11548             };
11549             
11550         }
11551         
11552         rows.push(crow);
11553         ev.els = [];
11554         ev.rows = rows;
11555         ev.cells = cells;
11556         
11557         cells[0].events.push(ev);
11558         
11559         this.calevents.push(ev);
11560     },
11561     
11562     clearEvents: function() {
11563         
11564         if(!this.calevents){
11565             return;
11566         }
11567         
11568         Roo.each(this.cells.elements, function(c){
11569             c.row = 0;
11570             c.events = [];
11571             c.more = [];
11572         });
11573         
11574         Roo.each(this.calevents, function(e) {
11575             Roo.each(e.els, function(el) {
11576                 el.un('mouseenter' ,this.onEventEnter, this);
11577                 el.un('mouseleave' ,this.onEventLeave, this);
11578                 el.remove();
11579             },this);
11580         },this);
11581         
11582         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
11583             e.remove();
11584         });
11585         
11586     },
11587     
11588     renderEvents: function()
11589     {   
11590         var _this = this;
11591         
11592         this.cells.each(function(c) {
11593             
11594             if(c.row < 5){
11595                 return;
11596             }
11597             
11598             var ev = c.events;
11599             
11600             var r = 4;
11601             if(c.row != c.events.length){
11602                 r = 4 - (4 - (c.row - c.events.length));
11603             }
11604             
11605             c.events = ev.slice(0, r);
11606             c.more = ev.slice(r);
11607             
11608             if(c.more.length && c.more.length == 1){
11609                 c.events.push(c.more.pop());
11610             }
11611             
11612             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
11613             
11614         });
11615             
11616         this.cells.each(function(c) {
11617             
11618             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
11619             
11620             
11621             for (var e = 0; e < c.events.length; e++){
11622                 var ev = c.events[e];
11623                 var rows = ev.rows;
11624                 
11625                 for(var i = 0; i < rows.length; i++) {
11626                 
11627                     // how many rows should it span..
11628
11629                     var  cfg = {
11630                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11631                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11632
11633                         unselectable : "on",
11634                         cn : [
11635                             {
11636                                 cls: 'fc-event-inner',
11637                                 cn : [
11638     //                                {
11639     //                                  tag:'span',
11640     //                                  cls: 'fc-event-time',
11641     //                                  html : cells.length > 1 ? '' : ev.time
11642     //                                },
11643                                     {
11644                                       tag:'span',
11645                                       cls: 'fc-event-title',
11646                                       html : String.format('{0}', ev.title)
11647                                     }
11648
11649
11650                                 ]
11651                             },
11652                             {
11653                                 cls: 'ui-resizable-handle ui-resizable-e',
11654                                 html : '&nbsp;&nbsp;&nbsp'
11655                             }
11656
11657                         ]
11658                     };
11659
11660                     if (i == 0) {
11661                         cfg.cls += ' fc-event-start';
11662                     }
11663                     if ((i+1) == rows.length) {
11664                         cfg.cls += ' fc-event-end';
11665                     }
11666
11667                     var ctr = _this.el.select('.fc-event-container',true).first();
11668                     var cg = ctr.createChild(cfg);
11669
11670                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11671                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11672
11673                     var r = (c.more.length) ? 1 : 0;
11674                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
11675                     cg.setWidth(ebox.right - sbox.x -2);
11676
11677                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
11678                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
11679                     cg.on('click', _this.onEventClick, _this, ev);
11680
11681                     ev.els.push(cg);
11682                     
11683                 }
11684                 
11685             }
11686             
11687             
11688             if(c.more.length){
11689                 var  cfg = {
11690                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
11691                     style : 'position: absolute',
11692                     unselectable : "on",
11693                     cn : [
11694                         {
11695                             cls: 'fc-event-inner',
11696                             cn : [
11697                                 {
11698                                   tag:'span',
11699                                   cls: 'fc-event-title',
11700                                   html : 'More'
11701                                 }
11702
11703
11704                             ]
11705                         },
11706                         {
11707                             cls: 'ui-resizable-handle ui-resizable-e',
11708                             html : '&nbsp;&nbsp;&nbsp'
11709                         }
11710
11711                     ]
11712                 };
11713
11714                 var ctr = _this.el.select('.fc-event-container',true).first();
11715                 var cg = ctr.createChild(cfg);
11716
11717                 var sbox = c.select('.fc-day-content',true).first().getBox();
11718                 var ebox = c.select('.fc-day-content',true).first().getBox();
11719                 //Roo.log(cg);
11720                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
11721                 cg.setWidth(ebox.right - sbox.x -2);
11722
11723                 cg.on('click', _this.onMoreEventClick, _this, c.more);
11724                 
11725             }
11726             
11727         });
11728         
11729         
11730         
11731     },
11732     
11733     onEventEnter: function (e, el,event,d) {
11734         this.fireEvent('evententer', this, el, event);
11735     },
11736     
11737     onEventLeave: function (e, el,event,d) {
11738         this.fireEvent('eventleave', this, el, event);
11739     },
11740     
11741     onEventClick: function (e, el,event,d) {
11742         this.fireEvent('eventclick', this, el, event);
11743     },
11744     
11745     onMonthChange: function () {
11746         this.store.load();
11747     },
11748     
11749     onMoreEventClick: function(e, el, more)
11750     {
11751         var _this = this;
11752         
11753         this.calpopover.placement = 'right';
11754         this.calpopover.setTitle('More');
11755         
11756         this.calpopover.setContent('');
11757         
11758         var ctr = this.calpopover.el.select('.popover-content', true).first();
11759         
11760         Roo.each(more, function(m){
11761             var cfg = {
11762                 cls : 'fc-event-hori fc-event-draggable',
11763                 html : m.title
11764             }
11765             var cg = ctr.createChild(cfg);
11766             
11767             cg.on('click', _this.onEventClick, _this, m);
11768         });
11769         
11770         this.calpopover.show(el);
11771         
11772         
11773     },
11774     
11775     onLoad: function () 
11776     {   
11777         this.calevents = [];
11778         var cal = this;
11779         
11780         if(this.store.getCount() > 0){
11781             this.store.data.each(function(d){
11782                cal.addItem({
11783                     id : d.data.id,
11784                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11785                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11786                     time : d.data.start_time,
11787                     title : d.data.title,
11788                     description : d.data.description,
11789                     venue : d.data.venue
11790                 });
11791             });
11792         }
11793         
11794         this.renderEvents();
11795         
11796         if(this.calevents.length && this.loadMask){
11797             this.maskEl.hide();
11798         }
11799     },
11800     
11801     onBeforeLoad: function()
11802     {
11803         this.clearEvents();
11804         if(this.loadMask){
11805             this.maskEl.show();
11806         }
11807     }
11808 });
11809
11810  
11811  /*
11812  * - LGPL
11813  *
11814  * element
11815  * 
11816  */
11817
11818 /**
11819  * @class Roo.bootstrap.Popover
11820  * @extends Roo.bootstrap.Component
11821  * Bootstrap Popover class
11822  * @cfg {String} html contents of the popover   (or false to use children..)
11823  * @cfg {String} title of popover (or false to hide)
11824  * @cfg {String} placement how it is placed
11825  * @cfg {String} trigger click || hover (or false to trigger manually)
11826  * @cfg {String} over what (parent or false to trigger manually.)
11827  * 
11828  * @constructor
11829  * Create a new Popover
11830  * @param {Object} config The config object
11831  */
11832
11833 Roo.bootstrap.Popover = function(config){
11834     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11835 };
11836
11837 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11838     
11839     title: 'Fill in a title',
11840     html: false,
11841     
11842     placement : 'right',
11843     trigger : 'hover', // hover
11844     
11845     over: 'parent',
11846     
11847     can_build_overlaid : false,
11848     
11849     getChildContainer : function()
11850     {
11851         return this.el.select('.popover-content',true).first();
11852     },
11853     
11854     getAutoCreate : function(){
11855          Roo.log('make popover?');
11856         var cfg = {
11857            cls : 'popover roo-dynamic',
11858            style: 'display:block',
11859            cn : [
11860                 {
11861                     cls : 'arrow'
11862                 },
11863                 {
11864                     cls : 'popover-inner',
11865                     cn : [
11866                         {
11867                             tag: 'h3',
11868                             cls: 'popover-title',
11869                             html : this.title
11870                         },
11871                         {
11872                             cls : 'popover-content',
11873                             html : this.html
11874                         }
11875                     ]
11876                     
11877                 }
11878            ]
11879         };
11880         
11881         return cfg;
11882     },
11883     setTitle: function(str)
11884     {
11885         this.el.select('.popover-title',true).first().dom.innerHTML = str;
11886     },
11887     setContent: function(str)
11888     {
11889         this.el.select('.popover-content',true).first().dom.innerHTML = str;
11890     },
11891     // as it get's added to the bottom of the page.
11892     onRender : function(ct, position)
11893     {
11894         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
11895         if(!this.el){
11896             var cfg = Roo.apply({},  this.getAutoCreate());
11897             cfg.id = Roo.id();
11898             
11899             if (this.cls) {
11900                 cfg.cls += ' ' + this.cls;
11901             }
11902             if (this.style) {
11903                 cfg.style = this.style;
11904             }
11905             Roo.log("adding to ")
11906             this.el = Roo.get(document.body).createChild(cfg, position);
11907             Roo.log(this.el);
11908         }
11909         this.initEvents();
11910     },
11911     
11912     initEvents : function()
11913     {
11914         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
11915         this.el.enableDisplayMode('block');
11916         this.el.hide();
11917         if (this.over === false) {
11918             return; 
11919         }
11920         if (this.triggers === false) {
11921             return;
11922         }
11923         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11924         var triggers = this.trigger ? this.trigger.split(' ') : [];
11925         Roo.each(triggers, function(trigger) {
11926         
11927             if (trigger == 'click') {
11928                 on_el.on('click', this.toggle, this);
11929             } else if (trigger != 'manual') {
11930                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
11931                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
11932       
11933                 on_el.on(eventIn  ,this.enter, this);
11934                 on_el.on(eventOut, this.leave, this);
11935             }
11936         }, this);
11937         
11938     },
11939     
11940     
11941     // private
11942     timeout : null,
11943     hoverState : null,
11944     
11945     toggle : function () {
11946         this.hoverState == 'in' ? this.leave() : this.enter();
11947     },
11948     
11949     enter : function () {
11950        
11951     
11952         clearTimeout(this.timeout);
11953     
11954         this.hoverState = 'in'
11955     
11956         if (!this.delay || !this.delay.show) {
11957             this.show();
11958             return 
11959         }
11960         var _t = this;
11961         this.timeout = setTimeout(function () {
11962             if (_t.hoverState == 'in') {
11963                 _t.show();
11964             }
11965         }, this.delay.show)
11966     },
11967     leave : function() {
11968         clearTimeout(this.timeout);
11969     
11970         this.hoverState = 'out'
11971     
11972         if (!this.delay || !this.delay.hide) {
11973             this.hide();
11974             return 
11975         }
11976         var _t = this;
11977         this.timeout = setTimeout(function () {
11978             if (_t.hoverState == 'out') {
11979                 _t.hide();
11980             }
11981         }, this.delay.hide)
11982     },
11983     
11984     show : function (on_el)
11985     {
11986         if (!on_el) {
11987             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11988         }
11989         // set content.
11990         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
11991         if (this.html !== false) {
11992             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
11993         }
11994         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
11995         if (!this.title.length) {
11996             this.el.select('.popover-title',true).hide();
11997         }
11998         
11999         var placement = typeof this.placement == 'function' ?
12000             this.placement.call(this, this.el, on_el) :
12001             this.placement;
12002             
12003         var autoToken = /\s?auto?\s?/i;
12004         var autoPlace = autoToken.test(placement);
12005         if (autoPlace) {
12006             placement = placement.replace(autoToken, '') || 'top';
12007         }
12008         
12009         //this.el.detach()
12010         //this.el.setXY([0,0]);
12011         this.el.show();
12012         this.el.dom.style.display='block';
12013         this.el.addClass(placement);
12014         
12015         //this.el.appendTo(on_el);
12016         
12017         var p = this.getPosition();
12018         var box = this.el.getBox();
12019         
12020         if (autoPlace) {
12021             // fixme..
12022         }
12023         var align = Roo.bootstrap.Popover.alignment[placement]
12024         this.el.alignTo(on_el, align[0],align[1]);
12025         //var arrow = this.el.select('.arrow',true).first();
12026         //arrow.set(align[2], 
12027         
12028         this.el.addClass('in');
12029         this.hoverState = null;
12030         
12031         if (this.el.hasClass('fade')) {
12032             // fade it?
12033         }
12034         
12035     },
12036     hide : function()
12037     {
12038         this.el.setXY([0,0]);
12039         this.el.removeClass('in');
12040         this.el.hide();
12041         
12042     }
12043     
12044 });
12045
12046 Roo.bootstrap.Popover.alignment = {
12047     'left' : ['r-l', [-10,0], 'right'],
12048     'right' : ['l-r', [10,0], 'left'],
12049     'bottom' : ['t-b', [0,10], 'top'],
12050     'top' : [ 'b-t', [0,-10], 'bottom']
12051 };
12052
12053  /*
12054  * - LGPL
12055  *
12056  * Progress
12057  * 
12058  */
12059
12060 /**
12061  * @class Roo.bootstrap.Progress
12062  * @extends Roo.bootstrap.Component
12063  * Bootstrap Progress class
12064  * @cfg {Boolean} striped striped of the progress bar
12065  * @cfg {Boolean} active animated of the progress bar
12066  * 
12067  * 
12068  * @constructor
12069  * Create a new Progress
12070  * @param {Object} config The config object
12071  */
12072
12073 Roo.bootstrap.Progress = function(config){
12074     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12075 };
12076
12077 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
12078     
12079     striped : false,
12080     active: false,
12081     
12082     getAutoCreate : function(){
12083         var cfg = {
12084             tag: 'div',
12085             cls: 'progress'
12086         };
12087         
12088         
12089         if(this.striped){
12090             cfg.cls += ' progress-striped';
12091         }
12092       
12093         if(this.active){
12094             cfg.cls += ' active';
12095         }
12096         
12097         
12098         return cfg;
12099     }
12100    
12101 });
12102
12103  
12104
12105  /*
12106  * - LGPL
12107  *
12108  * ProgressBar
12109  * 
12110  */
12111
12112 /**
12113  * @class Roo.bootstrap.ProgressBar
12114  * @extends Roo.bootstrap.Component
12115  * Bootstrap ProgressBar class
12116  * @cfg {Number} aria_valuenow aria-value now
12117  * @cfg {Number} aria_valuemin aria-value min
12118  * @cfg {Number} aria_valuemax aria-value max
12119  * @cfg {String} label label for the progress bar
12120  * @cfg {String} panel (success | info | warning | danger )
12121  * @cfg {String} role role of the progress bar
12122  * @cfg {String} sr_only text
12123  * 
12124  * 
12125  * @constructor
12126  * Create a new ProgressBar
12127  * @param {Object} config The config object
12128  */
12129
12130 Roo.bootstrap.ProgressBar = function(config){
12131     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12132 };
12133
12134 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
12135     
12136     aria_valuenow : 0,
12137     aria_valuemin : 0,
12138     aria_valuemax : 100,
12139     label : false,
12140     panel : false,
12141     role : false,
12142     sr_only: false,
12143     
12144     getAutoCreate : function()
12145     {
12146         
12147         var cfg = {
12148             tag: 'div',
12149             cls: 'progress-bar',
12150             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12151         };
12152         
12153         if(this.sr_only){
12154             cfg.cn = {
12155                 tag: 'span',
12156                 cls: 'sr-only',
12157                 html: this.sr_only
12158             }
12159         }
12160         
12161         if(this.role){
12162             cfg.role = this.role;
12163         }
12164         
12165         if(this.aria_valuenow){
12166             cfg['aria-valuenow'] = this.aria_valuenow;
12167         }
12168         
12169         if(this.aria_valuemin){
12170             cfg['aria-valuemin'] = this.aria_valuemin;
12171         }
12172         
12173         if(this.aria_valuemax){
12174             cfg['aria-valuemax'] = this.aria_valuemax;
12175         }
12176         
12177         if(this.label && !this.sr_only){
12178             cfg.html = this.label;
12179         }
12180         
12181         if(this.panel){
12182             cfg.cls += ' progress-bar-' + this.panel;
12183         }
12184         
12185         return cfg;
12186     },
12187     
12188     update : function(aria_valuenow)
12189     {
12190         this.aria_valuenow = aria_valuenow;
12191         
12192         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12193     }
12194    
12195 });
12196
12197  
12198
12199  /*
12200  * - LGPL
12201  *
12202  * TabPanel
12203  * 
12204  */
12205
12206 /**
12207  * @class Roo.bootstrap.TabPanel
12208  * @extends Roo.bootstrap.Component
12209  * Bootstrap TabPanel class
12210  * @cfg {Boolean} active panel active
12211  * @cfg {String} html panel content
12212  * @cfg {String} tabId tab relate id
12213  * @cfg {String} navId The navbar which triggers show hide
12214  * 
12215  * 
12216  * @constructor
12217  * Create a new TabPanel
12218  * @param {Object} config The config object
12219  */
12220
12221 Roo.bootstrap.TabPanel = function(config){
12222     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
12223      this.addEvents({
12224         /**
12225              * @event changed
12226              * Fires when the active status changes
12227              * @param {Roo.bootstrap.TabPanel} this
12228              * @param {Boolean} state the new state
12229             
12230          */
12231         'changed': true
12232      });
12233 };
12234
12235 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
12236     
12237     active: false,
12238     html: false,
12239     tabId: false,
12240     navId : false,
12241     
12242     getAutoCreate : function(){
12243         var cfg = {
12244             tag: 'div',
12245             cls: 'tab-pane',
12246             html: this.html || ''
12247         };
12248         
12249         if(this.active){
12250             cfg.cls += ' active';
12251         }
12252         
12253         if(this.tabId){
12254             cfg.tabId = this.tabId;
12255         }
12256         
12257         return cfg;
12258     },
12259     onRender : function(ct, position)
12260     {
12261        // Roo.log("Call onRender: " + this.xtype);
12262         
12263         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
12264         
12265         if (this.navId && this.tabId) {
12266             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
12267             if (!item) {
12268                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
12269             } else {
12270                 item.on('changed', function(item, state) {
12271                     this.setActive(state);
12272                 }, this);
12273             }
12274         }
12275         
12276     },
12277     setActive: function(state)
12278     {
12279         Roo.log("panel - set active " + this.tabId + "=" + state);
12280         
12281         this.active = state;
12282         if (!state) {
12283             this.el.removeClass('active');
12284             
12285         } else  if (!this.el.hasClass('active')) {
12286             this.el.addClass('active');
12287         }
12288         this.fireEvent('changed', this, state);
12289     }
12290     
12291     
12292 });
12293  
12294
12295  
12296
12297  /*
12298  * - LGPL
12299  *
12300  * DateField
12301  * 
12302  */
12303
12304 /**
12305  * @class Roo.bootstrap.DateField
12306  * @extends Roo.bootstrap.Input
12307  * Bootstrap DateField class
12308  * @cfg {Number} weekStart default 0
12309  * @cfg {Number} weekStart default 0
12310  * @cfg {Number} viewMode default empty, (months|years)
12311  * @cfg {Number} minViewMode default empty, (months|years)
12312  * @cfg {Number} startDate default -Infinity
12313  * @cfg {Number} endDate default Infinity
12314  * @cfg {Boolean} todayHighlight default false
12315  * @cfg {Boolean} todayBtn default false
12316  * @cfg {Boolean} calendarWeeks default false
12317  * @cfg {Object} daysOfWeekDisabled default empty
12318  * 
12319  * @cfg {Boolean} keyboardNavigation default true
12320  * @cfg {String} language default en
12321  * 
12322  * @constructor
12323  * Create a new DateField
12324  * @param {Object} config The config object
12325  */
12326
12327 Roo.bootstrap.DateField = function(config){
12328     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
12329      this.addEvents({
12330             /**
12331              * @event show
12332              * Fires when this field show.
12333              * @param {Roo.bootstrap.DateField} this
12334              * @param {Mixed} date The date value
12335              */
12336             show : true,
12337             /**
12338              * @event show
12339              * Fires when this field hide.
12340              * @param {Roo.bootstrap.DateField} this
12341              * @param {Mixed} date The date value
12342              */
12343             hide : true,
12344             /**
12345              * @event select
12346              * Fires when select a date.
12347              * @param {Roo.bootstrap.DateField} this
12348              * @param {Mixed} date The date value
12349              */
12350             select : true
12351         });
12352 };
12353
12354 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
12355     
12356     /**
12357      * @cfg {String} format
12358      * The default date format string which can be overriden for localization support.  The format must be
12359      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
12360      */
12361     format : "m/d/y",
12362     /**
12363      * @cfg {String} altFormats
12364      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
12365      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
12366      */
12367     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
12368     
12369     weekStart : 0,
12370     
12371     viewMode : '',
12372     
12373     minViewMode : '',
12374     
12375     todayHighlight : false,
12376     
12377     todayBtn: false,
12378     
12379     language: 'en',
12380     
12381     keyboardNavigation: true,
12382     
12383     calendarWeeks: false,
12384     
12385     startDate: -Infinity,
12386     
12387     endDate: Infinity,
12388     
12389     daysOfWeekDisabled: [],
12390     
12391     _events: [],
12392     
12393     UTCDate: function()
12394     {
12395         return new Date(Date.UTC.apply(Date, arguments));
12396     },
12397     
12398     UTCToday: function()
12399     {
12400         var today = new Date();
12401         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12402     },
12403     
12404     getDate: function() {
12405             var d = this.getUTCDate();
12406             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12407     },
12408     
12409     getUTCDate: function() {
12410             return this.date;
12411     },
12412     
12413     setDate: function(d) {
12414             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12415     },
12416     
12417     setUTCDate: function(d) {
12418             this.date = d;
12419             this.setValue(this.formatDate(this.date));
12420     },
12421         
12422     onRender: function(ct, position)
12423     {
12424         
12425         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12426         
12427         this.language = this.language || 'en';
12428         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12429         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12430         
12431         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12432         this.format = this.format || 'm/d/y';
12433         this.isInline = false;
12434         this.isInput = true;
12435         this.component = this.el.select('.add-on', true).first() || false;
12436         this.component = (this.component && this.component.length === 0) ? false : this.component;
12437         this.hasInput = this.component && this.inputEL().length;
12438         
12439         if (typeof(this.minViewMode === 'string')) {
12440             switch (this.minViewMode) {
12441                 case 'months':
12442                     this.minViewMode = 1;
12443                     break;
12444                 case 'years':
12445                     this.minViewMode = 2;
12446                     break;
12447                 default:
12448                     this.minViewMode = 0;
12449                     break;
12450             }
12451         }
12452         
12453         if (typeof(this.viewMode === 'string')) {
12454             switch (this.viewMode) {
12455                 case 'months':
12456                     this.viewMode = 1;
12457                     break;
12458                 case 'years':
12459                     this.viewMode = 2;
12460                     break;
12461                 default:
12462                     this.viewMode = 0;
12463                     break;
12464             }
12465         }
12466                 
12467         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12468         
12469         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12470         
12471         this.picker().on('mousedown', this.onMousedown, this);
12472         this.picker().on('click', this.onClick, this);
12473         
12474         this.picker().addClass('datepicker-dropdown');
12475         
12476         this.startViewMode = this.viewMode;
12477         
12478         
12479         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12480             if(!this.calendarWeeks){
12481                 v.remove();
12482                 return;
12483             };
12484             
12485             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12486             v.attr('colspan', function(i, val){
12487                 return parseInt(val) + 1;
12488             });
12489         })
12490                         
12491         
12492         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12493         
12494         this.setStartDate(this.startDate);
12495         this.setEndDate(this.endDate);
12496         
12497         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12498         
12499         this.fillDow();
12500         this.fillMonths();
12501         this.update();
12502         this.showMode();
12503         
12504         if(this.isInline) {
12505             this.show();
12506         }
12507     },
12508     
12509     picker : function()
12510     {
12511         return this.el.select('.datepicker', true).first();
12512     },
12513     
12514     fillDow: function()
12515     {
12516         var dowCnt = this.weekStart;
12517         
12518         var dow = {
12519             tag: 'tr',
12520             cn: [
12521                 
12522             ]
12523         };
12524         
12525         if(this.calendarWeeks){
12526             dow.cn.push({
12527                 tag: 'th',
12528                 cls: 'cw',
12529                 html: '&nbsp;'
12530             })
12531         }
12532         
12533         while (dowCnt < this.weekStart + 7) {
12534             dow.cn.push({
12535                 tag: 'th',
12536                 cls: 'dow',
12537                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12538             });
12539         }
12540         
12541         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12542     },
12543     
12544     fillMonths: function()
12545     {    
12546         var i = 0
12547         var months = this.picker().select('>.datepicker-months td', true).first();
12548         
12549         months.dom.innerHTML = '';
12550         
12551         while (i < 12) {
12552             var month = {
12553                 tag: 'span',
12554                 cls: 'month',
12555                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12556             }
12557             
12558             months.createChild(month);
12559         }
12560         
12561     },
12562     
12563     update: function(){
12564         
12565         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12566         
12567         if (this.date < this.startDate) {
12568             this.viewDate = new Date(this.startDate);
12569         } else if (this.date > this.endDate) {
12570             this.viewDate = new Date(this.endDate);
12571         } else {
12572             this.viewDate = new Date(this.date);
12573         }
12574         
12575         this.fill();
12576     },
12577     
12578     fill: function() {
12579         var d = new Date(this.viewDate),
12580                 year = d.getUTCFullYear(),
12581                 month = d.getUTCMonth(),
12582                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12583                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12584                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12585                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12586                 currentDate = this.date && this.date.valueOf(),
12587                 today = this.UTCToday();
12588         
12589         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12590         
12591 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12592         
12593 //        this.picker.select('>tfoot th.today').
12594 //                                              .text(dates[this.language].today)
12595 //                                              .toggle(this.todayBtn !== false);
12596     
12597         this.updateNavArrows();
12598         this.fillMonths();
12599                                                 
12600         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12601         
12602         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12603          
12604         prevMonth.setUTCDate(day);
12605         
12606         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12607         
12608         var nextMonth = new Date(prevMonth);
12609         
12610         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12611         
12612         nextMonth = nextMonth.valueOf();
12613         
12614         var fillMonths = false;
12615         
12616         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12617         
12618         while(prevMonth.valueOf() < nextMonth) {
12619             var clsName = '';
12620             
12621             if (prevMonth.getUTCDay() === this.weekStart) {
12622                 if(fillMonths){
12623                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12624                 }
12625                     
12626                 fillMonths = {
12627                     tag: 'tr',
12628                     cn: []
12629                 };
12630                 
12631                 if(this.calendarWeeks){
12632                     // ISO 8601: First week contains first thursday.
12633                     // ISO also states week starts on Monday, but we can be more abstract here.
12634                     var
12635                     // Start of current week: based on weekstart/current date
12636                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12637                     // Thursday of this week
12638                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12639                     // First Thursday of year, year from thursday
12640                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12641                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12642                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12643                     
12644                     fillMonths.cn.push({
12645                         tag: 'td',
12646                         cls: 'cw',
12647                         html: calWeek
12648                     });
12649                 }
12650             }
12651             
12652             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12653                 clsName += ' old';
12654             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12655                 clsName += ' new';
12656             }
12657             if (this.todayHighlight &&
12658                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12659                 prevMonth.getUTCMonth() == today.getMonth() &&
12660                 prevMonth.getUTCDate() == today.getDate()) {
12661                 clsName += ' today';
12662             }
12663             
12664             if (currentDate && prevMonth.valueOf() === currentDate) {
12665                 clsName += ' active';
12666             }
12667             
12668             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12669                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12670                     clsName += ' disabled';
12671             }
12672             
12673             fillMonths.cn.push({
12674                 tag: 'td',
12675                 cls: 'day ' + clsName,
12676                 html: prevMonth.getDate()
12677             })
12678             
12679             prevMonth.setDate(prevMonth.getDate()+1);
12680         }
12681           
12682         var currentYear = this.date && this.date.getUTCFullYear();
12683         var currentMonth = this.date && this.date.getUTCMonth();
12684         
12685         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12686         
12687         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12688             v.removeClass('active');
12689             
12690             if(currentYear === year && k === currentMonth){
12691                 v.addClass('active');
12692             }
12693             
12694             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12695                 v.addClass('disabled');
12696             }
12697             
12698         });
12699         
12700         
12701         year = parseInt(year/10, 10) * 10;
12702         
12703         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12704         
12705         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12706         
12707         year -= 1;
12708         for (var i = -1; i < 11; i++) {
12709             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12710                 tag: 'span',
12711                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12712                 html: year
12713             })
12714             
12715             year += 1;
12716         }
12717     },
12718     
12719     showMode: function(dir) {
12720         if (dir) {
12721             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12722         }
12723         Roo.each(this.picker().select('>div',true).elements, function(v){
12724             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12725             v.hide();
12726         });
12727         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12728     },
12729     
12730     place: function()
12731     {
12732         if(this.isInline) return;
12733         
12734         this.picker().removeClass(['bottom', 'top']);
12735         
12736         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12737             /*
12738              * place to the top of element!
12739              *
12740              */
12741             
12742             this.picker().addClass('top');
12743             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12744             
12745             return;
12746         }
12747         
12748         this.picker().addClass('bottom');
12749         
12750         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12751     },
12752     
12753     parseDate : function(value){
12754         if(!value || value instanceof Date){
12755             return value;
12756         }
12757         var v = Date.parseDate(value, this.format);
12758         if (!v && this.useIso) {
12759             v = Date.parseDate(value, 'Y-m-d');
12760         }
12761         if(!v && this.altFormats){
12762             if(!this.altFormatsArray){
12763                 this.altFormatsArray = this.altFormats.split("|");
12764             }
12765             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12766                 v = Date.parseDate(value, this.altFormatsArray[i]);
12767             }
12768         }
12769         return v;
12770     },
12771     
12772     formatDate : function(date, fmt){
12773         return (!date || !(date instanceof Date)) ?
12774         date : date.dateFormat(fmt || this.format);
12775     },
12776     
12777     onFocus : function()
12778     {
12779         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12780         this.show();
12781     },
12782     
12783     onBlur : function()
12784     {
12785         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12786         this.hide();
12787     },
12788     
12789     show : function()
12790     {
12791         this.picker().show();
12792         this.update();
12793         this.place();
12794         
12795         this.fireEvent('show', this, this.date);
12796     },
12797     
12798     hide : function()
12799     {
12800         if(this.isInline) return;
12801         this.picker().hide();
12802         this.viewMode = this.startViewMode;
12803         this.showMode();
12804         
12805         this.fireEvent('hide', this, this.date);
12806         
12807     },
12808     
12809     onMousedown: function(e){
12810         e.stopPropagation();
12811         e.preventDefault();
12812     },
12813     
12814     keyup: function(e){
12815         Roo.bootstrap.DateField.superclass.keyup.call(this);
12816         this.update();
12817         
12818     },
12819
12820     setValue: function(v){
12821         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12822         
12823         this.fireEvent('select', this, this.date);
12824         
12825     },
12826     
12827     fireKey: function(e){
12828         if (!this.picker().isVisible()){
12829             if (e.keyCode == 27) // allow escape to hide and re-show picker
12830                 this.show();
12831             return;
12832         }
12833         var dateChanged = false,
12834         dir, day, month,
12835         newDate, newViewDate;
12836         switch(e.keyCode){
12837             case 27: // escape
12838                 this.hide();
12839                 e.preventDefault();
12840                 break;
12841             case 37: // left
12842             case 39: // right
12843                 if (!this.keyboardNavigation) break;
12844                 dir = e.keyCode == 37 ? -1 : 1;
12845                 
12846                 if (e.ctrlKey){
12847                     newDate = this.moveYear(this.date, dir);
12848                     newViewDate = this.moveYear(this.viewDate, dir);
12849                 } else if (e.shiftKey){
12850                     newDate = this.moveMonth(this.date, dir);
12851                     newViewDate = this.moveMonth(this.viewDate, dir);
12852                 } else {
12853                     newDate = new Date(this.date);
12854                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12855                     newViewDate = new Date(this.viewDate);
12856                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12857                 }
12858                 if (this.dateWithinRange(newDate)){
12859                     this.date = newDate;
12860                     this.viewDate = newViewDate;
12861                     this.setValue(this.formatDate(this.date));
12862                     this.update();
12863                     e.preventDefault();
12864                     dateChanged = true;
12865                 }
12866                 break;
12867             case 38: // up
12868             case 40: // down
12869                 if (!this.keyboardNavigation) break;
12870                 dir = e.keyCode == 38 ? -1 : 1;
12871                 if (e.ctrlKey){
12872                     newDate = this.moveYear(this.date, dir);
12873                     newViewDate = this.moveYear(this.viewDate, dir);
12874                 } else if (e.shiftKey){
12875                     newDate = this.moveMonth(this.date, dir);
12876                     newViewDate = this.moveMonth(this.viewDate, dir);
12877                 } else {
12878                     newDate = new Date(this.date);
12879                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12880                     newViewDate = new Date(this.viewDate);
12881                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12882                 }
12883                 if (this.dateWithinRange(newDate)){
12884                     this.date = newDate;
12885                     this.viewDate = newViewDate;
12886                     this.setValue(this.formatDate(this.date));
12887                     this.update();
12888                     e.preventDefault();
12889                     dateChanged = true;
12890                 }
12891                 break;
12892             case 13: // enter
12893                 this.setValue(this.formatDate(this.date));
12894                 this.hide();
12895                 e.preventDefault();
12896                 break;
12897             case 9: // tab
12898                 this.setValue(this.formatDate(this.date));
12899                 this.hide();
12900                 break;
12901         }
12902     },
12903     
12904     
12905     onClick: function(e) {
12906         e.stopPropagation();
12907         e.preventDefault();
12908         
12909         var target = e.getTarget();
12910         
12911         if(target.nodeName.toLowerCase() === 'i'){
12912             target = Roo.get(target).dom.parentNode;
12913         }
12914         
12915         var nodeName = target.nodeName;
12916         var className = target.className;
12917         var html = target.innerHTML;
12918         
12919         switch(nodeName.toLowerCase()) {
12920             case 'th':
12921                 switch(className) {
12922                     case 'switch':
12923                         this.showMode(1);
12924                         break;
12925                     case 'prev':
12926                     case 'next':
12927                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12928                         switch(this.viewMode){
12929                                 case 0:
12930                                         this.viewDate = this.moveMonth(this.viewDate, dir);
12931                                         break;
12932                                 case 1:
12933                                 case 2:
12934                                         this.viewDate = this.moveYear(this.viewDate, dir);
12935                                         break;
12936                         }
12937                         this.fill();
12938                         break;
12939                     case 'today':
12940                         var date = new Date();
12941                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12942                         this.fill()
12943                         this.setValue(this.formatDate(this.date));
12944                         this.hide();
12945                         break;
12946                 }
12947                 break;
12948             case 'span':
12949                 if (className.indexOf('disabled') === -1) {
12950                     this.viewDate.setUTCDate(1);
12951                     if (className.indexOf('month') !== -1) {
12952                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12953                     } else {
12954                         var year = parseInt(html, 10) || 0;
12955                         this.viewDate.setUTCFullYear(year);
12956                         
12957                     }
12958                     this.showMode(-1);
12959                     this.fill();
12960                 }
12961                 break;
12962                 
12963             case 'td':
12964                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12965                     var day = parseInt(html, 10) || 1;
12966                     var year = this.viewDate.getUTCFullYear(),
12967                         month = this.viewDate.getUTCMonth();
12968
12969                     if (className.indexOf('old') !== -1) {
12970                         if(month === 0 ){
12971                             month = 11;
12972                             year -= 1;
12973                         }else{
12974                             month -= 1;
12975                         }
12976                     } else if (className.indexOf('new') !== -1) {
12977                         if (month == 11) {
12978                             month = 0;
12979                             year += 1;
12980                         } else {
12981                             month += 1;
12982                         }
12983                     }
12984                     this.date = this.UTCDate(year, month, day,0,0,0,0);
12985                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
12986                     this.fill();
12987                     this.setValue(this.formatDate(this.date));
12988                     this.hide();
12989                 }
12990                 break;
12991         }
12992     },
12993     
12994     setStartDate: function(startDate){
12995         this.startDate = startDate || -Infinity;
12996         if (this.startDate !== -Infinity) {
12997             this.startDate = this.parseDate(this.startDate);
12998         }
12999         this.update();
13000         this.updateNavArrows();
13001     },
13002
13003     setEndDate: function(endDate){
13004         this.endDate = endDate || Infinity;
13005         if (this.endDate !== Infinity) {
13006             this.endDate = this.parseDate(this.endDate);
13007         }
13008         this.update();
13009         this.updateNavArrows();
13010     },
13011     
13012     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
13013         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
13014         if (typeof(this.daysOfWeekDisabled) !== 'object') {
13015             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
13016         }
13017         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
13018             return parseInt(d, 10);
13019         });
13020         this.update();
13021         this.updateNavArrows();
13022     },
13023     
13024     updateNavArrows: function() {
13025         var d = new Date(this.viewDate),
13026         year = d.getUTCFullYear(),
13027         month = d.getUTCMonth();
13028         
13029         Roo.each(this.picker().select('.prev', true).elements, function(v){
13030             v.show();
13031             switch (this.viewMode) {
13032                 case 0:
13033
13034                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
13035                         v.hide();
13036                     }
13037                     break;
13038                 case 1:
13039                 case 2:
13040                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
13041                         v.hide();
13042                     }
13043                     break;
13044             }
13045         });
13046         
13047         Roo.each(this.picker().select('.next', true).elements, function(v){
13048             v.show();
13049             switch (this.viewMode) {
13050                 case 0:
13051
13052                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13053                         v.hide();
13054                     }
13055                     break;
13056                 case 1:
13057                 case 2:
13058                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13059                         v.hide();
13060                     }
13061                     break;
13062             }
13063         })
13064     },
13065     
13066     moveMonth: function(date, dir){
13067         if (!dir) return date;
13068         var new_date = new Date(date.valueOf()),
13069         day = new_date.getUTCDate(),
13070         month = new_date.getUTCMonth(),
13071         mag = Math.abs(dir),
13072         new_month, test;
13073         dir = dir > 0 ? 1 : -1;
13074         if (mag == 1){
13075             test = dir == -1
13076             // If going back one month, make sure month is not current month
13077             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13078             ? function(){
13079                 return new_date.getUTCMonth() == month;
13080             }
13081             // If going forward one month, make sure month is as expected
13082             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13083             : function(){
13084                 return new_date.getUTCMonth() != new_month;
13085             };
13086             new_month = month + dir;
13087             new_date.setUTCMonth(new_month);
13088             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13089             if (new_month < 0 || new_month > 11)
13090                 new_month = (new_month + 12) % 12;
13091         } else {
13092             // For magnitudes >1, move one month at a time...
13093             for (var i=0; i<mag; i++)
13094                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13095                 new_date = this.moveMonth(new_date, dir);
13096             // ...then reset the day, keeping it in the new month
13097             new_month = new_date.getUTCMonth();
13098             new_date.setUTCDate(day);
13099             test = function(){
13100                 return new_month != new_date.getUTCMonth();
13101             };
13102         }
13103         // Common date-resetting loop -- if date is beyond end of month, make it
13104         // end of month
13105         while (test()){
13106             new_date.setUTCDate(--day);
13107             new_date.setUTCMonth(new_month);
13108         }
13109         return new_date;
13110     },
13111
13112     moveYear: function(date, dir){
13113         return this.moveMonth(date, dir*12);
13114     },
13115
13116     dateWithinRange: function(date){
13117         return date >= this.startDate && date <= this.endDate;
13118     },
13119
13120     
13121     remove: function() {
13122         this.picker().remove();
13123     }
13124    
13125 });
13126
13127 Roo.apply(Roo.bootstrap.DateField,  {
13128     
13129     head : {
13130         tag: 'thead',
13131         cn: [
13132         {
13133             tag: 'tr',
13134             cn: [
13135             {
13136                 tag: 'th',
13137                 cls: 'prev',
13138                 html: '<i class="icon-arrow-left"/>'
13139             },
13140             {
13141                 tag: 'th',
13142                 cls: 'switch',
13143                 colspan: '5'
13144             },
13145             {
13146                 tag: 'th',
13147                 cls: 'next',
13148                 html: '<i class="icon-arrow-right"/>'
13149             }
13150
13151             ]
13152         }
13153         ]
13154     },
13155     
13156     content : {
13157         tag: 'tbody',
13158         cn: [
13159         {
13160             tag: 'tr',
13161             cn: [
13162             {
13163                 tag: 'td',
13164                 colspan: '7'
13165             }
13166             ]
13167         }
13168         ]
13169     },
13170     
13171     footer : {
13172         tag: 'tfoot',
13173         cn: [
13174         {
13175             tag: 'tr',
13176             cn: [
13177             {
13178                 tag: 'th',
13179                 colspan: '7',
13180                 cls: 'today'
13181             }
13182                     
13183             ]
13184         }
13185         ]
13186     },
13187     
13188     dates:{
13189         en: {
13190             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13191             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13192             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13193             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13194             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13195             today: "Today"
13196         }
13197     },
13198     
13199     modes: [
13200     {
13201         clsName: 'days',
13202         navFnc: 'Month',
13203         navStep: 1
13204     },
13205     {
13206         clsName: 'months',
13207         navFnc: 'FullYear',
13208         navStep: 1
13209     },
13210     {
13211         clsName: 'years',
13212         navFnc: 'FullYear',
13213         navStep: 10
13214     }]
13215 });
13216
13217 Roo.apply(Roo.bootstrap.DateField,  {
13218   
13219     template : {
13220         tag: 'div',
13221         cls: 'datepicker dropdown-menu',
13222         cn: [
13223         {
13224             tag: 'div',
13225             cls: 'datepicker-days',
13226             cn: [
13227             {
13228                 tag: 'table',
13229                 cls: 'table-condensed',
13230                 cn:[
13231                 Roo.bootstrap.DateField.head,
13232                 {
13233                     tag: 'tbody'
13234                 },
13235                 Roo.bootstrap.DateField.footer
13236                 ]
13237             }
13238             ]
13239         },
13240         {
13241             tag: 'div',
13242             cls: 'datepicker-months',
13243             cn: [
13244             {
13245                 tag: 'table',
13246                 cls: 'table-condensed',
13247                 cn:[
13248                 Roo.bootstrap.DateField.head,
13249                 Roo.bootstrap.DateField.content,
13250                 Roo.bootstrap.DateField.footer
13251                 ]
13252             }
13253             ]
13254         },
13255         {
13256             tag: 'div',
13257             cls: 'datepicker-years',
13258             cn: [
13259             {
13260                 tag: 'table',
13261                 cls: 'table-condensed',
13262                 cn:[
13263                 Roo.bootstrap.DateField.head,
13264                 Roo.bootstrap.DateField.content,
13265                 Roo.bootstrap.DateField.footer
13266                 ]
13267             }
13268             ]
13269         }
13270         ]
13271     }
13272 });
13273
13274  
13275
13276  /*
13277  * - LGPL
13278  *
13279  * TimeField
13280  * 
13281  */
13282
13283 /**
13284  * @class Roo.bootstrap.TimeField
13285  * @extends Roo.bootstrap.Input
13286  * Bootstrap DateField class
13287  * 
13288  * 
13289  * @constructor
13290  * Create a new TimeField
13291  * @param {Object} config The config object
13292  */
13293
13294 Roo.bootstrap.TimeField = function(config){
13295     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
13296     this.addEvents({
13297             /**
13298              * @event show
13299              * Fires when this field show.
13300              * @param {Roo.bootstrap.DateField} this
13301              * @param {Mixed} date The date value
13302              */
13303             show : true,
13304             /**
13305              * @event show
13306              * Fires when this field hide.
13307              * @param {Roo.bootstrap.DateField} this
13308              * @param {Mixed} date The date value
13309              */
13310             hide : true,
13311             /**
13312              * @event select
13313              * Fires when select a date.
13314              * @param {Roo.bootstrap.DateField} this
13315              * @param {Mixed} date The date value
13316              */
13317             select : true
13318         });
13319 };
13320
13321 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
13322     
13323     /**
13324      * @cfg {String} format
13325      * The default time format string which can be overriden for localization support.  The format must be
13326      * valid according to {@link Date#parseDate} (defaults to 'H:i').
13327      */
13328     format : "H:i",
13329        
13330     onRender: function(ct, position)
13331     {
13332         
13333         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
13334                 
13335         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
13336         
13337         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13338         
13339         this.pop = this.picker().select('>.datepicker-time',true).first();
13340         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
13341         
13342         this.picker().on('mousedown', this.onMousedown, this);
13343         this.picker().on('click', this.onClick, this);
13344         
13345         this.picker().addClass('datepicker-dropdown');
13346     
13347         this.fillTime();
13348         this.update();
13349             
13350         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
13351         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
13352         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
13353         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
13354         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
13355         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
13356
13357     },
13358     
13359     fireKey: function(e){
13360         if (!this.picker().isVisible()){
13361             if (e.keyCode == 27) // allow escape to hide and re-show picker
13362                 this.show();
13363             return;
13364         }
13365
13366         e.preventDefault();
13367         
13368         switch(e.keyCode){
13369             case 27: // escape
13370                 this.hide();
13371                 break;
13372             case 37: // left
13373             case 39: // right
13374                 this.onTogglePeriod();
13375                 break;
13376             case 38: // up
13377                 this.onIncrementMinutes();
13378                 break;
13379             case 40: // down
13380                 this.onDecrementMinutes();
13381                 break;
13382             case 13: // enter
13383             case 9: // tab
13384                 this.setTime();
13385                 break;
13386         }
13387     },
13388     
13389     onClick: function(e) {
13390         e.stopPropagation();
13391         e.preventDefault();
13392     },
13393     
13394     picker : function()
13395     {
13396         return this.el.select('.datepicker', true).first();
13397     },
13398     
13399     fillTime: function()
13400     {    
13401         var time = this.pop.select('tbody', true).first();
13402         
13403         time.dom.innerHTML = '';
13404         
13405         time.createChild({
13406             tag: 'tr',
13407             cn: [
13408                 {
13409                     tag: 'td',
13410                     cn: [
13411                         {
13412                             tag: 'a',
13413                             href: '#',
13414                             cls: 'btn',
13415                             cn: [
13416                                 {
13417                                     tag: 'span',
13418                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
13419                                 }
13420                             ]
13421                         } 
13422                     ]
13423                 },
13424                 {
13425                     tag: 'td',
13426                     cls: 'separator'
13427                 },
13428                 {
13429                     tag: 'td',
13430                     cn: [
13431                         {
13432                             tag: 'a',
13433                             href: '#',
13434                             cls: 'btn',
13435                             cn: [
13436                                 {
13437                                     tag: 'span',
13438                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
13439                                 }
13440                             ]
13441                         }
13442                     ]
13443                 },
13444                 {
13445                     tag: 'td',
13446                     cls: 'separator'
13447                 }
13448             ]
13449         });
13450         
13451         time.createChild({
13452             tag: 'tr',
13453             cn: [
13454                 {
13455                     tag: 'td',
13456                     cn: [
13457                         {
13458                             tag: 'span',
13459                             cls: 'timepicker-hour',
13460                             html: '00'
13461                         }  
13462                     ]
13463                 },
13464                 {
13465                     tag: 'td',
13466                     cls: 'separator',
13467                     html: ':'
13468                 },
13469                 {
13470                     tag: 'td',
13471                     cn: [
13472                         {
13473                             tag: 'span',
13474                             cls: 'timepicker-minute',
13475                             html: '00'
13476                         }  
13477                     ]
13478                 },
13479                 {
13480                     tag: 'td',
13481                     cls: 'separator'
13482                 },
13483                 {
13484                     tag: 'td',
13485                     cn: [
13486                         {
13487                             tag: 'button',
13488                             type: 'button',
13489                             cls: 'btn btn-primary period',
13490                             html: 'AM'
13491                             
13492                         }
13493                     ]
13494                 }
13495             ]
13496         });
13497         
13498         time.createChild({
13499             tag: 'tr',
13500             cn: [
13501                 {
13502                     tag: 'td',
13503                     cn: [
13504                         {
13505                             tag: 'a',
13506                             href: '#',
13507                             cls: 'btn',
13508                             cn: [
13509                                 {
13510                                     tag: 'span',
13511                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
13512                                 }
13513                             ]
13514                         }
13515                     ]
13516                 },
13517                 {
13518                     tag: 'td',
13519                     cls: 'separator'
13520                 },
13521                 {
13522                     tag: 'td',
13523                     cn: [
13524                         {
13525                             tag: 'a',
13526                             href: '#',
13527                             cls: 'btn',
13528                             cn: [
13529                                 {
13530                                     tag: 'span',
13531                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13532                                 }
13533                             ]
13534                         }
13535                     ]
13536                 },
13537                 {
13538                     tag: 'td',
13539                     cls: 'separator'
13540                 }
13541             ]
13542         });
13543         
13544     },
13545     
13546     update: function()
13547     {
13548         
13549         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13550         
13551         this.fill();
13552     },
13553     
13554     fill: function() 
13555     {
13556         var hours = this.time.getHours();
13557         var minutes = this.time.getMinutes();
13558         var period = 'AM';
13559         
13560         if(hours > 11){
13561             period = 'PM';
13562         }
13563         
13564         if(hours == 0){
13565             hours = 12;
13566         }
13567         
13568         
13569         if(hours > 12){
13570             hours = hours - 12;
13571         }
13572         
13573         if(hours < 10){
13574             hours = '0' + hours;
13575         }
13576         
13577         if(minutes < 10){
13578             minutes = '0' + minutes;
13579         }
13580         
13581         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13582         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13583         this.pop.select('button', true).first().dom.innerHTML = period;
13584         
13585     },
13586     
13587     place: function()
13588     {   
13589         this.picker().removeClass(['bottom', 'top']);
13590         
13591         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13592             /*
13593              * place to the top of element!
13594              *
13595              */
13596             
13597             this.picker().addClass('top');
13598             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13599             
13600             return;
13601         }
13602         
13603         this.picker().addClass('bottom');
13604         
13605         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13606     },
13607   
13608     onFocus : function()
13609     {
13610         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13611         this.show();
13612     },
13613     
13614     onBlur : function()
13615     {
13616         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13617         this.hide();
13618     },
13619     
13620     show : function()
13621     {
13622         this.picker().show();
13623         this.pop.show();
13624         this.update();
13625         this.place();
13626         
13627         this.fireEvent('show', this, this.date);
13628     },
13629     
13630     hide : function()
13631     {
13632         this.picker().hide();
13633         this.pop.hide();
13634         
13635         this.fireEvent('hide', this, this.date);
13636     },
13637     
13638     setTime : function()
13639     {
13640         this.hide();
13641         this.setValue(this.time.format(this.format));
13642         
13643         this.fireEvent('select', this, this.date);
13644         
13645         
13646     },
13647     
13648     onMousedown: function(e){
13649         e.stopPropagation();
13650         e.preventDefault();
13651     },
13652     
13653     onIncrementHours: function()
13654     {
13655         Roo.log('onIncrementHours');
13656         this.time = this.time.add(Date.HOUR, 1);
13657         this.update();
13658         
13659     },
13660     
13661     onDecrementHours: function()
13662     {
13663         Roo.log('onDecrementHours');
13664         this.time = this.time.add(Date.HOUR, -1);
13665         this.update();
13666     },
13667     
13668     onIncrementMinutes: function()
13669     {
13670         Roo.log('onIncrementMinutes');
13671         this.time = this.time.add(Date.MINUTE, 1);
13672         this.update();
13673     },
13674     
13675     onDecrementMinutes: function()
13676     {
13677         Roo.log('onDecrementMinutes');
13678         this.time = this.time.add(Date.MINUTE, -1);
13679         this.update();
13680     },
13681     
13682     onTogglePeriod: function()
13683     {
13684         Roo.log('onTogglePeriod');
13685         this.time = this.time.add(Date.HOUR, 12);
13686         this.update();
13687     }
13688     
13689    
13690 });
13691
13692 Roo.apply(Roo.bootstrap.TimeField,  {
13693     
13694     content : {
13695         tag: 'tbody',
13696         cn: [
13697             {
13698                 tag: 'tr',
13699                 cn: [
13700                 {
13701                     tag: 'td',
13702                     colspan: '7'
13703                 }
13704                 ]
13705             }
13706         ]
13707     },
13708     
13709     footer : {
13710         tag: 'tfoot',
13711         cn: [
13712             {
13713                 tag: 'tr',
13714                 cn: [
13715                 {
13716                     tag: 'th',
13717                     colspan: '7',
13718                     cls: '',
13719                     cn: [
13720                         {
13721                             tag: 'button',
13722                             cls: 'btn btn-info ok',
13723                             html: 'OK'
13724                         }
13725                     ]
13726                 }
13727
13728                 ]
13729             }
13730         ]
13731     }
13732 });
13733
13734 Roo.apply(Roo.bootstrap.TimeField,  {
13735   
13736     template : {
13737         tag: 'div',
13738         cls: 'datepicker dropdown-menu',
13739         cn: [
13740             {
13741                 tag: 'div',
13742                 cls: 'datepicker-time',
13743                 cn: [
13744                 {
13745                     tag: 'table',
13746                     cls: 'table-condensed',
13747                     cn:[
13748                     Roo.bootstrap.TimeField.content,
13749                     Roo.bootstrap.TimeField.footer
13750                     ]
13751                 }
13752                 ]
13753             }
13754         ]
13755     }
13756 });
13757
13758  
13759
13760  /*
13761  * - LGPL
13762  *
13763  * CheckBox
13764  * 
13765  */
13766
13767 /**
13768  * @class Roo.bootstrap.CheckBox
13769  * @extends Roo.bootstrap.Input
13770  * Bootstrap CheckBox class
13771  * 
13772  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13773  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13774  * @cfg {String} boxLabel The text that appears beside the checkbox
13775  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
13776  * @cfg {Boolean} checked initnal the element
13777  * 
13778  * 
13779  * @constructor
13780  * Create a new CheckBox
13781  * @param {Object} config The config object
13782  */
13783
13784 Roo.bootstrap.CheckBox = function(config){
13785     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13786    
13787         this.addEvents({
13788             /**
13789             * @event check
13790             * Fires when the element is checked or unchecked.
13791             * @param {Roo.bootstrap.CheckBox} this This input
13792             * @param {Boolean} checked The new checked value
13793             */
13794            check : true
13795         });
13796 };
13797
13798 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13799     
13800     inputType: 'checkbox',
13801     inputValue: 1,
13802     valueOff: 0,
13803     boxLabel: false,
13804     checked: false,
13805     weight : false,
13806     
13807     getAutoCreate : function()
13808     {
13809         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13810         
13811         var id = Roo.id();
13812         
13813         var cfg = {};
13814         
13815         cfg.cls = 'form-group checkbox' //input-group
13816         
13817         
13818         
13819         
13820         var input =  {
13821             tag: 'input',
13822             id : id,
13823             type : this.inputType,
13824             value : (!this.checked) ? this.valueOff : this.inputValue,
13825             cls : 'roo-checkbox', //'form-box',
13826             placeholder : this.placeholder || ''
13827             
13828         };
13829         
13830         if (this.weight) { // Validity check?
13831             cfg.cls += " checkbox-" + this.weight;
13832         }
13833         
13834         if (this.disabled) {
13835             input.disabled=true;
13836         }
13837         
13838         if(this.checked){
13839             input.checked = this.checked;
13840         }
13841         
13842         if (this.name) {
13843             input.name = this.name;
13844         }
13845         
13846         if (this.size) {
13847             input.cls += ' input-' + this.size;
13848         }
13849         
13850         var settings=this;
13851         ['xs','sm','md','lg'].map(function(size){
13852             if (settings[size]) {
13853                 cfg.cls += ' col-' + size + '-' + settings[size];
13854             }
13855         });
13856         
13857        
13858         
13859         var inputblock = input;
13860         
13861         
13862         
13863         
13864         if (this.before || this.after) {
13865             
13866             inputblock = {
13867                 cls : 'input-group',
13868                 cn :  [] 
13869             };
13870             if (this.before) {
13871                 inputblock.cn.push({
13872                     tag :'span',
13873                     cls : 'input-group-addon',
13874                     html : this.before
13875                 });
13876             }
13877             inputblock.cn.push(input);
13878             if (this.after) {
13879                 inputblock.cn.push({
13880                     tag :'span',
13881                     cls : 'input-group-addon',
13882                     html : this.after
13883                 });
13884             }
13885             
13886         };
13887         
13888         if (align ==='left' && this.fieldLabel.length) {
13889                 Roo.log("left and has label");
13890                 cfg.cn = [
13891                     
13892                     {
13893                         tag: 'label',
13894                         'for' :  id,
13895                         cls : 'control-label col-md-' + this.labelWidth,
13896                         html : this.fieldLabel
13897                         
13898                     },
13899                     {
13900                         cls : "col-md-" + (12 - this.labelWidth), 
13901                         cn: [
13902                             inputblock
13903                         ]
13904                     }
13905                     
13906                 ];
13907         } else if ( this.fieldLabel.length) {
13908                 Roo.log(" label");
13909                 cfg.cn = [
13910                    
13911                     {
13912                         tag: this.boxLabel ? 'span' : 'label',
13913                         'for': id,
13914                         cls: 'control-label box-input-label',
13915                         //cls : 'input-group-addon',
13916                         html : this.fieldLabel
13917                         
13918                     },
13919                     
13920                     inputblock
13921                     
13922                 ];
13923
13924         } else {
13925             
13926                 Roo.log(" no label && no align");
13927                 cfg.cn = [  inputblock ] ;
13928                 
13929                 
13930         };
13931          if(this.boxLabel){
13932             cfg.cn.push( {
13933                 tag: 'label',
13934                 'for': id,
13935                 cls: 'box-label',
13936                 html: this.boxLabel
13937                 
13938             });
13939         }
13940         
13941         
13942        
13943         return cfg;
13944         
13945     },
13946     
13947     /**
13948      * return the real input element.
13949      */
13950     inputEl: function ()
13951     {
13952         return this.el.select('input.roo-checkbox',true).first();
13953     },
13954     
13955     label: function()
13956     {
13957         return this.el.select('label.control-label',true).first();
13958     },
13959     
13960     initEvents : function()
13961     {
13962 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
13963         
13964         this.inputEl().on('click', this.onClick,  this);
13965         
13966     },
13967     
13968     onClick : function()
13969     {   
13970         this.setChecked(!this.checked);
13971     },
13972     
13973     setChecked : function(state,suppressEvent)
13974     {
13975         this.checked = state;
13976         
13977         this.inputEl().dom.checked = state;
13978         
13979         if(suppressEvent !== true){
13980             this.fireEvent('check', this, state);
13981         }
13982         
13983         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13984         
13985     },
13986     
13987     setValue : function(v,suppressEvent)
13988     {
13989         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
13990     }
13991     
13992 });
13993
13994  
13995 /*
13996  * - LGPL
13997  *
13998  * Radio
13999  * 
14000  */
14001
14002 /**
14003  * @class Roo.bootstrap.Radio
14004  * @extends Roo.bootstrap.CheckBox
14005  * Bootstrap Radio class
14006
14007  * @constructor
14008  * Create a new Radio
14009  * @param {Object} config The config object
14010  */
14011
14012 Roo.bootstrap.Radio = function(config){
14013     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
14014    
14015 };
14016
14017 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
14018     
14019     inputType: 'radio',
14020     inputValue: '',
14021     valueOff: '',
14022     
14023     getAutoCreate : function()
14024     {
14025         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14026         
14027         var id = Roo.id();
14028         
14029         var cfg = {};
14030         
14031         cfg.cls = 'form-group radio' //input-group
14032         
14033         var input =  {
14034             tag: 'input',
14035             id : id,
14036             type : this.inputType,
14037             value : (!this.checked) ? this.valueOff : this.inputValue,
14038             cls : 'roo-radio',
14039             placeholder : this.placeholder || ''
14040             
14041         };
14042           if (this.weight) { // Validity check?
14043             cfg.cls += " radio-" + this.weight;
14044         }
14045         if (this.disabled) {
14046             input.disabled=true;
14047         }
14048         
14049         if(this.checked){
14050             input.checked = this.checked;
14051         }
14052         
14053         if (this.name) {
14054             input.name = this.name;
14055         }
14056         
14057         if (this.size) {
14058             input.cls += ' input-' + this.size;
14059         }
14060         
14061         var settings=this;
14062         ['xs','sm','md','lg'].map(function(size){
14063             if (settings[size]) {
14064                 cfg.cls += ' col-' + size + '-' + settings[size];
14065             }
14066         });
14067         
14068         var inputblock = input;
14069         
14070         if (this.before || this.after) {
14071             
14072             inputblock = {
14073                 cls : 'input-group',
14074                 cn :  [] 
14075             };
14076             if (this.before) {
14077                 inputblock.cn.push({
14078                     tag :'span',
14079                     cls : 'input-group-addon',
14080                     html : this.before
14081                 });
14082             }
14083             inputblock.cn.push(input);
14084             if (this.after) {
14085                 inputblock.cn.push({
14086                     tag :'span',
14087                     cls : 'input-group-addon',
14088                     html : this.after
14089                 });
14090             }
14091             
14092         };
14093         
14094         if (align ==='left' && this.fieldLabel.length) {
14095                 Roo.log("left and has label");
14096                 cfg.cn = [
14097                     
14098                     {
14099                         tag: 'label',
14100                         'for' :  id,
14101                         cls : 'control-label col-md-' + this.labelWidth,
14102                         html : this.fieldLabel
14103                         
14104                     },
14105                     {
14106                         cls : "col-md-" + (12 - this.labelWidth), 
14107                         cn: [
14108                             inputblock
14109                         ]
14110                     }
14111                     
14112                 ];
14113         } else if ( this.fieldLabel.length) {
14114                 Roo.log(" label");
14115                  cfg.cn = [
14116                    
14117                     {
14118                         tag: 'label',
14119                         'for': id,
14120                         cls: 'control-label box-input-label',
14121                         //cls : 'input-group-addon',
14122                         html : this.fieldLabel
14123                         
14124                     },
14125                     
14126                     inputblock
14127                     
14128                 ];
14129
14130         } else {
14131             
14132                    Roo.log(" no label && no align");
14133                 cfg.cn = [
14134                     
14135                         inputblock
14136                     
14137                 ];
14138                 
14139                 
14140         };
14141         
14142         if(this.boxLabel){
14143             cfg.cn.push({
14144                 tag: 'label',
14145                 'for': id,
14146                 cls: 'box-label',
14147                 html: this.boxLabel
14148             })
14149         }
14150         
14151         return cfg;
14152         
14153     },
14154     inputEl: function ()
14155     {
14156         return this.el.select('input.roo-radio',true).first();
14157     },
14158     onClick : function()
14159     {   
14160         this.setChecked(true);
14161     },
14162     
14163     setChecked : function(state,suppressEvent)
14164     {
14165         if(state){
14166             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14167                 v.dom.checked = false;
14168             });
14169         }
14170         
14171         this.checked = state;
14172         this.inputEl().dom.checked = state;
14173         
14174         if(suppressEvent !== true){
14175             this.fireEvent('check', this, state);
14176         }
14177         
14178         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14179         
14180     },
14181     
14182     getGroupValue : function()
14183     {
14184         var value = ''
14185         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14186             if(v.dom.checked == true){
14187                 value = v.dom.value;
14188             }
14189         });
14190         
14191         return value;
14192     },
14193     
14194     /**
14195      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
14196      * @return {Mixed} value The field value
14197      */
14198     getValue : function(){
14199         return this.getGroupValue();
14200     }
14201     
14202 });
14203
14204  
14205 //<script type="text/javascript">
14206
14207 /*
14208  * Based  Ext JS Library 1.1.1
14209  * Copyright(c) 2006-2007, Ext JS, LLC.
14210  * LGPL
14211  *
14212  */
14213  
14214 /**
14215  * @class Roo.HtmlEditorCore
14216  * @extends Roo.Component
14217  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
14218  *
14219  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
14220  */
14221
14222 Roo.HtmlEditorCore = function(config){
14223     
14224     
14225     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
14226     this.addEvents({
14227         /**
14228          * @event initialize
14229          * Fires when the editor is fully initialized (including the iframe)
14230          * @param {Roo.HtmlEditorCore} this
14231          */
14232         initialize: true,
14233         /**
14234          * @event activate
14235          * Fires when the editor is first receives the focus. Any insertion must wait
14236          * until after this event.
14237          * @param {Roo.HtmlEditorCore} this
14238          */
14239         activate: true,
14240          /**
14241          * @event beforesync
14242          * Fires before the textarea is updated with content from the editor iframe. Return false
14243          * to cancel the sync.
14244          * @param {Roo.HtmlEditorCore} this
14245          * @param {String} html
14246          */
14247         beforesync: true,
14248          /**
14249          * @event beforepush
14250          * Fires before the iframe editor is updated with content from the textarea. Return false
14251          * to cancel the push.
14252          * @param {Roo.HtmlEditorCore} this
14253          * @param {String} html
14254          */
14255         beforepush: true,
14256          /**
14257          * @event sync
14258          * Fires when the textarea is updated with content from the editor iframe.
14259          * @param {Roo.HtmlEditorCore} this
14260          * @param {String} html
14261          */
14262         sync: true,
14263          /**
14264          * @event push
14265          * Fires when the iframe editor is updated with content from the textarea.
14266          * @param {Roo.HtmlEditorCore} this
14267          * @param {String} html
14268          */
14269         push: true,
14270         
14271         /**
14272          * @event editorevent
14273          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14274          * @param {Roo.HtmlEditorCore} this
14275          */
14276         editorevent: true
14277     });
14278      
14279 };
14280
14281
14282 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
14283
14284
14285      /**
14286      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
14287      */
14288     
14289     owner : false,
14290     
14291      /**
14292      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14293      *                        Roo.resizable.
14294      */
14295     resizable : false,
14296      /**
14297      * @cfg {Number} height (in pixels)
14298      */   
14299     height: 300,
14300    /**
14301      * @cfg {Number} width (in pixels)
14302      */   
14303     width: 500,
14304     
14305     /**
14306      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14307      * 
14308      */
14309     stylesheets: false,
14310     
14311     // id of frame..
14312     frameId: false,
14313     
14314     // private properties
14315     validationEvent : false,
14316     deferHeight: true,
14317     initialized : false,
14318     activated : false,
14319     sourceEditMode : false,
14320     onFocus : Roo.emptyFn,
14321     iframePad:3,
14322     hideMode:'offsets',
14323     
14324     clearUp: true,
14325     
14326      
14327     
14328
14329     /**
14330      * Protected method that will not generally be called directly. It
14331      * is called when the editor initializes the iframe with HTML contents. Override this method if you
14332      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
14333      */
14334     getDocMarkup : function(){
14335         // body styles..
14336         var st = '';
14337         Roo.log(this.stylesheets);
14338         
14339         // inherit styels from page...?? 
14340         if (this.stylesheets === false) {
14341             
14342             Roo.get(document.head).select('style').each(function(node) {
14343                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14344             });
14345             
14346             Roo.get(document.head).select('link').each(function(node) { 
14347                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14348             });
14349             
14350         } else if (!this.stylesheets.length) {
14351                 // simple..
14352                 st = '<style type="text/css">' +
14353                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14354                    '</style>';
14355         } else {
14356             Roo.each(this.stylesheets, function(s) {
14357                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
14358             });
14359             
14360         }
14361         
14362         st +=  '<style type="text/css">' +
14363             'IMG { cursor: pointer } ' +
14364         '</style>';
14365
14366         
14367         return '<html><head>' + st  +
14368             //<style type="text/css">' +
14369             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14370             //'</style>' +
14371             ' </head><body class="roo-htmleditor-body"></body></html>';
14372     },
14373
14374     // private
14375     onRender : function(ct, position)
14376     {
14377         var _t = this;
14378         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
14379         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
14380         
14381         
14382         this.el.dom.style.border = '0 none';
14383         this.el.dom.setAttribute('tabIndex', -1);
14384         this.el.addClass('x-hidden hide');
14385         
14386         
14387         
14388         if(Roo.isIE){ // fix IE 1px bogus margin
14389             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
14390         }
14391        
14392         
14393         this.frameId = Roo.id();
14394         
14395          
14396         
14397         var iframe = this.owner.wrap.createChild({
14398             tag: 'iframe',
14399             cls: 'form-control', // bootstrap..
14400             id: this.frameId,
14401             name: this.frameId,
14402             frameBorder : 'no',
14403             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
14404         }, this.el
14405         );
14406         
14407         
14408         this.iframe = iframe.dom;
14409
14410          this.assignDocWin();
14411         
14412         this.doc.designMode = 'on';
14413        
14414         this.doc.open();
14415         this.doc.write(this.getDocMarkup());
14416         this.doc.close();
14417
14418         
14419         var task = { // must defer to wait for browser to be ready
14420             run : function(){
14421                 //console.log("run task?" + this.doc.readyState);
14422                 this.assignDocWin();
14423                 if(this.doc.body || this.doc.readyState == 'complete'){
14424                     try {
14425                         this.doc.designMode="on";
14426                     } catch (e) {
14427                         return;
14428                     }
14429                     Roo.TaskMgr.stop(task);
14430                     this.initEditor.defer(10, this);
14431                 }
14432             },
14433             interval : 10,
14434             duration: 10000,
14435             scope: this
14436         };
14437         Roo.TaskMgr.start(task);
14438
14439         
14440          
14441     },
14442
14443     // private
14444     onResize : function(w, h)
14445     {
14446          Roo.log('resize: ' +w + ',' + h );
14447         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14448         if(!this.iframe){
14449             return;
14450         }
14451         if(typeof w == 'number'){
14452             
14453             this.iframe.style.width = w + 'px';
14454         }
14455         if(typeof h == 'number'){
14456             
14457             this.iframe.style.height = h + 'px';
14458             if(this.doc){
14459                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14460             }
14461         }
14462         
14463     },
14464
14465     /**
14466      * Toggles the editor between standard and source edit mode.
14467      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14468      */
14469     toggleSourceEdit : function(sourceEditMode){
14470         
14471         this.sourceEditMode = sourceEditMode === true;
14472         
14473         if(this.sourceEditMode){
14474  
14475             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
14476             
14477         }else{
14478             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14479             //this.iframe.className = '';
14480             this.deferFocus();
14481         }
14482         //this.setSize(this.owner.wrap.getSize());
14483         //this.fireEvent('editmodechange', this, this.sourceEditMode);
14484     },
14485
14486     
14487   
14488
14489     /**
14490      * Protected method that will not generally be called directly. If you need/want
14491      * custom HTML cleanup, this is the method you should override.
14492      * @param {String} html The HTML to be cleaned
14493      * return {String} The cleaned HTML
14494      */
14495     cleanHtml : function(html){
14496         html = String(html);
14497         if(html.length > 5){
14498             if(Roo.isSafari){ // strip safari nonsense
14499                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14500             }
14501         }
14502         if(html == '&nbsp;'){
14503             html = '';
14504         }
14505         return html;
14506     },
14507
14508     /**
14509      * HTML Editor -> Textarea
14510      * Protected method that will not generally be called directly. Syncs the contents
14511      * of the editor iframe with the textarea.
14512      */
14513     syncValue : function(){
14514         if(this.initialized){
14515             var bd = (this.doc.body || this.doc.documentElement);
14516             //this.cleanUpPaste(); -- this is done else where and causes havoc..
14517             var html = bd.innerHTML;
14518             if(Roo.isSafari){
14519                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14520                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14521                 if(m && m[1]){
14522                     html = '<div style="'+m[0]+'">' + html + '</div>';
14523                 }
14524             }
14525             html = this.cleanHtml(html);
14526             // fix up the special chars.. normaly like back quotes in word...
14527             // however we do not want to do this with chinese..
14528             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14529                 var cc = b.charCodeAt();
14530                 if (
14531                     (cc >= 0x4E00 && cc < 0xA000 ) ||
14532                     (cc >= 0x3400 && cc < 0x4E00 ) ||
14533                     (cc >= 0xf900 && cc < 0xfb00 )
14534                 ) {
14535                         return b;
14536                 }
14537                 return "&#"+cc+";" 
14538             });
14539             if(this.owner.fireEvent('beforesync', this, html) !== false){
14540                 this.el.dom.value = html;
14541                 this.owner.fireEvent('sync', this, html);
14542             }
14543         }
14544     },
14545
14546     /**
14547      * Protected method that will not generally be called directly. Pushes the value of the textarea
14548      * into the iframe editor.
14549      */
14550     pushValue : function(){
14551         if(this.initialized){
14552             var v = this.el.dom.value.trim();
14553             
14554 //            if(v.length < 1){
14555 //                v = '&#160;';
14556 //            }
14557             
14558             if(this.owner.fireEvent('beforepush', this, v) !== false){
14559                 var d = (this.doc.body || this.doc.documentElement);
14560                 d.innerHTML = v;
14561                 this.cleanUpPaste();
14562                 this.el.dom.value = d.innerHTML;
14563                 this.owner.fireEvent('push', this, v);
14564             }
14565         }
14566     },
14567
14568     // private
14569     deferFocus : function(){
14570         this.focus.defer(10, this);
14571     },
14572
14573     // doc'ed in Field
14574     focus : function(){
14575         if(this.win && !this.sourceEditMode){
14576             this.win.focus();
14577         }else{
14578             this.el.focus();
14579         }
14580     },
14581     
14582     assignDocWin: function()
14583     {
14584         var iframe = this.iframe;
14585         
14586          if(Roo.isIE){
14587             this.doc = iframe.contentWindow.document;
14588             this.win = iframe.contentWindow;
14589         } else {
14590             if (!Roo.get(this.frameId)) {
14591                 return;
14592             }
14593             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14594             this.win = Roo.get(this.frameId).dom.contentWindow;
14595         }
14596     },
14597     
14598     // private
14599     initEditor : function(){
14600         //console.log("INIT EDITOR");
14601         this.assignDocWin();
14602         
14603         
14604         
14605         this.doc.designMode="on";
14606         this.doc.open();
14607         this.doc.write(this.getDocMarkup());
14608         this.doc.close();
14609         
14610         var dbody = (this.doc.body || this.doc.documentElement);
14611         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14612         // this copies styles from the containing element into thsi one..
14613         // not sure why we need all of this..
14614         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14615         
14616         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
14617         //ss['background-attachment'] = 'fixed'; // w3c
14618         dbody.bgProperties = 'fixed'; // ie
14619         //Roo.DomHelper.applyStyles(dbody, ss);
14620         Roo.EventManager.on(this.doc, {
14621             //'mousedown': this.onEditorEvent,
14622             'mouseup': this.onEditorEvent,
14623             'dblclick': this.onEditorEvent,
14624             'click': this.onEditorEvent,
14625             'keyup': this.onEditorEvent,
14626             buffer:100,
14627             scope: this
14628         });
14629         if(Roo.isGecko){
14630             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14631         }
14632         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14633             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14634         }
14635         this.initialized = true;
14636
14637         this.owner.fireEvent('initialize', this);
14638         this.pushValue();
14639     },
14640
14641     // private
14642     onDestroy : function(){
14643         
14644         
14645         
14646         if(this.rendered){
14647             
14648             //for (var i =0; i < this.toolbars.length;i++) {
14649             //    // fixme - ask toolbars for heights?
14650             //    this.toolbars[i].onDestroy();
14651            // }
14652             
14653             //this.wrap.dom.innerHTML = '';
14654             //this.wrap.remove();
14655         }
14656     },
14657
14658     // private
14659     onFirstFocus : function(){
14660         
14661         this.assignDocWin();
14662         
14663         
14664         this.activated = true;
14665          
14666     
14667         if(Roo.isGecko){ // prevent silly gecko errors
14668             this.win.focus();
14669             var s = this.win.getSelection();
14670             if(!s.focusNode || s.focusNode.nodeType != 3){
14671                 var r = s.getRangeAt(0);
14672                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14673                 r.collapse(true);
14674                 this.deferFocus();
14675             }
14676             try{
14677                 this.execCmd('useCSS', true);
14678                 this.execCmd('styleWithCSS', false);
14679             }catch(e){}
14680         }
14681         this.owner.fireEvent('activate', this);
14682     },
14683
14684     // private
14685     adjustFont: function(btn){
14686         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14687         //if(Roo.isSafari){ // safari
14688         //    adjust *= 2;
14689        // }
14690         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14691         if(Roo.isSafari){ // safari
14692             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14693             v =  (v < 10) ? 10 : v;
14694             v =  (v > 48) ? 48 : v;
14695             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14696             
14697         }
14698         
14699         
14700         v = Math.max(1, v+adjust);
14701         
14702         this.execCmd('FontSize', v  );
14703     },
14704
14705     onEditorEvent : function(e){
14706         this.owner.fireEvent('editorevent', this, e);
14707       //  this.updateToolbar();
14708         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14709     },
14710
14711     insertTag : function(tg)
14712     {
14713         // could be a bit smarter... -> wrap the current selected tRoo..
14714         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14715             
14716             range = this.createRange(this.getSelection());
14717             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14718             wrappingNode.appendChild(range.extractContents());
14719             range.insertNode(wrappingNode);
14720
14721             return;
14722             
14723             
14724             
14725         }
14726         this.execCmd("formatblock",   tg);
14727         
14728     },
14729     
14730     insertText : function(txt)
14731     {
14732         
14733         
14734         var range = this.createRange();
14735         range.deleteContents();
14736                //alert(Sender.getAttribute('label'));
14737                
14738         range.insertNode(this.doc.createTextNode(txt));
14739     } ,
14740     
14741      
14742
14743     /**
14744      * Executes a Midas editor command on the editor document and performs necessary focus and
14745      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14746      * @param {String} cmd The Midas command
14747      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14748      */
14749     relayCmd : function(cmd, value){
14750         this.win.focus();
14751         this.execCmd(cmd, value);
14752         this.owner.fireEvent('editorevent', this);
14753         //this.updateToolbar();
14754         this.owner.deferFocus();
14755     },
14756
14757     /**
14758      * Executes a Midas editor command directly on the editor document.
14759      * For visual commands, you should use {@link #relayCmd} instead.
14760      * <b>This should only be called after the editor is initialized.</b>
14761      * @param {String} cmd The Midas command
14762      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14763      */
14764     execCmd : function(cmd, value){
14765         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14766         this.syncValue();
14767     },
14768  
14769  
14770    
14771     /**
14772      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14773      * to insert tRoo.
14774      * @param {String} text | dom node.. 
14775      */
14776     insertAtCursor : function(text)
14777     {
14778         
14779         
14780         
14781         if(!this.activated){
14782             return;
14783         }
14784         /*
14785         if(Roo.isIE){
14786             this.win.focus();
14787             var r = this.doc.selection.createRange();
14788             if(r){
14789                 r.collapse(true);
14790                 r.pasteHTML(text);
14791                 this.syncValue();
14792                 this.deferFocus();
14793             
14794             }
14795             return;
14796         }
14797         */
14798         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14799             this.win.focus();
14800             
14801             
14802             // from jquery ui (MIT licenced)
14803             var range, node;
14804             var win = this.win;
14805             
14806             if (win.getSelection && win.getSelection().getRangeAt) {
14807                 range = win.getSelection().getRangeAt(0);
14808                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14809                 range.insertNode(node);
14810             } else if (win.document.selection && win.document.selection.createRange) {
14811                 // no firefox support
14812                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14813                 win.document.selection.createRange().pasteHTML(txt);
14814             } else {
14815                 // no firefox support
14816                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14817                 this.execCmd('InsertHTML', txt);
14818             } 
14819             
14820             this.syncValue();
14821             
14822             this.deferFocus();
14823         }
14824     },
14825  // private
14826     mozKeyPress : function(e){
14827         if(e.ctrlKey){
14828             var c = e.getCharCode(), cmd;
14829           
14830             if(c > 0){
14831                 c = String.fromCharCode(c).toLowerCase();
14832                 switch(c){
14833                     case 'b':
14834                         cmd = 'bold';
14835                         break;
14836                     case 'i':
14837                         cmd = 'italic';
14838                         break;
14839                     
14840                     case 'u':
14841                         cmd = 'underline';
14842                         break;
14843                     
14844                     case 'v':
14845                         this.cleanUpPaste.defer(100, this);
14846                         return;
14847                         
14848                 }
14849                 if(cmd){
14850                     this.win.focus();
14851                     this.execCmd(cmd);
14852                     this.deferFocus();
14853                     e.preventDefault();
14854                 }
14855                 
14856             }
14857         }
14858     },
14859
14860     // private
14861     fixKeys : function(){ // load time branching for fastest keydown performance
14862         if(Roo.isIE){
14863             return function(e){
14864                 var k = e.getKey(), r;
14865                 if(k == e.TAB){
14866                     e.stopEvent();
14867                     r = this.doc.selection.createRange();
14868                     if(r){
14869                         r.collapse(true);
14870                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14871                         this.deferFocus();
14872                     }
14873                     return;
14874                 }
14875                 
14876                 if(k == e.ENTER){
14877                     r = this.doc.selection.createRange();
14878                     if(r){
14879                         var target = r.parentElement();
14880                         if(!target || target.tagName.toLowerCase() != 'li'){
14881                             e.stopEvent();
14882                             r.pasteHTML('<br />');
14883                             r.collapse(false);
14884                             r.select();
14885                         }
14886                     }
14887                 }
14888                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14889                     this.cleanUpPaste.defer(100, this);
14890                     return;
14891                 }
14892                 
14893                 
14894             };
14895         }else if(Roo.isOpera){
14896             return function(e){
14897                 var k = e.getKey();
14898                 if(k == e.TAB){
14899                     e.stopEvent();
14900                     this.win.focus();
14901                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
14902                     this.deferFocus();
14903                 }
14904                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14905                     this.cleanUpPaste.defer(100, this);
14906                     return;
14907                 }
14908                 
14909             };
14910         }else if(Roo.isSafari){
14911             return function(e){
14912                 var k = e.getKey();
14913                 
14914                 if(k == e.TAB){
14915                     e.stopEvent();
14916                     this.execCmd('InsertText','\t');
14917                     this.deferFocus();
14918                     return;
14919                 }
14920                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14921                     this.cleanUpPaste.defer(100, this);
14922                     return;
14923                 }
14924                 
14925              };
14926         }
14927     }(),
14928     
14929     getAllAncestors: function()
14930     {
14931         var p = this.getSelectedNode();
14932         var a = [];
14933         if (!p) {
14934             a.push(p); // push blank onto stack..
14935             p = this.getParentElement();
14936         }
14937         
14938         
14939         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
14940             a.push(p);
14941             p = p.parentNode;
14942         }
14943         a.push(this.doc.body);
14944         return a;
14945     },
14946     lastSel : false,
14947     lastSelNode : false,
14948     
14949     
14950     getSelection : function() 
14951     {
14952         this.assignDocWin();
14953         return Roo.isIE ? this.doc.selection : this.win.getSelection();
14954     },
14955     
14956     getSelectedNode: function() 
14957     {
14958         // this may only work on Gecko!!!
14959         
14960         // should we cache this!!!!
14961         
14962         
14963         
14964          
14965         var range = this.createRange(this.getSelection()).cloneRange();
14966         
14967         if (Roo.isIE) {
14968             var parent = range.parentElement();
14969             while (true) {
14970                 var testRange = range.duplicate();
14971                 testRange.moveToElementText(parent);
14972                 if (testRange.inRange(range)) {
14973                     break;
14974                 }
14975                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
14976                     break;
14977                 }
14978                 parent = parent.parentElement;
14979             }
14980             return parent;
14981         }
14982         
14983         // is ancestor a text element.
14984         var ac =  range.commonAncestorContainer;
14985         if (ac.nodeType == 3) {
14986             ac = ac.parentNode;
14987         }
14988         
14989         var ar = ac.childNodes;
14990          
14991         var nodes = [];
14992         var other_nodes = [];
14993         var has_other_nodes = false;
14994         for (var i=0;i<ar.length;i++) {
14995             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
14996                 continue;
14997             }
14998             // fullly contained node.
14999             
15000             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
15001                 nodes.push(ar[i]);
15002                 continue;
15003             }
15004             
15005             // probably selected..
15006             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
15007                 other_nodes.push(ar[i]);
15008                 continue;
15009             }
15010             // outer..
15011             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
15012                 continue;
15013             }
15014             
15015             
15016             has_other_nodes = true;
15017         }
15018         if (!nodes.length && other_nodes.length) {
15019             nodes= other_nodes;
15020         }
15021         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
15022             return false;
15023         }
15024         
15025         return nodes[0];
15026     },
15027     createRange: function(sel)
15028     {
15029         // this has strange effects when using with 
15030         // top toolbar - not sure if it's a great idea.
15031         //this.editor.contentWindow.focus();
15032         if (typeof sel != "undefined") {
15033             try {
15034                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
15035             } catch(e) {
15036                 return this.doc.createRange();
15037             }
15038         } else {
15039             return this.doc.createRange();
15040         }
15041     },
15042     getParentElement: function()
15043     {
15044         
15045         this.assignDocWin();
15046         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
15047         
15048         var range = this.createRange(sel);
15049          
15050         try {
15051             var p = range.commonAncestorContainer;
15052             while (p.nodeType == 3) { // text node
15053                 p = p.parentNode;
15054             }
15055             return p;
15056         } catch (e) {
15057             return null;
15058         }
15059     
15060     },
15061     /***
15062      *
15063      * Range intersection.. the hard stuff...
15064      *  '-1' = before
15065      *  '0' = hits..
15066      *  '1' = after.
15067      *         [ -- selected range --- ]
15068      *   [fail]                        [fail]
15069      *
15070      *    basically..
15071      *      if end is before start or  hits it. fail.
15072      *      if start is after end or hits it fail.
15073      *
15074      *   if either hits (but other is outside. - then it's not 
15075      *   
15076      *    
15077      **/
15078     
15079     
15080     // @see http://www.thismuchiknow.co.uk/?p=64.
15081     rangeIntersectsNode : function(range, node)
15082     {
15083         var nodeRange = node.ownerDocument.createRange();
15084         try {
15085             nodeRange.selectNode(node);
15086         } catch (e) {
15087             nodeRange.selectNodeContents(node);
15088         }
15089     
15090         var rangeStartRange = range.cloneRange();
15091         rangeStartRange.collapse(true);
15092     
15093         var rangeEndRange = range.cloneRange();
15094         rangeEndRange.collapse(false);
15095     
15096         var nodeStartRange = nodeRange.cloneRange();
15097         nodeStartRange.collapse(true);
15098     
15099         var nodeEndRange = nodeRange.cloneRange();
15100         nodeEndRange.collapse(false);
15101     
15102         return rangeStartRange.compareBoundaryPoints(
15103                  Range.START_TO_START, nodeEndRange) == -1 &&
15104                rangeEndRange.compareBoundaryPoints(
15105                  Range.START_TO_START, nodeStartRange) == 1;
15106         
15107          
15108     },
15109     rangeCompareNode : function(range, node)
15110     {
15111         var nodeRange = node.ownerDocument.createRange();
15112         try {
15113             nodeRange.selectNode(node);
15114         } catch (e) {
15115             nodeRange.selectNodeContents(node);
15116         }
15117         
15118         
15119         range.collapse(true);
15120     
15121         nodeRange.collapse(true);
15122      
15123         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15124         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15125          
15126         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15127         
15128         var nodeIsBefore   =  ss == 1;
15129         var nodeIsAfter    = ee == -1;
15130         
15131         if (nodeIsBefore && nodeIsAfter)
15132             return 0; // outer
15133         if (!nodeIsBefore && nodeIsAfter)
15134             return 1; //right trailed.
15135         
15136         if (nodeIsBefore && !nodeIsAfter)
15137             return 2;  // left trailed.
15138         // fully contined.
15139         return 3;
15140     },
15141
15142     // private? - in a new class?
15143     cleanUpPaste :  function()
15144     {
15145         // cleans up the whole document..
15146         Roo.log('cleanuppaste');
15147         
15148         this.cleanUpChildren(this.doc.body);
15149         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15150         if (clean != this.doc.body.innerHTML) {
15151             this.doc.body.innerHTML = clean;
15152         }
15153         
15154     },
15155     
15156     cleanWordChars : function(input) {// change the chars to hex code
15157         var he = Roo.HtmlEditorCore;
15158         
15159         var output = input;
15160         Roo.each(he.swapCodes, function(sw) { 
15161             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15162             
15163             output = output.replace(swapper, sw[1]);
15164         });
15165         
15166         return output;
15167     },
15168     
15169     
15170     cleanUpChildren : function (n)
15171     {
15172         if (!n.childNodes.length) {
15173             return;
15174         }
15175         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15176            this.cleanUpChild(n.childNodes[i]);
15177         }
15178     },
15179     
15180     
15181         
15182     
15183     cleanUpChild : function (node)
15184     {
15185         var ed = this;
15186         //console.log(node);
15187         if (node.nodeName == "#text") {
15188             // clean up silly Windows -- stuff?
15189             return; 
15190         }
15191         if (node.nodeName == "#comment") {
15192             node.parentNode.removeChild(node);
15193             // clean up silly Windows -- stuff?
15194             return; 
15195         }
15196         
15197         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
15198             // remove node.
15199             node.parentNode.removeChild(node);
15200             return;
15201             
15202         }
15203         
15204         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
15205         
15206         // remove <a name=....> as rendering on yahoo mailer is borked with this.
15207         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
15208         
15209         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
15210         //    remove_keep_children = true;
15211         //}
15212         
15213         if (remove_keep_children) {
15214             this.cleanUpChildren(node);
15215             // inserts everything just before this node...
15216             while (node.childNodes.length) {
15217                 var cn = node.childNodes[0];
15218                 node.removeChild(cn);
15219                 node.parentNode.insertBefore(cn, node);
15220             }
15221             node.parentNode.removeChild(node);
15222             return;
15223         }
15224         
15225         if (!node.attributes || !node.attributes.length) {
15226             this.cleanUpChildren(node);
15227             return;
15228         }
15229         
15230         function cleanAttr(n,v)
15231         {
15232             
15233             if (v.match(/^\./) || v.match(/^\//)) {
15234                 return;
15235             }
15236             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
15237                 return;
15238             }
15239             if (v.match(/^#/)) {
15240                 return;
15241             }
15242 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
15243             node.removeAttribute(n);
15244             
15245         }
15246         
15247         function cleanStyle(n,v)
15248         {
15249             if (v.match(/expression/)) { //XSS?? should we even bother..
15250                 node.removeAttribute(n);
15251                 return;
15252             }
15253             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
15254             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
15255             
15256             
15257             var parts = v.split(/;/);
15258             var clean = [];
15259             
15260             Roo.each(parts, function(p) {
15261                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
15262                 if (!p.length) {
15263                     return true;
15264                 }
15265                 var l = p.split(':').shift().replace(/\s+/g,'');
15266                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
15267                 
15268                 if ( cblack.indexOf(l) > -1) {
15269 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15270                     //node.removeAttribute(n);
15271                     return true;
15272                 }
15273                 //Roo.log()
15274                 // only allow 'c whitelisted system attributes'
15275                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
15276 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15277                     //node.removeAttribute(n);
15278                     return true;
15279                 }
15280                 
15281                 
15282                  
15283                 
15284                 clean.push(p);
15285                 return true;
15286             });
15287             if (clean.length) { 
15288                 node.setAttribute(n, clean.join(';'));
15289             } else {
15290                 node.removeAttribute(n);
15291             }
15292             
15293         }
15294         
15295         
15296         for (var i = node.attributes.length-1; i > -1 ; i--) {
15297             var a = node.attributes[i];
15298             //console.log(a);
15299             
15300             if (a.name.toLowerCase().substr(0,2)=='on')  {
15301                 node.removeAttribute(a.name);
15302                 continue;
15303             }
15304             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
15305                 node.removeAttribute(a.name);
15306                 continue;
15307             }
15308             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
15309                 cleanAttr(a.name,a.value); // fixme..
15310                 continue;
15311             }
15312             if (a.name == 'style') {
15313                 cleanStyle(a.name,a.value);
15314                 continue;
15315             }
15316             /// clean up MS crap..
15317             // tecnically this should be a list of valid class'es..
15318             
15319             
15320             if (a.name == 'class') {
15321                 if (a.value.match(/^Mso/)) {
15322                     node.className = '';
15323                 }
15324                 
15325                 if (a.value.match(/body/)) {
15326                     node.className = '';
15327                 }
15328                 continue;
15329             }
15330             
15331             // style cleanup!?
15332             // class cleanup?
15333             
15334         }
15335         
15336         
15337         this.cleanUpChildren(node);
15338         
15339         
15340     },
15341     /**
15342      * Clean up MS wordisms...
15343      */
15344     cleanWord : function(node)
15345     {
15346         var _t = this;
15347         var cleanWordChildren = function()
15348         {
15349             if (!node.childNodes.length) {
15350                 return;
15351             }
15352             for (var i = node.childNodes.length-1; i > -1 ; i--) {
15353                _t.cleanWord(node.childNodes[i]);
15354             }
15355         }
15356         
15357         
15358         if (!node) {
15359             this.cleanWord(this.doc.body);
15360             return;
15361         }
15362         if (node.nodeName == "#text") {
15363             // clean up silly Windows -- stuff?
15364             return; 
15365         }
15366         if (node.nodeName == "#comment") {
15367             node.parentNode.removeChild(node);
15368             // clean up silly Windows -- stuff?
15369             return; 
15370         }
15371         
15372         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
15373             node.parentNode.removeChild(node);
15374             return;
15375         }
15376         
15377         // remove - but keep children..
15378         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
15379             while (node.childNodes.length) {
15380                 var cn = node.childNodes[0];
15381                 node.removeChild(cn);
15382                 node.parentNode.insertBefore(cn, node);
15383             }
15384             node.parentNode.removeChild(node);
15385             cleanWordChildren();
15386             return;
15387         }
15388         // clean styles
15389         if (node.className.length) {
15390             
15391             var cn = node.className.split(/\W+/);
15392             var cna = [];
15393             Roo.each(cn, function(cls) {
15394                 if (cls.match(/Mso[a-zA-Z]+/)) {
15395                     return;
15396                 }
15397                 cna.push(cls);
15398             });
15399             node.className = cna.length ? cna.join(' ') : '';
15400             if (!cna.length) {
15401                 node.removeAttribute("class");
15402             }
15403         }
15404         
15405         if (node.hasAttribute("lang")) {
15406             node.removeAttribute("lang");
15407         }
15408         
15409         if (node.hasAttribute("style")) {
15410             
15411             var styles = node.getAttribute("style").split(";");
15412             var nstyle = [];
15413             Roo.each(styles, function(s) {
15414                 if (!s.match(/:/)) {
15415                     return;
15416                 }
15417                 var kv = s.split(":");
15418                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
15419                     return;
15420                 }
15421                 // what ever is left... we allow.
15422                 nstyle.push(s);
15423             });
15424             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
15425             if (!nstyle.length) {
15426                 node.removeAttribute('style');
15427             }
15428         }
15429         
15430         cleanWordChildren();
15431         
15432         
15433     },
15434     domToHTML : function(currentElement, depth, nopadtext) {
15435         
15436             depth = depth || 0;
15437             nopadtext = nopadtext || false;
15438         
15439             if (!currentElement) {
15440                 return this.domToHTML(this.doc.body);
15441             }
15442             
15443             //Roo.log(currentElement);
15444             var j;
15445             var allText = false;
15446             var nodeName = currentElement.nodeName;
15447             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
15448             
15449             if  (nodeName == '#text') {
15450                 return currentElement.nodeValue;
15451             }
15452             
15453             
15454             var ret = '';
15455             if (nodeName != 'BODY') {
15456                  
15457                 var i = 0;
15458                 // Prints the node tagName, such as <A>, <IMG>, etc
15459                 if (tagName) {
15460                     var attr = [];
15461                     for(i = 0; i < currentElement.attributes.length;i++) {
15462                         // quoting?
15463                         var aname = currentElement.attributes.item(i).name;
15464                         if (!currentElement.attributes.item(i).value.length) {
15465                             continue;
15466                         }
15467                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
15468                     }
15469                     
15470                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
15471                 } 
15472                 else {
15473                     
15474                     // eack
15475                 }
15476             } else {
15477                 tagName = false;
15478             }
15479             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
15480                 return ret;
15481             }
15482             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
15483                 nopadtext = true;
15484             }
15485             
15486             
15487             // Traverse the tree
15488             i = 0;
15489             var currentElementChild = currentElement.childNodes.item(i);
15490             var allText = true;
15491             var innerHTML  = '';
15492             lastnode = '';
15493             while (currentElementChild) {
15494                 // Formatting code (indent the tree so it looks nice on the screen)
15495                 var nopad = nopadtext;
15496                 if (lastnode == 'SPAN') {
15497                     nopad  = true;
15498                 }
15499                 // text
15500                 if  (currentElementChild.nodeName == '#text') {
15501                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
15502                     if (!nopad && toadd.length > 80) {
15503                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
15504                     }
15505                     innerHTML  += toadd;
15506                     
15507                     i++;
15508                     currentElementChild = currentElement.childNodes.item(i);
15509                     lastNode = '';
15510                     continue;
15511                 }
15512                 allText = false;
15513                 
15514                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
15515                     
15516                 // Recursively traverse the tree structure of the child node
15517                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
15518                 lastnode = currentElementChild.nodeName;
15519                 i++;
15520                 currentElementChild=currentElement.childNodes.item(i);
15521             }
15522             
15523             ret += innerHTML;
15524             
15525             if (!allText) {
15526                     // The remaining code is mostly for formatting the tree
15527                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
15528             }
15529             
15530             
15531             if (tagName) {
15532                 ret+= "</"+tagName+">";
15533             }
15534             return ret;
15535             
15536         }
15537     
15538     // hide stuff that is not compatible
15539     /**
15540      * @event blur
15541      * @hide
15542      */
15543     /**
15544      * @event change
15545      * @hide
15546      */
15547     /**
15548      * @event focus
15549      * @hide
15550      */
15551     /**
15552      * @event specialkey
15553      * @hide
15554      */
15555     /**
15556      * @cfg {String} fieldClass @hide
15557      */
15558     /**
15559      * @cfg {String} focusClass @hide
15560      */
15561     /**
15562      * @cfg {String} autoCreate @hide
15563      */
15564     /**
15565      * @cfg {String} inputType @hide
15566      */
15567     /**
15568      * @cfg {String} invalidClass @hide
15569      */
15570     /**
15571      * @cfg {String} invalidText @hide
15572      */
15573     /**
15574      * @cfg {String} msgFx @hide
15575      */
15576     /**
15577      * @cfg {String} validateOnBlur @hide
15578      */
15579 });
15580
15581 Roo.HtmlEditorCore.white = [
15582         'area', 'br', 'img', 'input', 'hr', 'wbr',
15583         
15584        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
15585        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
15586        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
15587        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
15588        'table',   'ul',         'xmp', 
15589        
15590        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
15591       'thead',   'tr', 
15592      
15593       'dir', 'menu', 'ol', 'ul', 'dl',
15594        
15595       'embed',  'object'
15596 ];
15597
15598
15599 Roo.HtmlEditorCore.black = [
15600     //    'embed',  'object', // enable - backend responsiblity to clean thiese
15601         'applet', // 
15602         'base',   'basefont', 'bgsound', 'blink',  'body', 
15603         'frame',  'frameset', 'head',    'html',   'ilayer', 
15604         'iframe', 'layer',  'link',     'meta',    'object',   
15605         'script', 'style' ,'title',  'xml' // clean later..
15606 ];
15607 Roo.HtmlEditorCore.clean = [
15608     'script', 'style', 'title', 'xml'
15609 ];
15610 Roo.HtmlEditorCore.remove = [
15611     'font'
15612 ];
15613 // attributes..
15614
15615 Roo.HtmlEditorCore.ablack = [
15616     'on'
15617 ];
15618     
15619 Roo.HtmlEditorCore.aclean = [ 
15620     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
15621 ];
15622
15623 // protocols..
15624 Roo.HtmlEditorCore.pwhite= [
15625         'http',  'https',  'mailto'
15626 ];
15627
15628 // white listed style attributes.
15629 Roo.HtmlEditorCore.cwhite= [
15630       //  'text-align', /// default is to allow most things..
15631       
15632          
15633 //        'font-size'//??
15634 ];
15635
15636 // black listed style attributes.
15637 Roo.HtmlEditorCore.cblack= [
15638       //  'font-size' -- this can be set by the project 
15639 ];
15640
15641
15642 Roo.HtmlEditorCore.swapCodes   =[ 
15643     [    8211, "--" ], 
15644     [    8212, "--" ], 
15645     [    8216,  "'" ],  
15646     [    8217, "'" ],  
15647     [    8220, '"' ],  
15648     [    8221, '"' ],  
15649     [    8226, "*" ],  
15650     [    8230, "..." ]
15651 ]; 
15652
15653     /*
15654  * - LGPL
15655  *
15656  * HtmlEditor
15657  * 
15658  */
15659
15660 /**
15661  * @class Roo.bootstrap.HtmlEditor
15662  * @extends Roo.bootstrap.TextArea
15663  * Bootstrap HtmlEditor class
15664
15665  * @constructor
15666  * Create a new HtmlEditor
15667  * @param {Object} config The config object
15668  */
15669
15670 Roo.bootstrap.HtmlEditor = function(config){
15671     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15672     if (!this.toolbars) {
15673         this.toolbars = [];
15674     }
15675     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15676     this.addEvents({
15677             /**
15678              * @event initialize
15679              * Fires when the editor is fully initialized (including the iframe)
15680              * @param {HtmlEditor} this
15681              */
15682             initialize: true,
15683             /**
15684              * @event activate
15685              * Fires when the editor is first receives the focus. Any insertion must wait
15686              * until after this event.
15687              * @param {HtmlEditor} this
15688              */
15689             activate: true,
15690              /**
15691              * @event beforesync
15692              * Fires before the textarea is updated with content from the editor iframe. Return false
15693              * to cancel the sync.
15694              * @param {HtmlEditor} this
15695              * @param {String} html
15696              */
15697             beforesync: true,
15698              /**
15699              * @event beforepush
15700              * Fires before the iframe editor is updated with content from the textarea. Return false
15701              * to cancel the push.
15702              * @param {HtmlEditor} this
15703              * @param {String} html
15704              */
15705             beforepush: true,
15706              /**
15707              * @event sync
15708              * Fires when the textarea is updated with content from the editor iframe.
15709              * @param {HtmlEditor} this
15710              * @param {String} html
15711              */
15712             sync: true,
15713              /**
15714              * @event push
15715              * Fires when the iframe editor is updated with content from the textarea.
15716              * @param {HtmlEditor} this
15717              * @param {String} html
15718              */
15719             push: true,
15720              /**
15721              * @event editmodechange
15722              * Fires when the editor switches edit modes
15723              * @param {HtmlEditor} this
15724              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15725              */
15726             editmodechange: true,
15727             /**
15728              * @event editorevent
15729              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15730              * @param {HtmlEditor} this
15731              */
15732             editorevent: true,
15733             /**
15734              * @event firstfocus
15735              * Fires when on first focus - needed by toolbars..
15736              * @param {HtmlEditor} this
15737              */
15738             firstfocus: true,
15739             /**
15740              * @event autosave
15741              * Auto save the htmlEditor value as a file into Events
15742              * @param {HtmlEditor} this
15743              */
15744             autosave: true,
15745             /**
15746              * @event savedpreview
15747              * preview the saved version of htmlEditor
15748              * @param {HtmlEditor} this
15749              */
15750             savedpreview: true
15751         });
15752 };
15753
15754
15755 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15756     
15757     
15758       /**
15759      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15760      */
15761     toolbars : false,
15762    
15763      /**
15764      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15765      *                        Roo.resizable.
15766      */
15767     resizable : false,
15768      /**
15769      * @cfg {Number} height (in pixels)
15770      */   
15771     height: 300,
15772    /**
15773      * @cfg {Number} width (in pixels)
15774      */   
15775     width: false,
15776     
15777     /**
15778      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15779      * 
15780      */
15781     stylesheets: false,
15782     
15783     // id of frame..
15784     frameId: false,
15785     
15786     // private properties
15787     validationEvent : false,
15788     deferHeight: true,
15789     initialized : false,
15790     activated : false,
15791     
15792     onFocus : Roo.emptyFn,
15793     iframePad:3,
15794     hideMode:'offsets',
15795     
15796     
15797     tbContainer : false,
15798     
15799     toolbarContainer :function() {
15800         return this.wrap.select('.x-html-editor-tb',true).first();
15801     },
15802
15803     /**
15804      * Protected method that will not generally be called directly. It
15805      * is called when the editor creates its toolbar. Override this method if you need to
15806      * add custom toolbar buttons.
15807      * @param {HtmlEditor} editor
15808      */
15809     createToolbar : function(){
15810         
15811         Roo.log("create toolbars");
15812         
15813         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
15814         this.toolbars[0].render(this.toolbarContainer());
15815         
15816         return;
15817         
15818 //        if (!editor.toolbars || !editor.toolbars.length) {
15819 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15820 //        }
15821 //        
15822 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15823 //            editor.toolbars[i] = Roo.factory(
15824 //                    typeof(editor.toolbars[i]) == 'string' ?
15825 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15826 //                Roo.bootstrap.HtmlEditor);
15827 //            editor.toolbars[i].init(editor);
15828 //        }
15829     },
15830
15831      
15832     // private
15833     onRender : function(ct, position)
15834     {
15835        // Roo.log("Call onRender: " + this.xtype);
15836         var _t = this;
15837         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15838       
15839         this.wrap = this.inputEl().wrap({
15840             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15841         });
15842         
15843         this.editorcore.onRender(ct, position);
15844          
15845         if (this.resizable) {
15846             this.resizeEl = new Roo.Resizable(this.wrap, {
15847                 pinned : true,
15848                 wrap: true,
15849                 dynamic : true,
15850                 minHeight : this.height,
15851                 height: this.height,
15852                 handles : this.resizable,
15853                 width: this.width,
15854                 listeners : {
15855                     resize : function(r, w, h) {
15856                         _t.onResize(w,h); // -something
15857                     }
15858                 }
15859             });
15860             
15861         }
15862         this.createToolbar(this);
15863        
15864         
15865         if(!this.width && this.resizable){
15866             this.setSize(this.wrap.getSize());
15867         }
15868         if (this.resizeEl) {
15869             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15870             // should trigger onReize..
15871         }
15872         
15873     },
15874
15875     // private
15876     onResize : function(w, h)
15877     {
15878         Roo.log('resize: ' +w + ',' + h );
15879         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15880         var ew = false;
15881         var eh = false;
15882         
15883         if(this.inputEl() ){
15884             if(typeof w == 'number'){
15885                 var aw = w - this.wrap.getFrameWidth('lr');
15886                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15887                 ew = aw;
15888             }
15889             if(typeof h == 'number'){
15890                  var tbh = -11;  // fixme it needs to tool bar size!
15891                 for (var i =0; i < this.toolbars.length;i++) {
15892                     // fixme - ask toolbars for heights?
15893                     tbh += this.toolbars[i].el.getHeight();
15894                     //if (this.toolbars[i].footer) {
15895                     //    tbh += this.toolbars[i].footer.el.getHeight();
15896                     //}
15897                 }
15898               
15899                 
15900                 
15901                 
15902                 
15903                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
15904                 ah -= 5; // knock a few pixes off for look..
15905                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
15906                 var eh = ah;
15907             }
15908         }
15909         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
15910         this.editorcore.onResize(ew,eh);
15911         
15912     },
15913
15914     /**
15915      * Toggles the editor between standard and source edit mode.
15916      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15917      */
15918     toggleSourceEdit : function(sourceEditMode)
15919     {
15920         this.editorcore.toggleSourceEdit(sourceEditMode);
15921         
15922         if(this.editorcore.sourceEditMode){
15923             Roo.log('editor - showing textarea');
15924             
15925 //            Roo.log('in');
15926 //            Roo.log(this.syncValue());
15927             this.syncValue();
15928             this.inputEl().removeClass('hide');
15929             this.inputEl().dom.removeAttribute('tabIndex');
15930             this.inputEl().focus();
15931         }else{
15932             Roo.log('editor - hiding textarea');
15933 //            Roo.log('out')
15934 //            Roo.log(this.pushValue()); 
15935             this.pushValue();
15936             
15937             this.inputEl().addClass('hide');
15938             this.inputEl().dom.setAttribute('tabIndex', -1);
15939             //this.deferFocus();
15940         }
15941          
15942         if(this.resizable){
15943             this.setSize(this.wrap.getSize());
15944         }
15945         
15946         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
15947     },
15948  
15949     // private (for BoxComponent)
15950     adjustSize : Roo.BoxComponent.prototype.adjustSize,
15951
15952     // private (for BoxComponent)
15953     getResizeEl : function(){
15954         return this.wrap;
15955     },
15956
15957     // private (for BoxComponent)
15958     getPositionEl : function(){
15959         return this.wrap;
15960     },
15961
15962     // private
15963     initEvents : function(){
15964         this.originalValue = this.getValue();
15965     },
15966
15967 //    /**
15968 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15969 //     * @method
15970 //     */
15971 //    markInvalid : Roo.emptyFn,
15972 //    /**
15973 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15974 //     * @method
15975 //     */
15976 //    clearInvalid : Roo.emptyFn,
15977
15978     setValue : function(v){
15979         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
15980         this.editorcore.pushValue();
15981     },
15982
15983      
15984     // private
15985     deferFocus : function(){
15986         this.focus.defer(10, this);
15987     },
15988
15989     // doc'ed in Field
15990     focus : function(){
15991         this.editorcore.focus();
15992         
15993     },
15994       
15995
15996     // private
15997     onDestroy : function(){
15998         
15999         
16000         
16001         if(this.rendered){
16002             
16003             for (var i =0; i < this.toolbars.length;i++) {
16004                 // fixme - ask toolbars for heights?
16005                 this.toolbars[i].onDestroy();
16006             }
16007             
16008             this.wrap.dom.innerHTML = '';
16009             this.wrap.remove();
16010         }
16011     },
16012
16013     // private
16014     onFirstFocus : function(){
16015         //Roo.log("onFirstFocus");
16016         this.editorcore.onFirstFocus();
16017          for (var i =0; i < this.toolbars.length;i++) {
16018             this.toolbars[i].onFirstFocus();
16019         }
16020         
16021     },
16022     
16023     // private
16024     syncValue : function()
16025     {   
16026         this.editorcore.syncValue();
16027     },
16028     
16029     pushValue : function()
16030     {   
16031         this.editorcore.pushValue();
16032     }
16033      
16034     
16035     // hide stuff that is not compatible
16036     /**
16037      * @event blur
16038      * @hide
16039      */
16040     /**
16041      * @event change
16042      * @hide
16043      */
16044     /**
16045      * @event focus
16046      * @hide
16047      */
16048     /**
16049      * @event specialkey
16050      * @hide
16051      */
16052     /**
16053      * @cfg {String} fieldClass @hide
16054      */
16055     /**
16056      * @cfg {String} focusClass @hide
16057      */
16058     /**
16059      * @cfg {String} autoCreate @hide
16060      */
16061     /**
16062      * @cfg {String} inputType @hide
16063      */
16064     /**
16065      * @cfg {String} invalidClass @hide
16066      */
16067     /**
16068      * @cfg {String} invalidText @hide
16069      */
16070     /**
16071      * @cfg {String} msgFx @hide
16072      */
16073     /**
16074      * @cfg {String} validateOnBlur @hide
16075      */
16076 });
16077  
16078     
16079    
16080    
16081    
16082       
16083 Roo.namespace('Roo.bootstrap.htmleditor');
16084 /**
16085  * @class Roo.bootstrap.HtmlEditorToolbar1
16086  * Basic Toolbar
16087  * 
16088  * Usage:
16089  *
16090  new Roo.bootstrap.HtmlEditor({
16091     ....
16092     toolbars : [
16093         new Roo.bootstrap.HtmlEditorToolbar1({
16094             disable : { fonts: 1 , format: 1, ..., ... , ...],
16095             btns : [ .... ]
16096         })
16097     }
16098      
16099  * 
16100  * @cfg {Object} disable List of elements to disable..
16101  * @cfg {Array} btns List of additional buttons.
16102  * 
16103  * 
16104  * NEEDS Extra CSS? 
16105  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
16106  */
16107  
16108 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
16109 {
16110     
16111     Roo.apply(this, config);
16112     
16113     // default disabled, based on 'good practice'..
16114     this.disable = this.disable || {};
16115     Roo.applyIf(this.disable, {
16116         fontSize : true,
16117         colors : true,
16118         specialElements : true
16119     });
16120     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
16121     
16122     this.editor = config.editor;
16123     this.editorcore = config.editor.editorcore;
16124     
16125     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
16126     
16127     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
16128     // dont call parent... till later.
16129 }
16130 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
16131      
16132     bar : true,
16133     
16134     editor : false,
16135     editorcore : false,
16136     
16137     
16138     formats : [
16139         "p" ,  
16140         "h1","h2","h3","h4","h5","h6", 
16141         "pre", "code", 
16142         "abbr", "acronym", "address", "cite", "samp", "var",
16143         'div','span'
16144     ],
16145     
16146     onRender : function(ct, position)
16147     {
16148        // Roo.log("Call onRender: " + this.xtype);
16149         
16150        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
16151        Roo.log(this.el);
16152        this.el.dom.style.marginBottom = '0';
16153        var _this = this;
16154        var editorcore = this.editorcore;
16155        var editor= this.editor;
16156        
16157        var children = [];
16158        var btn = function(id,cmd , toggle, handler){
16159        
16160             var  event = toggle ? 'toggle' : 'click';
16161        
16162             var a = {
16163                 size : 'sm',
16164                 xtype: 'Button',
16165                 xns: Roo.bootstrap,
16166                 glyphicon : id,
16167                 cmd : id || cmd,
16168                 enableToggle:toggle !== false,
16169                 //html : 'submit'
16170                 pressed : toggle ? false : null,
16171                 listeners : {}
16172             }
16173             a.listeners[toggle ? 'toggle' : 'click'] = function() {
16174                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
16175             }
16176             children.push(a);
16177             return a;
16178        }
16179         
16180         var style = {
16181                 xtype: 'Button',
16182                 size : 'sm',
16183                 xns: Roo.bootstrap,
16184                 glyphicon : 'font',
16185                 //html : 'submit'
16186                 menu : {
16187                     xtype: 'Menu',
16188                     xns: Roo.bootstrap,
16189                     items:  []
16190                 }
16191         };
16192         Roo.each(this.formats, function(f) {
16193             style.menu.items.push({
16194                 xtype :'MenuItem',
16195                 xns: Roo.bootstrap,
16196                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
16197                 tagname : f,
16198                 listeners : {
16199                     click : function()
16200                     {
16201                         editorcore.insertTag(this.tagname);
16202                         editor.focus();
16203                     }
16204                 }
16205                 
16206             });
16207         });
16208          children.push(style);   
16209             
16210             
16211         btn('bold',false,true);
16212         btn('italic',false,true);
16213         btn('align-left', 'justifyleft',true);
16214         btn('align-center', 'justifycenter',true);
16215         btn('align-right' , 'justifyright',true);
16216         btn('link', false, false, function(btn) {
16217             //Roo.log("create link?");
16218             var url = prompt(this.createLinkText, this.defaultLinkValue);
16219             if(url && url != 'http:/'+'/'){
16220                 this.editorcore.relayCmd('createlink', url);
16221             }
16222         }),
16223         btn('list','insertunorderedlist',true);
16224         btn('pencil', false,true, function(btn){
16225                 Roo.log(this);
16226                 
16227                 this.toggleSourceEdit(btn.pressed);
16228         });
16229         /*
16230         var cog = {
16231                 xtype: 'Button',
16232                 size : 'sm',
16233                 xns: Roo.bootstrap,
16234                 glyphicon : 'cog',
16235                 //html : 'submit'
16236                 menu : {
16237                     xtype: 'Menu',
16238                     xns: Roo.bootstrap,
16239                     items:  []
16240                 }
16241         };
16242         
16243         cog.menu.items.push({
16244             xtype :'MenuItem',
16245             xns: Roo.bootstrap,
16246             html : Clean styles,
16247             tagname : f,
16248             listeners : {
16249                 click : function()
16250                 {
16251                     editorcore.insertTag(this.tagname);
16252                     editor.focus();
16253                 }
16254             }
16255             
16256         });
16257        */
16258         
16259          
16260        this.xtype = 'NavSimplebar';
16261         
16262         for(var i=0;i< children.length;i++) {
16263             
16264             this.buttons.add(this.addxtypeChild(children[i]));
16265             
16266         }
16267         
16268         editor.on('editorevent', this.updateToolbar, this);
16269     },
16270     onBtnClick : function(id)
16271     {
16272        this.editorcore.relayCmd(id);
16273        this.editorcore.focus();
16274     },
16275     
16276     /**
16277      * Protected method that will not generally be called directly. It triggers
16278      * a toolbar update by reading the markup state of the current selection in the editor.
16279      */
16280     updateToolbar: function(){
16281
16282         if(!this.editorcore.activated){
16283             this.editor.onFirstFocus(); // is this neeed?
16284             return;
16285         }
16286
16287         var btns = this.buttons; 
16288         var doc = this.editorcore.doc;
16289         btns.get('bold').setActive(doc.queryCommandState('bold'));
16290         btns.get('italic').setActive(doc.queryCommandState('italic'));
16291         //btns.get('underline').setActive(doc.queryCommandState('underline'));
16292         
16293         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
16294         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
16295         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
16296         
16297         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
16298         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
16299          /*
16300         
16301         var ans = this.editorcore.getAllAncestors();
16302         if (this.formatCombo) {
16303             
16304             
16305             var store = this.formatCombo.store;
16306             this.formatCombo.setValue("");
16307             for (var i =0; i < ans.length;i++) {
16308                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
16309                     // select it..
16310                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
16311                     break;
16312                 }
16313             }
16314         }
16315         
16316         
16317         
16318         // hides menus... - so this cant be on a menu...
16319         Roo.bootstrap.MenuMgr.hideAll();
16320         */
16321         Roo.bootstrap.MenuMgr.hideAll();
16322         //this.editorsyncValue();
16323     },
16324     onFirstFocus: function() {
16325         this.buttons.each(function(item){
16326            item.enable();
16327         });
16328     },
16329     toggleSourceEdit : function(sourceEditMode){
16330         
16331           
16332         if(sourceEditMode){
16333             Roo.log("disabling buttons");
16334            this.buttons.each( function(item){
16335                 if(item.cmd != 'pencil'){
16336                     item.disable();
16337                 }
16338             });
16339           
16340         }else{
16341             Roo.log("enabling buttons");
16342             if(this.editorcore.initialized){
16343                 this.buttons.each( function(item){
16344                     item.enable();
16345                 });
16346             }
16347             
16348         }
16349         Roo.log("calling toggole on editor");
16350         // tell the editor that it's been pressed..
16351         this.editor.toggleSourceEdit(sourceEditMode);
16352        
16353     }
16354 });
16355
16356
16357
16358
16359 /*
16360  * Based on:
16361  * Ext JS Library 1.1.1
16362  * Copyright(c) 2006-2007, Ext JS, LLC.
16363  *
16364  * Originally Released Under LGPL - original licence link has changed is not relivant.
16365  *
16366  * Fork - LGPL
16367  * <script type="text/javascript">
16368  */
16369  
16370
16371 /**
16372  * @class Roo.grid.ColumnModel
16373  * @extends Roo.util.Observable
16374  * This is the default implementation of a ColumnModel used by the Grid. It defines
16375  * the columns in the grid.
16376  * <br>Usage:<br>
16377  <pre><code>
16378  var colModel = new Roo.grid.ColumnModel([
16379         {header: "Ticker", width: 60, sortable: true, locked: true},
16380         {header: "Company Name", width: 150, sortable: true},
16381         {header: "Market Cap.", width: 100, sortable: true},
16382         {header: "$ Sales", width: 100, sortable: true, renderer: money},
16383         {header: "Employees", width: 100, sortable: true, resizable: false}
16384  ]);
16385  </code></pre>
16386  * <p>
16387  
16388  * The config options listed for this class are options which may appear in each
16389  * individual column definition.
16390  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
16391  * @constructor
16392  * @param {Object} config An Array of column config objects. See this class's
16393  * config objects for details.
16394 */
16395 Roo.grid.ColumnModel = function(config){
16396         /**
16397      * The config passed into the constructor
16398      */
16399     this.config = config;
16400     this.lookup = {};
16401
16402     // if no id, create one
16403     // if the column does not have a dataIndex mapping,
16404     // map it to the order it is in the config
16405     for(var i = 0, len = config.length; i < len; i++){
16406         var c = config[i];
16407         if(typeof c.dataIndex == "undefined"){
16408             c.dataIndex = i;
16409         }
16410         if(typeof c.renderer == "string"){
16411             c.renderer = Roo.util.Format[c.renderer];
16412         }
16413         if(typeof c.id == "undefined"){
16414             c.id = Roo.id();
16415         }
16416         if(c.editor && c.editor.xtype){
16417             c.editor  = Roo.factory(c.editor, Roo.grid);
16418         }
16419         if(c.editor && c.editor.isFormField){
16420             c.editor = new Roo.grid.GridEditor(c.editor);
16421         }
16422         this.lookup[c.id] = c;
16423     }
16424
16425     /**
16426      * The width of columns which have no width specified (defaults to 100)
16427      * @type Number
16428      */
16429     this.defaultWidth = 100;
16430
16431     /**
16432      * Default sortable of columns which have no sortable specified (defaults to false)
16433      * @type Boolean
16434      */
16435     this.defaultSortable = false;
16436
16437     this.addEvents({
16438         /**
16439              * @event widthchange
16440              * Fires when the width of a column changes.
16441              * @param {ColumnModel} this
16442              * @param {Number} columnIndex The column index
16443              * @param {Number} newWidth The new width
16444              */
16445             "widthchange": true,
16446         /**
16447              * @event headerchange
16448              * Fires when the text of a header changes.
16449              * @param {ColumnModel} this
16450              * @param {Number} columnIndex The column index
16451              * @param {Number} newText The new header text
16452              */
16453             "headerchange": true,
16454         /**
16455              * @event hiddenchange
16456              * Fires when a column is hidden or "unhidden".
16457              * @param {ColumnModel} this
16458              * @param {Number} columnIndex The column index
16459              * @param {Boolean} hidden true if hidden, false otherwise
16460              */
16461             "hiddenchange": true,
16462             /**
16463          * @event columnmoved
16464          * Fires when a column is moved.
16465          * @param {ColumnModel} this
16466          * @param {Number} oldIndex
16467          * @param {Number} newIndex
16468          */
16469         "columnmoved" : true,
16470         /**
16471          * @event columlockchange
16472          * Fires when a column's locked state is changed
16473          * @param {ColumnModel} this
16474          * @param {Number} colIndex
16475          * @param {Boolean} locked true if locked
16476          */
16477         "columnlockchange" : true
16478     });
16479     Roo.grid.ColumnModel.superclass.constructor.call(this);
16480 };
16481 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
16482     /**
16483      * @cfg {String} header The header text to display in the Grid view.
16484      */
16485     /**
16486      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
16487      * {@link Roo.data.Record} definition from which to draw the column's value. If not
16488      * specified, the column's index is used as an index into the Record's data Array.
16489      */
16490     /**
16491      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
16492      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
16493      */
16494     /**
16495      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
16496      * Defaults to the value of the {@link #defaultSortable} property.
16497      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
16498      */
16499     /**
16500      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
16501      */
16502     /**
16503      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
16504      */
16505     /**
16506      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
16507      */
16508     /**
16509      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
16510      */
16511     /**
16512      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
16513      * given the cell's data value. See {@link #setRenderer}. If not specified, the
16514      * default renderer uses the raw data value.
16515      */
16516        /**
16517      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
16518      */
16519     /**
16520      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
16521      */
16522
16523     /**
16524      * Returns the id of the column at the specified index.
16525      * @param {Number} index The column index
16526      * @return {String} the id
16527      */
16528     getColumnId : function(index){
16529         return this.config[index].id;
16530     },
16531
16532     /**
16533      * Returns the column for a specified id.
16534      * @param {String} id The column id
16535      * @return {Object} the column
16536      */
16537     getColumnById : function(id){
16538         return this.lookup[id];
16539     },
16540
16541     
16542     /**
16543      * Returns the column for a specified dataIndex.
16544      * @param {String} dataIndex The column dataIndex
16545      * @return {Object|Boolean} the column or false if not found
16546      */
16547     getColumnByDataIndex: function(dataIndex){
16548         var index = this.findColumnIndex(dataIndex);
16549         return index > -1 ? this.config[index] : false;
16550     },
16551     
16552     /**
16553      * Returns the index for a specified column id.
16554      * @param {String} id The column id
16555      * @return {Number} the index, or -1 if not found
16556      */
16557     getIndexById : function(id){
16558         for(var i = 0, len = this.config.length; i < len; i++){
16559             if(this.config[i].id == id){
16560                 return i;
16561             }
16562         }
16563         return -1;
16564     },
16565     
16566     /**
16567      * Returns the index for a specified column dataIndex.
16568      * @param {String} dataIndex The column dataIndex
16569      * @return {Number} the index, or -1 if not found
16570      */
16571     
16572     findColumnIndex : function(dataIndex){
16573         for(var i = 0, len = this.config.length; i < len; i++){
16574             if(this.config[i].dataIndex == dataIndex){
16575                 return i;
16576             }
16577         }
16578         return -1;
16579     },
16580     
16581     
16582     moveColumn : function(oldIndex, newIndex){
16583         var c = this.config[oldIndex];
16584         this.config.splice(oldIndex, 1);
16585         this.config.splice(newIndex, 0, c);
16586         this.dataMap = null;
16587         this.fireEvent("columnmoved", this, oldIndex, newIndex);
16588     },
16589
16590     isLocked : function(colIndex){
16591         return this.config[colIndex].locked === true;
16592     },
16593
16594     setLocked : function(colIndex, value, suppressEvent){
16595         if(this.isLocked(colIndex) == value){
16596             return;
16597         }
16598         this.config[colIndex].locked = value;
16599         if(!suppressEvent){
16600             this.fireEvent("columnlockchange", this, colIndex, value);
16601         }
16602     },
16603
16604     getTotalLockedWidth : function(){
16605         var totalWidth = 0;
16606         for(var i = 0; i < this.config.length; i++){
16607             if(this.isLocked(i) && !this.isHidden(i)){
16608                 this.totalWidth += this.getColumnWidth(i);
16609             }
16610         }
16611         return totalWidth;
16612     },
16613
16614     getLockedCount : function(){
16615         for(var i = 0, len = this.config.length; i < len; i++){
16616             if(!this.isLocked(i)){
16617                 return i;
16618             }
16619         }
16620     },
16621
16622     /**
16623      * Returns the number of columns.
16624      * @return {Number}
16625      */
16626     getColumnCount : function(visibleOnly){
16627         if(visibleOnly === true){
16628             var c = 0;
16629             for(var i = 0, len = this.config.length; i < len; i++){
16630                 if(!this.isHidden(i)){
16631                     c++;
16632                 }
16633             }
16634             return c;
16635         }
16636         return this.config.length;
16637     },
16638
16639     /**
16640      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16641      * @param {Function} fn
16642      * @param {Object} scope (optional)
16643      * @return {Array} result
16644      */
16645     getColumnsBy : function(fn, scope){
16646         var r = [];
16647         for(var i = 0, len = this.config.length; i < len; i++){
16648             var c = this.config[i];
16649             if(fn.call(scope||this, c, i) === true){
16650                 r[r.length] = c;
16651             }
16652         }
16653         return r;
16654     },
16655
16656     /**
16657      * Returns true if the specified column is sortable.
16658      * @param {Number} col The column index
16659      * @return {Boolean}
16660      */
16661     isSortable : function(col){
16662         if(typeof this.config[col].sortable == "undefined"){
16663             return this.defaultSortable;
16664         }
16665         return this.config[col].sortable;
16666     },
16667
16668     /**
16669      * Returns the rendering (formatting) function defined for the column.
16670      * @param {Number} col The column index.
16671      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16672      */
16673     getRenderer : function(col){
16674         if(!this.config[col].renderer){
16675             return Roo.grid.ColumnModel.defaultRenderer;
16676         }
16677         return this.config[col].renderer;
16678     },
16679
16680     /**
16681      * Sets the rendering (formatting) function for a column.
16682      * @param {Number} col The column index
16683      * @param {Function} fn The function to use to process the cell's raw data
16684      * to return HTML markup for the grid view. The render function is called with
16685      * the following parameters:<ul>
16686      * <li>Data value.</li>
16687      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16688      * <li>css A CSS style string to apply to the table cell.</li>
16689      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16690      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16691      * <li>Row index</li>
16692      * <li>Column index</li>
16693      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16694      */
16695     setRenderer : function(col, fn){
16696         this.config[col].renderer = fn;
16697     },
16698
16699     /**
16700      * Returns the width for the specified column.
16701      * @param {Number} col The column index
16702      * @return {Number}
16703      */
16704     getColumnWidth : function(col){
16705         return this.config[col].width * 1 || this.defaultWidth;
16706     },
16707
16708     /**
16709      * Sets the width for a column.
16710      * @param {Number} col The column index
16711      * @param {Number} width The new width
16712      */
16713     setColumnWidth : function(col, width, suppressEvent){
16714         this.config[col].width = width;
16715         this.totalWidth = null;
16716         if(!suppressEvent){
16717              this.fireEvent("widthchange", this, col, width);
16718         }
16719     },
16720
16721     /**
16722      * Returns the total width of all columns.
16723      * @param {Boolean} includeHidden True to include hidden column widths
16724      * @return {Number}
16725      */
16726     getTotalWidth : function(includeHidden){
16727         if(!this.totalWidth){
16728             this.totalWidth = 0;
16729             for(var i = 0, len = this.config.length; i < len; i++){
16730                 if(includeHidden || !this.isHidden(i)){
16731                     this.totalWidth += this.getColumnWidth(i);
16732                 }
16733             }
16734         }
16735         return this.totalWidth;
16736     },
16737
16738     /**
16739      * Returns the header for the specified column.
16740      * @param {Number} col The column index
16741      * @return {String}
16742      */
16743     getColumnHeader : function(col){
16744         return this.config[col].header;
16745     },
16746
16747     /**
16748      * Sets the header for a column.
16749      * @param {Number} col The column index
16750      * @param {String} header The new header
16751      */
16752     setColumnHeader : function(col, header){
16753         this.config[col].header = header;
16754         this.fireEvent("headerchange", this, col, header);
16755     },
16756
16757     /**
16758      * Returns the tooltip for the specified column.
16759      * @param {Number} col The column index
16760      * @return {String}
16761      */
16762     getColumnTooltip : function(col){
16763             return this.config[col].tooltip;
16764     },
16765     /**
16766      * Sets the tooltip for a column.
16767      * @param {Number} col The column index
16768      * @param {String} tooltip The new tooltip
16769      */
16770     setColumnTooltip : function(col, tooltip){
16771             this.config[col].tooltip = tooltip;
16772     },
16773
16774     /**
16775      * Returns the dataIndex for the specified column.
16776      * @param {Number} col The column index
16777      * @return {Number}
16778      */
16779     getDataIndex : function(col){
16780         return this.config[col].dataIndex;
16781     },
16782
16783     /**
16784      * Sets the dataIndex for a column.
16785      * @param {Number} col The column index
16786      * @param {Number} dataIndex The new dataIndex
16787      */
16788     setDataIndex : function(col, dataIndex){
16789         this.config[col].dataIndex = dataIndex;
16790     },
16791
16792     
16793     
16794     /**
16795      * Returns true if the cell is editable.
16796      * @param {Number} colIndex The column index
16797      * @param {Number} rowIndex The row index
16798      * @return {Boolean}
16799      */
16800     isCellEditable : function(colIndex, rowIndex){
16801         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16802     },
16803
16804     /**
16805      * Returns the editor defined for the cell/column.
16806      * return false or null to disable editing.
16807      * @param {Number} colIndex The column index
16808      * @param {Number} rowIndex The row index
16809      * @return {Object}
16810      */
16811     getCellEditor : function(colIndex, rowIndex){
16812         return this.config[colIndex].editor;
16813     },
16814
16815     /**
16816      * Sets if a column is editable.
16817      * @param {Number} col The column index
16818      * @param {Boolean} editable True if the column is editable
16819      */
16820     setEditable : function(col, editable){
16821         this.config[col].editable = editable;
16822     },
16823
16824
16825     /**
16826      * Returns true if the column is hidden.
16827      * @param {Number} colIndex The column index
16828      * @return {Boolean}
16829      */
16830     isHidden : function(colIndex){
16831         return this.config[colIndex].hidden;
16832     },
16833
16834
16835     /**
16836      * Returns true if the column width cannot be changed
16837      */
16838     isFixed : function(colIndex){
16839         return this.config[colIndex].fixed;
16840     },
16841
16842     /**
16843      * Returns true if the column can be resized
16844      * @return {Boolean}
16845      */
16846     isResizable : function(colIndex){
16847         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16848     },
16849     /**
16850      * Sets if a column is hidden.
16851      * @param {Number} colIndex The column index
16852      * @param {Boolean} hidden True if the column is hidden
16853      */
16854     setHidden : function(colIndex, hidden){
16855         this.config[colIndex].hidden = hidden;
16856         this.totalWidth = null;
16857         this.fireEvent("hiddenchange", this, colIndex, hidden);
16858     },
16859
16860     /**
16861      * Sets the editor for a column.
16862      * @param {Number} col The column index
16863      * @param {Object} editor The editor object
16864      */
16865     setEditor : function(col, editor){
16866         this.config[col].editor = editor;
16867     }
16868 });
16869
16870 Roo.grid.ColumnModel.defaultRenderer = function(value){
16871         if(typeof value == "string" && value.length < 1){
16872             return "&#160;";
16873         }
16874         return value;
16875 };
16876
16877 // Alias for backwards compatibility
16878 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
16879
16880 /**
16881  * @class Roo.bootstrap.Table.AbstractSelectionModel
16882  * @extends Roo.util.Observable
16883  * Abstract base class for grid SelectionModels.  It provides the interface that should be
16884  * implemented by descendant classes.  This class should not be directly instantiated.
16885  * @constructor
16886  */
16887 Roo.bootstrap.Table.AbstractSelectionModel = function(){
16888     this.locked = false;
16889     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
16890 };
16891
16892
16893 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
16894     /** @ignore Called by the grid automatically. Do not call directly. */
16895     init : function(grid){
16896         this.grid = grid;
16897         this.initEvents();
16898     },
16899
16900     /**
16901      * Locks the selections.
16902      */
16903     lock : function(){
16904         this.locked = true;
16905     },
16906
16907     /**
16908      * Unlocks the selections.
16909      */
16910     unlock : function(){
16911         this.locked = false;
16912     },
16913
16914     /**
16915      * Returns true if the selections are locked.
16916      * @return {Boolean}
16917      */
16918     isLocked : function(){
16919         return this.locked;
16920     }
16921 });
16922 /**
16923  * @extends Roo.bootstrap.Table.AbstractSelectionModel
16924  * @class Roo.bootstrap.Table.RowSelectionModel
16925  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
16926  * It supports multiple selections and keyboard selection/navigation. 
16927  * @constructor
16928  * @param {Object} config
16929  */
16930
16931 Roo.bootstrap.Table.RowSelectionModel = function(config){
16932     Roo.apply(this, config);
16933     this.selections = new Roo.util.MixedCollection(false, function(o){
16934         return o.id;
16935     });
16936
16937     this.last = false;
16938     this.lastActive = false;
16939
16940     this.addEvents({
16941         /**
16942              * @event selectionchange
16943              * Fires when the selection changes
16944              * @param {SelectionModel} this
16945              */
16946             "selectionchange" : true,
16947         /**
16948              * @event afterselectionchange
16949              * Fires after the selection changes (eg. by key press or clicking)
16950              * @param {SelectionModel} this
16951              */
16952             "afterselectionchange" : true,
16953         /**
16954              * @event beforerowselect
16955              * Fires when a row is selected being selected, return false to cancel.
16956              * @param {SelectionModel} this
16957              * @param {Number} rowIndex The selected index
16958              * @param {Boolean} keepExisting False if other selections will be cleared
16959              */
16960             "beforerowselect" : true,
16961         /**
16962              * @event rowselect
16963              * Fires when a row is selected.
16964              * @param {SelectionModel} this
16965              * @param {Number} rowIndex The selected index
16966              * @param {Roo.data.Record} r The record
16967              */
16968             "rowselect" : true,
16969         /**
16970              * @event rowdeselect
16971              * Fires when a row is deselected.
16972              * @param {SelectionModel} this
16973              * @param {Number} rowIndex The selected index
16974              */
16975         "rowdeselect" : true
16976     });
16977     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
16978     this.locked = false;
16979 };
16980
16981 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
16982     /**
16983      * @cfg {Boolean} singleSelect
16984      * True to allow selection of only one row at a time (defaults to false)
16985      */
16986     singleSelect : false,
16987
16988     // private
16989     initEvents : function(){
16990
16991         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
16992             this.grid.on("mousedown", this.handleMouseDown, this);
16993         }else{ // allow click to work like normal
16994             this.grid.on("rowclick", this.handleDragableRowClick, this);
16995         }
16996
16997         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
16998             "up" : function(e){
16999                 if(!e.shiftKey){
17000                     this.selectPrevious(e.shiftKey);
17001                 }else if(this.last !== false && this.lastActive !== false){
17002                     var last = this.last;
17003                     this.selectRange(this.last,  this.lastActive-1);
17004                     this.grid.getView().focusRow(this.lastActive);
17005                     if(last !== false){
17006                         this.last = last;
17007                     }
17008                 }else{
17009                     this.selectFirstRow();
17010                 }
17011                 this.fireEvent("afterselectionchange", this);
17012             },
17013             "down" : function(e){
17014                 if(!e.shiftKey){
17015                     this.selectNext(e.shiftKey);
17016                 }else if(this.last !== false && this.lastActive !== false){
17017                     var last = this.last;
17018                     this.selectRange(this.last,  this.lastActive+1);
17019                     this.grid.getView().focusRow(this.lastActive);
17020                     if(last !== false){
17021                         this.last = last;
17022                     }
17023                 }else{
17024                     this.selectFirstRow();
17025                 }
17026                 this.fireEvent("afterselectionchange", this);
17027             },
17028             scope: this
17029         });
17030
17031         var view = this.grid.view;
17032         view.on("refresh", this.onRefresh, this);
17033         view.on("rowupdated", this.onRowUpdated, this);
17034         view.on("rowremoved", this.onRemove, this);
17035     },
17036
17037     // private
17038     onRefresh : function(){
17039         var ds = this.grid.dataSource, i, v = this.grid.view;
17040         var s = this.selections;
17041         s.each(function(r){
17042             if((i = ds.indexOfId(r.id)) != -1){
17043                 v.onRowSelect(i);
17044             }else{
17045                 s.remove(r);
17046             }
17047         });
17048     },
17049
17050     // private
17051     onRemove : function(v, index, r){
17052         this.selections.remove(r);
17053     },
17054
17055     // private
17056     onRowUpdated : function(v, index, r){
17057         if(this.isSelected(r)){
17058             v.onRowSelect(index);
17059         }
17060     },
17061
17062     /**
17063      * Select records.
17064      * @param {Array} records The records to select
17065      * @param {Boolean} keepExisting (optional) True to keep existing selections
17066      */
17067     selectRecords : function(records, keepExisting){
17068         if(!keepExisting){
17069             this.clearSelections();
17070         }
17071         var ds = this.grid.dataSource;
17072         for(var i = 0, len = records.length; i < len; i++){
17073             this.selectRow(ds.indexOf(records[i]), true);
17074         }
17075     },
17076
17077     /**
17078      * Gets the number of selected rows.
17079      * @return {Number}
17080      */
17081     getCount : function(){
17082         return this.selections.length;
17083     },
17084
17085     /**
17086      * Selects the first row in the grid.
17087      */
17088     selectFirstRow : function(){
17089         this.selectRow(0);
17090     },
17091
17092     /**
17093      * Select the last row.
17094      * @param {Boolean} keepExisting (optional) True to keep existing selections
17095      */
17096     selectLastRow : function(keepExisting){
17097         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
17098     },
17099
17100     /**
17101      * Selects the row immediately following the last selected row.
17102      * @param {Boolean} keepExisting (optional) True to keep existing selections
17103      */
17104     selectNext : function(keepExisting){
17105         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
17106             this.selectRow(this.last+1, keepExisting);
17107             this.grid.getView().focusRow(this.last);
17108         }
17109     },
17110
17111     /**
17112      * Selects the row that precedes the last selected row.
17113      * @param {Boolean} keepExisting (optional) True to keep existing selections
17114      */
17115     selectPrevious : function(keepExisting){
17116         if(this.last){
17117             this.selectRow(this.last-1, keepExisting);
17118             this.grid.getView().focusRow(this.last);
17119         }
17120     },
17121
17122     /**
17123      * Returns the selected records
17124      * @return {Array} Array of selected records
17125      */
17126     getSelections : function(){
17127         return [].concat(this.selections.items);
17128     },
17129
17130     /**
17131      * Returns the first selected record.
17132      * @return {Record}
17133      */
17134     getSelected : function(){
17135         return this.selections.itemAt(0);
17136     },
17137
17138
17139     /**
17140      * Clears all selections.
17141      */
17142     clearSelections : function(fast){
17143         if(this.locked) return;
17144         if(fast !== true){
17145             var ds = this.grid.dataSource;
17146             var s = this.selections;
17147             s.each(function(r){
17148                 this.deselectRow(ds.indexOfId(r.id));
17149             }, this);
17150             s.clear();
17151         }else{
17152             this.selections.clear();
17153         }
17154         this.last = false;
17155     },
17156
17157
17158     /**
17159      * Selects all rows.
17160      */
17161     selectAll : function(){
17162         if(this.locked) return;
17163         this.selections.clear();
17164         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
17165             this.selectRow(i, true);
17166         }
17167     },
17168
17169     /**
17170      * Returns True if there is a selection.
17171      * @return {Boolean}
17172      */
17173     hasSelection : function(){
17174         return this.selections.length > 0;
17175     },
17176
17177     /**
17178      * Returns True if the specified row is selected.
17179      * @param {Number/Record} record The record or index of the record to check
17180      * @return {Boolean}
17181      */
17182     isSelected : function(index){
17183         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
17184         return (r && this.selections.key(r.id) ? true : false);
17185     },
17186
17187     /**
17188      * Returns True if the specified record id is selected.
17189      * @param {String} id The id of record to check
17190      * @return {Boolean}
17191      */
17192     isIdSelected : function(id){
17193         return (this.selections.key(id) ? true : false);
17194     },
17195
17196     // private
17197     handleMouseDown : function(e, t){
17198         var view = this.grid.getView(), rowIndex;
17199         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
17200             return;
17201         };
17202         if(e.shiftKey && this.last !== false){
17203             var last = this.last;
17204             this.selectRange(last, rowIndex, e.ctrlKey);
17205             this.last = last; // reset the last
17206             view.focusRow(rowIndex);
17207         }else{
17208             var isSelected = this.isSelected(rowIndex);
17209             if(e.button !== 0 && isSelected){
17210                 view.focusRow(rowIndex);
17211             }else if(e.ctrlKey && isSelected){
17212                 this.deselectRow(rowIndex);
17213             }else if(!isSelected){
17214                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
17215                 view.focusRow(rowIndex);
17216             }
17217         }
17218         this.fireEvent("afterselectionchange", this);
17219     },
17220     // private
17221     handleDragableRowClick :  function(grid, rowIndex, e) 
17222     {
17223         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
17224             this.selectRow(rowIndex, false);
17225             grid.view.focusRow(rowIndex);
17226              this.fireEvent("afterselectionchange", this);
17227         }
17228     },
17229     
17230     /**
17231      * Selects multiple rows.
17232      * @param {Array} rows Array of the indexes of the row to select
17233      * @param {Boolean} keepExisting (optional) True to keep existing selections
17234      */
17235     selectRows : function(rows, keepExisting){
17236         if(!keepExisting){
17237             this.clearSelections();
17238         }
17239         for(var i = 0, len = rows.length; i < len; i++){
17240             this.selectRow(rows[i], true);
17241         }
17242     },
17243
17244     /**
17245      * Selects a range of rows. All rows in between startRow and endRow are also selected.
17246      * @param {Number} startRow The index of the first row in the range
17247      * @param {Number} endRow The index of the last row in the range
17248      * @param {Boolean} keepExisting (optional) True to retain existing selections
17249      */
17250     selectRange : function(startRow, endRow, keepExisting){
17251         if(this.locked) return;
17252         if(!keepExisting){
17253             this.clearSelections();
17254         }
17255         if(startRow <= endRow){
17256             for(var i = startRow; i <= endRow; i++){
17257                 this.selectRow(i, true);
17258             }
17259         }else{
17260             for(var i = startRow; i >= endRow; i--){
17261                 this.selectRow(i, true);
17262             }
17263         }
17264     },
17265
17266     /**
17267      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
17268      * @param {Number} startRow The index of the first row in the range
17269      * @param {Number} endRow The index of the last row in the range
17270      */
17271     deselectRange : function(startRow, endRow, preventViewNotify){
17272         if(this.locked) return;
17273         for(var i = startRow; i <= endRow; i++){
17274             this.deselectRow(i, preventViewNotify);
17275         }
17276     },
17277
17278     /**
17279      * Selects a row.
17280      * @param {Number} row The index of the row to select
17281      * @param {Boolean} keepExisting (optional) True to keep existing selections
17282      */
17283     selectRow : function(index, keepExisting, preventViewNotify){
17284         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17285         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17286             if(!keepExisting || this.singleSelect){
17287                 this.clearSelections();
17288             }
17289             var r = this.grid.dataSource.getAt(index);
17290             this.selections.add(r);
17291             this.last = this.lastActive = index;
17292             if(!preventViewNotify){
17293                 this.grid.getView().onRowSelect(index);
17294             }
17295             this.fireEvent("rowselect", this, index, r);
17296             this.fireEvent("selectionchange", this);
17297         }
17298     },
17299
17300     /**
17301      * Deselects a row.
17302      * @param {Number} row The index of the row to deselect
17303      */
17304     deselectRow : function(index, preventViewNotify){
17305         if(this.locked) return;
17306         if(this.last == index){
17307             this.last = false;
17308         }
17309         if(this.lastActive == index){
17310             this.lastActive = false;
17311         }
17312         var r = this.grid.dataSource.getAt(index);
17313         this.selections.remove(r);
17314         if(!preventViewNotify){
17315             this.grid.getView().onRowDeselect(index);
17316         }
17317         this.fireEvent("rowdeselect", this, index);
17318         this.fireEvent("selectionchange", this);
17319     },
17320
17321     // private
17322     restoreLast : function(){
17323         if(this._last){
17324             this.last = this._last;
17325         }
17326     },
17327
17328     // private
17329     acceptsNav : function(row, col, cm){
17330         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17331     },
17332
17333     // private
17334     onEditorKey : function(field, e){
17335         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17336         if(k == e.TAB){
17337             e.stopEvent();
17338             ed.completeEdit();
17339             if(e.shiftKey){
17340                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17341             }else{
17342                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17343             }
17344         }else if(k == e.ENTER && !e.ctrlKey){
17345             e.stopEvent();
17346             ed.completeEdit();
17347             if(e.shiftKey){
17348                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17349             }else{
17350                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17351             }
17352         }else if(k == e.ESC){
17353             ed.cancelEdit();
17354         }
17355         if(newCell){
17356             g.startEditing(newCell[0], newCell[1]);
17357         }
17358     }
17359 });/*
17360  * Based on:
17361  * Ext JS Library 1.1.1
17362  * Copyright(c) 2006-2007, Ext JS, LLC.
17363  *
17364  * Originally Released Under LGPL - original licence link has changed is not relivant.
17365  *
17366  * Fork - LGPL
17367  * <script type="text/javascript">
17368  */
17369  
17370 /**
17371  * @class Roo.bootstrap.PagingToolbar
17372  * @extends Roo.Row
17373  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
17374  * @constructor
17375  * Create a new PagingToolbar
17376  * @param {Object} config The config object
17377  */
17378 Roo.bootstrap.PagingToolbar = function(config)
17379 {
17380     // old args format still supported... - xtype is prefered..
17381         // created from xtype...
17382     var ds = config.dataSource;
17383     var items = [];
17384     if (config.items) {
17385         items = config.items;
17386         config.items = [];
17387     }
17388     
17389     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
17390     this.ds = ds;
17391     this.cursor = 0;
17392     if (ds) { 
17393         this.bind(ds);
17394     }
17395     
17396     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
17397     
17398     // supprot items array.
17399     
17400     Roo.each(items, function(e) {
17401         this.add(Roo.factory(e));
17402     },this);
17403     
17404     
17405     
17406     
17407     
17408 };
17409
17410 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
17411     /**
17412      * @cfg {Roo.data.Store} dataSource
17413      * The underlying data store providing the paged data
17414      */
17415     /**
17416      * @cfg {String/HTMLElement/Element} container
17417      * container The id or element that will contain the toolbar
17418      */
17419     /**
17420      * @cfg {Boolean} displayInfo
17421      * True to display the displayMsg (defaults to false)
17422      */
17423     /**
17424      * @cfg {Number} pageSize
17425      * The number of records to display per page (defaults to 20)
17426      */
17427     pageSize: 20,
17428     /**
17429      * @cfg {String} displayMsg
17430      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
17431      */
17432     displayMsg : 'Displaying {0} - {1} of {2}',
17433     /**
17434      * @cfg {String} emptyMsg
17435      * The message to display when no records are found (defaults to "No data to display")
17436      */
17437     emptyMsg : 'No data to display',
17438     /**
17439      * Customizable piece of the default paging text (defaults to "Page")
17440      * @type String
17441      */
17442     beforePageText : "Page",
17443     /**
17444      * Customizable piece of the default paging text (defaults to "of %0")
17445      * @type String
17446      */
17447     afterPageText : "of {0}",
17448     /**
17449      * Customizable piece of the default paging text (defaults to "First Page")
17450      * @type String
17451      */
17452     firstText : "First Page",
17453     /**
17454      * Customizable piece of the default paging text (defaults to "Previous Page")
17455      * @type String
17456      */
17457     prevText : "Previous Page",
17458     /**
17459      * Customizable piece of the default paging text (defaults to "Next Page")
17460      * @type String
17461      */
17462     nextText : "Next Page",
17463     /**
17464      * Customizable piece of the default paging text (defaults to "Last Page")
17465      * @type String
17466      */
17467     lastText : "Last Page",
17468     /**
17469      * Customizable piece of the default paging text (defaults to "Refresh")
17470      * @type String
17471      */
17472     refreshText : "Refresh",
17473
17474     // private
17475     onRender : function(ct, position) 
17476     {
17477         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
17478         this.navgroup.parentId = this.id;
17479         this.navgroup.onRender(this.el, null);
17480         // add the buttons to the navgroup
17481         
17482         this.first = this.navgroup.addItem({
17483             tooltip: this.firstText,
17484             cls: "prev",
17485             icon : 'fa fa-backward',
17486             disabled: true,
17487             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
17488         });
17489         
17490         this.prev =  this.navgroup.addItem({
17491             tooltip: this.prevText,
17492             cls: "prev",
17493             icon : 'fa fa-step-backward',
17494             disabled: true,
17495             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
17496         });
17497     //this.addSeparator();
17498         
17499         
17500         var field = this.navgroup.addItem( {
17501             tagtype : 'span',
17502             cls : 'x-paging-position',
17503             
17504             html : this.beforePageText  +
17505                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
17506                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
17507          } ); //?? escaped?
17508         
17509         this.field = field.el.select('input', true).first();
17510         this.field.on("keydown", this.onPagingKeydown, this);
17511         this.field.on("focus", function(){this.dom.select();});
17512     
17513     
17514         this.afterTextEl =  field.el.select('.x-paging-after').first();
17515         //this.field.setHeight(18);
17516         //this.addSeparator();
17517         this.next = this.navgroup.addItem({
17518             tooltip: this.nextText,
17519             cls: "next",
17520             html : ' <i class="fa fa-step-forward">',
17521             disabled: true,
17522             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
17523         });
17524         this.last = this.navgroup.addItem({
17525             tooltip: this.lastText,
17526             icon : 'fa fa-forward',
17527             cls: "next",
17528             disabled: true,
17529             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
17530         });
17531     //this.addSeparator();
17532         this.loading = this.navgroup.addItem({
17533             tooltip: this.refreshText,
17534             icon: 'fa fa-refresh',
17535             
17536             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
17537         });
17538
17539         if(this.displayInfo){
17540             var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info' } );
17541             this.displayEl = navel.el.select('a',true).first();
17542         }
17543     
17544     },
17545
17546     // private
17547     updateInfo : function(){
17548         if(this.displayEl){
17549             var count = this.ds.getCount();
17550             var msg = count == 0 ?
17551                 this.emptyMsg :
17552                 String.format(
17553                     this.displayMsg,
17554                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
17555                 );
17556             this.displayEl.update(msg);
17557         }
17558     },
17559
17560     // private
17561     onLoad : function(ds, r, o){
17562        this.cursor = o.params ? o.params.start : 0;
17563        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
17564
17565        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
17566        this.field.dom.value = ap;
17567        this.first.setDisabled(ap == 1);
17568        this.prev.setDisabled(ap == 1);
17569        this.next.setDisabled(ap == ps);
17570        this.last.setDisabled(ap == ps);
17571        this.loading.enable();
17572        this.updateInfo();
17573     },
17574
17575     // private
17576     getPageData : function(){
17577         var total = this.ds.getTotalCount();
17578         return {
17579             total : total,
17580             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
17581             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
17582         };
17583     },
17584
17585     // private
17586     onLoadError : function(){
17587         this.loading.enable();
17588     },
17589
17590     // private
17591     onPagingKeydown : function(e){
17592         var k = e.getKey();
17593         var d = this.getPageData();
17594         if(k == e.RETURN){
17595             var v = this.field.dom.value, pageNum;
17596             if(!v || isNaN(pageNum = parseInt(v, 10))){
17597                 this.field.dom.value = d.activePage;
17598                 return;
17599             }
17600             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
17601             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
17602             e.stopEvent();
17603         }
17604         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))
17605         {
17606           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
17607           this.field.dom.value = pageNum;
17608           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
17609           e.stopEvent();
17610         }
17611         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
17612         {
17613           var v = this.field.dom.value, pageNum; 
17614           var increment = (e.shiftKey) ? 10 : 1;
17615           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
17616             increment *= -1;
17617           if(!v || isNaN(pageNum = parseInt(v, 10))) {
17618             this.field.dom.value = d.activePage;
17619             return;
17620           }
17621           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
17622           {
17623             this.field.dom.value = parseInt(v, 10) + increment;
17624             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
17625             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
17626           }
17627           e.stopEvent();
17628         }
17629     },
17630
17631     // private
17632     beforeLoad : function(){
17633         if(this.loading){
17634             this.loading.disable();
17635         }
17636     },
17637
17638     // private
17639     onClick : function(which){
17640         var ds = this.ds;
17641         if (!ds) {
17642             return;
17643         }
17644         switch(which){
17645             case "first":
17646                 ds.load({params:{start: 0, limit: this.pageSize}});
17647             break;
17648             case "prev":
17649                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
17650             break;
17651             case "next":
17652                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
17653             break;
17654             case "last":
17655                 var total = ds.getTotalCount();
17656                 var extra = total % this.pageSize;
17657                 var lastStart = extra ? (total - extra) : total-this.pageSize;
17658                 ds.load({params:{start: lastStart, limit: this.pageSize}});
17659             break;
17660             case "refresh":
17661                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
17662             break;
17663         }
17664     },
17665
17666     /**
17667      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
17668      * @param {Roo.data.Store} store The data store to unbind
17669      */
17670     unbind : function(ds){
17671         ds.un("beforeload", this.beforeLoad, this);
17672         ds.un("load", this.onLoad, this);
17673         ds.un("loadexception", this.onLoadError, this);
17674         ds.un("remove", this.updateInfo, this);
17675         ds.un("add", this.updateInfo, this);
17676         this.ds = undefined;
17677     },
17678
17679     /**
17680      * Binds the paging toolbar to the specified {@link Roo.data.Store}
17681      * @param {Roo.data.Store} store The data store to bind
17682      */
17683     bind : function(ds){
17684         ds.on("beforeload", this.beforeLoad, this);
17685         ds.on("load", this.onLoad, this);
17686         ds.on("loadexception", this.onLoadError, this);
17687         ds.on("remove", this.updateInfo, this);
17688         ds.on("add", this.updateInfo, this);
17689         this.ds = ds;
17690     }
17691 });/*
17692  * - LGPL
17693  *
17694  * element
17695  * 
17696  */
17697
17698 /**
17699  * @class Roo.bootstrap.MessageBar
17700  * @extends Roo.bootstrap.Component
17701  * Bootstrap MessageBar class
17702  * @cfg {String} html contents of the MessageBar
17703  * @cfg {String} weight (info | success | warning | danger) default info
17704  * @cfg {String} beforeClass insert the bar before the given class
17705  * @cfg {Boolean} closable (true | false) default false
17706  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
17707  * 
17708  * @constructor
17709  * Create a new Element
17710  * @param {Object} config The config object
17711  */
17712
17713 Roo.bootstrap.MessageBar = function(config){
17714     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
17715 };
17716
17717 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
17718     
17719     html: '',
17720     weight: 'info',
17721     closable: false,
17722     fixed: false,
17723     beforeClass: 'bootstrap-sticky-wrap',
17724     
17725     getAutoCreate : function(){
17726         
17727         var cfg = {
17728             tag: 'div',
17729             cls: 'alert alert-dismissable alert-' + this.weight,
17730             cn: [
17731                 {
17732                     tag: 'span',
17733                     cls: 'message',
17734                     html: this.html || ''
17735                 }
17736             ]
17737         }
17738         
17739         if(this.fixed){
17740             cfg.cls += ' alert-messages-fixed';
17741         }
17742         
17743         if(this.closable){
17744             cfg.cn.push({
17745                 tag: 'button',
17746                 cls: 'close',
17747                 html: 'x'
17748             });
17749         }
17750         
17751         return cfg;
17752     },
17753     
17754     onRender : function(ct, position)
17755     {
17756         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17757         
17758         if(!this.el){
17759             var cfg = Roo.apply({},  this.getAutoCreate());
17760             cfg.id = Roo.id();
17761             
17762             if (this.cls) {
17763                 cfg.cls += ' ' + this.cls;
17764             }
17765             if (this.style) {
17766                 cfg.style = this.style;
17767             }
17768             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
17769             
17770             this.el.setVisibilityMode(Roo.Element.DISPLAY);
17771         }
17772         
17773         this.el.select('>button.close').on('click', this.hide, this);
17774         
17775     },
17776     
17777     show : function()
17778     {
17779         if (!this.rendered) {
17780             this.render();
17781         }
17782         
17783         this.el.show();
17784         
17785         this.fireEvent('show', this);
17786         
17787     },
17788     
17789     hide : function()
17790     {
17791         if (!this.rendered) {
17792             this.render();
17793         }
17794         
17795         this.el.hide();
17796         
17797         this.fireEvent('hide', this);
17798     },
17799     
17800     update : function()
17801     {
17802 //        var e = this.el.dom.firstChild;
17803 //        
17804 //        if(this.closable){
17805 //            e = e.nextSibling;
17806 //        }
17807 //        
17808 //        e.data = this.html || '';
17809
17810         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
17811     }
17812    
17813 });
17814
17815  
17816
17817